tunacode-cli 0.0.9__py3-none-any.whl → 0.0.10__py3-none-any.whl
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.
Potentially problematic release.
This version of tunacode-cli might be problematic. Click here for more details.
- tunacode/cli/commands.py +34 -165
- tunacode/cli/main.py +15 -38
- tunacode/cli/repl.py +24 -18
- tunacode/configuration/defaults.py +1 -1
- tunacode/configuration/models.py +4 -11
- tunacode/configuration/settings.py +10 -3
- tunacode/constants.py +6 -4
- tunacode/context.py +3 -1
- tunacode/core/agents/main.py +94 -52
- tunacode/core/setup/agent_setup.py +1 -1
- tunacode/core/setup/config_setup.py +148 -78
- tunacode/core/setup/coordinator.py +4 -2
- tunacode/core/setup/environment_setup.py +1 -1
- tunacode/core/setup/git_safety_setup.py +51 -39
- tunacode/exceptions.py +2 -0
- tunacode/prompts/system.txt +1 -1
- tunacode/services/undo_service.py +16 -13
- tunacode/setup.py +6 -2
- tunacode/tools/base.py +20 -11
- tunacode/tools/update_file.py +14 -24
- tunacode/tools/write_file.py +7 -9
- tunacode/ui/completers.py +33 -98
- tunacode/ui/input.py +9 -13
- tunacode/ui/keybindings.py +3 -1
- tunacode/ui/lexers.py +17 -16
- tunacode/ui/output.py +8 -14
- tunacode/ui/panels.py +7 -5
- tunacode/ui/prompt_manager.py +4 -8
- tunacode/ui/tool_ui.py +3 -3
- tunacode/utils/system.py +0 -40
- tunacode_cli-0.0.10.dist-info/METADATA +366 -0
- tunacode_cli-0.0.10.dist-info/RECORD +65 -0
- {tunacode_cli-0.0.9.dist-info → tunacode_cli-0.0.10.dist-info}/licenses/LICENSE +1 -1
- tunacode/cli/model_selector.py +0 -178
- tunacode/core/agents/tinyagent_main.py +0 -194
- tunacode/core/setup/optimized_coordinator.py +0 -73
- tunacode/services/enhanced_undo_service.py +0 -322
- tunacode/services/project_undo_service.py +0 -311
- tunacode/tools/tinyagent_tools.py +0 -103
- tunacode/utils/lazy_imports.py +0 -59
- tunacode/utils/regex_cache.py +0 -33
- tunacode_cli-0.0.9.dist-info/METADATA +0 -321
- tunacode_cli-0.0.9.dist-info/RECORD +0 -73
- {tunacode_cli-0.0.9.dist-info → tunacode_cli-0.0.10.dist-info}/WHEEL +0 -0
- {tunacode_cli-0.0.9.dist-info → tunacode_cli-0.0.10.dist-info}/entry_points.txt +0 -0
- {tunacode_cli-0.0.9.dist-info → tunacode_cli-0.0.10.dist-info}/top_level.txt +0 -0
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Project-local undo service that stores backups in the working directory.
|
|
3
|
-
Designed for pip-installed CLI tool usage.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import json
|
|
7
|
-
import shutil
|
|
8
|
-
import time
|
|
9
|
-
from datetime import datetime
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
from typing import Dict, Optional, Tuple
|
|
12
|
-
|
|
13
|
-
from tunacode.core.state import StateManager
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ProjectUndoService:
|
|
17
|
-
"""Undo system that stores backups in .tunacode/ within the project."""
|
|
18
|
-
|
|
19
|
-
# Directory name for local undo data
|
|
20
|
-
UNDO_DIR_NAME = ".tunacode"
|
|
21
|
-
BACKUP_SUBDIR = "backups"
|
|
22
|
-
GITIGNORE_CONTENT = """# TunaCode undo system files
|
|
23
|
-
*
|
|
24
|
-
!.gitignore
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
def __init__(self, state_manager: StateManager, project_dir: Optional[Path] = None):
|
|
28
|
-
self.state_manager = state_manager
|
|
29
|
-
self.project_dir = Path(project_dir or Path.cwd())
|
|
30
|
-
|
|
31
|
-
# Local undo directory in the project
|
|
32
|
-
self.undo_dir = self.project_dir / self.UNDO_DIR_NAME
|
|
33
|
-
self.backup_dir = self.undo_dir / self.BACKUP_SUBDIR
|
|
34
|
-
self.op_log_file = self.undo_dir / "operations.jsonl"
|
|
35
|
-
|
|
36
|
-
# Git directory (if project uses git)
|
|
37
|
-
self.git_dir = self.project_dir / ".git"
|
|
38
|
-
|
|
39
|
-
self._init_undo_directory()
|
|
40
|
-
|
|
41
|
-
def _init_undo_directory(self):
|
|
42
|
-
"""Initialize the .tunacode directory in the project."""
|
|
43
|
-
try:
|
|
44
|
-
# Create directories
|
|
45
|
-
self.undo_dir.mkdir(exist_ok=True)
|
|
46
|
-
self.backup_dir.mkdir(exist_ok=True)
|
|
47
|
-
|
|
48
|
-
# Create .gitignore to exclude undo files from version control
|
|
49
|
-
gitignore_path = self.undo_dir / ".gitignore"
|
|
50
|
-
if not gitignore_path.exists():
|
|
51
|
-
gitignore_path.write_text(self.GITIGNORE_CONTENT)
|
|
52
|
-
|
|
53
|
-
# Create operation log
|
|
54
|
-
if not self.op_log_file.exists():
|
|
55
|
-
self.op_log_file.touch()
|
|
56
|
-
|
|
57
|
-
# Add informational README
|
|
58
|
-
readme_path = self.undo_dir / "README.md"
|
|
59
|
-
if not readme_path.exists():
|
|
60
|
-
readme_path.write_text(
|
|
61
|
-
"""# TunaCode Undo System
|
|
62
|
-
|
|
63
|
-
This directory contains local backup files created by TunaCode to enable undo functionality.
|
|
64
|
-
|
|
65
|
-
## Contents:
|
|
66
|
-
- `backups/` - Timestamped file backups
|
|
67
|
-
- `operations.jsonl` - Operation history log
|
|
68
|
-
- `.gitignore` - Excludes these files from git
|
|
69
|
-
|
|
70
|
-
## Notes:
|
|
71
|
-
- These files are local to your machine
|
|
72
|
-
- Safe to delete if you don't need undo history
|
|
73
|
-
- Automatically cleaned up (keeps last 50 backups)
|
|
74
|
-
- Not committed to version control
|
|
75
|
-
|
|
76
|
-
Created by TunaCode (https://github.com/larock22/tunacode)
|
|
77
|
-
"""
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
except PermissionError:
|
|
81
|
-
print(f"⚠️ Cannot create {self.UNDO_DIR_NAME}/ directory - undo will be limited")
|
|
82
|
-
except Exception as e:
|
|
83
|
-
print(f"⚠️ Error initializing undo directory: {e}")
|
|
84
|
-
|
|
85
|
-
def should_track_file(self, filepath: Path) -> bool:
|
|
86
|
-
"""Check if a file should be tracked by undo system."""
|
|
87
|
-
# Don't track files in our own undo directory
|
|
88
|
-
try:
|
|
89
|
-
if self.undo_dir in filepath.parents:
|
|
90
|
-
return False
|
|
91
|
-
except ValueError:
|
|
92
|
-
pass
|
|
93
|
-
|
|
94
|
-
# Don't track hidden files/directories (except .gitignore, .env, etc)
|
|
95
|
-
parts = filepath.parts
|
|
96
|
-
for part in parts:
|
|
97
|
-
if part.startswith(".") and part not in {".gitignore", ".env", ".envrc"}:
|
|
98
|
-
return False
|
|
99
|
-
|
|
100
|
-
# Don't track common build/cache directories
|
|
101
|
-
exclude_dirs = {
|
|
102
|
-
"node_modules",
|
|
103
|
-
"__pycache__",
|
|
104
|
-
".pytest_cache",
|
|
105
|
-
"dist",
|
|
106
|
-
"build",
|
|
107
|
-
".next",
|
|
108
|
-
".nuxt",
|
|
109
|
-
"target",
|
|
110
|
-
}
|
|
111
|
-
if any(excluded in parts for excluded in exclude_dirs):
|
|
112
|
-
return False
|
|
113
|
-
|
|
114
|
-
return True
|
|
115
|
-
|
|
116
|
-
def backup_file(self, filepath: Path) -> Optional[Path]:
|
|
117
|
-
"""Create a timestamped backup of a file."""
|
|
118
|
-
# Check if we should track this file
|
|
119
|
-
if not self.should_track_file(filepath):
|
|
120
|
-
return None
|
|
121
|
-
|
|
122
|
-
try:
|
|
123
|
-
# Use relative path for organizing backups
|
|
124
|
-
rel_path = filepath.relative_to(self.project_dir)
|
|
125
|
-
|
|
126
|
-
# Create subdirectories in backup dir to mirror project structure
|
|
127
|
-
backup_subdir = self.backup_dir / rel_path.parent
|
|
128
|
-
backup_subdir.mkdir(parents=True, exist_ok=True)
|
|
129
|
-
|
|
130
|
-
# Create backup filename with timestamp
|
|
131
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
132
|
-
backup_name = f"{filepath.name}.{timestamp}.bak"
|
|
133
|
-
backup_path = backup_subdir / backup_name
|
|
134
|
-
|
|
135
|
-
# Copy the file if it exists
|
|
136
|
-
if filepath.exists():
|
|
137
|
-
shutil.copy2(filepath, backup_path)
|
|
138
|
-
else:
|
|
139
|
-
# Create empty backup for new files
|
|
140
|
-
backup_path.touch()
|
|
141
|
-
|
|
142
|
-
# Log the backup
|
|
143
|
-
self._log_operation(
|
|
144
|
-
{
|
|
145
|
-
"type": "backup",
|
|
146
|
-
"file": str(rel_path),
|
|
147
|
-
"backup": str(backup_path.relative_to(self.undo_dir)),
|
|
148
|
-
"timestamp": datetime.now().isoformat(),
|
|
149
|
-
}
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
# Clean up old backups for this file
|
|
153
|
-
self._cleanup_file_backups(backup_subdir, filepath.name, keep=10)
|
|
154
|
-
|
|
155
|
-
return backup_path
|
|
156
|
-
|
|
157
|
-
except Exception:
|
|
158
|
-
# Silent fail - don't interrupt user's work
|
|
159
|
-
return None
|
|
160
|
-
|
|
161
|
-
def _cleanup_file_backups(self, backup_dir: Path, filename: str, keep: int = 10):
|
|
162
|
-
"""Keep only the most recent backups for a specific file."""
|
|
163
|
-
try:
|
|
164
|
-
# Find all backups for this file
|
|
165
|
-
pattern = f"{filename}.*.bak"
|
|
166
|
-
backups = sorted(backup_dir.glob(pattern), key=lambda p: p.stat().st_mtime)
|
|
167
|
-
|
|
168
|
-
# Remove old backups
|
|
169
|
-
if len(backups) > keep:
|
|
170
|
-
for old_backup in backups[:-keep]:
|
|
171
|
-
old_backup.unlink()
|
|
172
|
-
|
|
173
|
-
except Exception:
|
|
174
|
-
pass # Silent cleanup failure
|
|
175
|
-
|
|
176
|
-
def _log_operation(self, operation: Dict):
|
|
177
|
-
"""Log an operation to the operations file."""
|
|
178
|
-
try:
|
|
179
|
-
with open(self.op_log_file, "a") as f:
|
|
180
|
-
json.dump(operation, f)
|
|
181
|
-
f.write("\n")
|
|
182
|
-
|
|
183
|
-
# Keep log file size reasonable (last 1000 operations)
|
|
184
|
-
self._trim_log_file(1000)
|
|
185
|
-
|
|
186
|
-
except Exception:
|
|
187
|
-
pass
|
|
188
|
-
|
|
189
|
-
def _trim_log_file(self, max_lines: int):
|
|
190
|
-
"""Keep only the last N operations in the log."""
|
|
191
|
-
try:
|
|
192
|
-
with open(self.op_log_file, "r") as f:
|
|
193
|
-
lines = f.readlines()
|
|
194
|
-
|
|
195
|
-
if len(lines) > max_lines:
|
|
196
|
-
with open(self.op_log_file, "w") as f:
|
|
197
|
-
f.writelines(lines[-max_lines:])
|
|
198
|
-
|
|
199
|
-
except Exception:
|
|
200
|
-
pass
|
|
201
|
-
|
|
202
|
-
def get_undo_status(self) -> Dict[str, any]:
|
|
203
|
-
"""Get current status of undo system."""
|
|
204
|
-
try:
|
|
205
|
-
backup_count = sum(1 for _ in self.backup_dir.rglob("*.bak"))
|
|
206
|
-
backup_size = sum(f.stat().st_size for f in self.backup_dir.rglob("*.bak"))
|
|
207
|
-
log_size = self.op_log_file.stat().st_size if self.op_log_file.exists() else 0
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
"enabled": True,
|
|
211
|
-
"location": str(self.undo_dir),
|
|
212
|
-
"backup_count": backup_count,
|
|
213
|
-
"backup_size_mb": backup_size / (1024 * 1024),
|
|
214
|
-
"log_size_kb": log_size / 1024,
|
|
215
|
-
"git_available": self.git_dir.exists(),
|
|
216
|
-
}
|
|
217
|
-
except Exception:
|
|
218
|
-
return {"enabled": False, "error": "Cannot access undo directory"}
|
|
219
|
-
|
|
220
|
-
def cleanup_old_backups(self, days: int = 7):
|
|
221
|
-
"""Remove backups older than specified days."""
|
|
222
|
-
try:
|
|
223
|
-
cutoff_time = time.time() - (days * 24 * 60 * 60)
|
|
224
|
-
|
|
225
|
-
for backup in self.backup_dir.rglob("*.bak"):
|
|
226
|
-
if backup.stat().st_mtime < cutoff_time:
|
|
227
|
-
backup.unlink()
|
|
228
|
-
|
|
229
|
-
except Exception:
|
|
230
|
-
pass
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
class ProjectSafeFileOperations:
|
|
234
|
-
"""File operations with project-local backup."""
|
|
235
|
-
|
|
236
|
-
def __init__(self, undo_service: ProjectUndoService):
|
|
237
|
-
self.undo = undo_service
|
|
238
|
-
|
|
239
|
-
async def safe_write(self, filepath: Path, content: str) -> Tuple[bool, str]:
|
|
240
|
-
"""Write file with automatic local backup."""
|
|
241
|
-
filepath = Path(filepath).resolve()
|
|
242
|
-
|
|
243
|
-
# Create backup before write
|
|
244
|
-
backup_path = self.undo.backup_file(filepath)
|
|
245
|
-
|
|
246
|
-
# Check if file exists for logging
|
|
247
|
-
if filepath.exists() and filepath.stat().st_size < 100_000:
|
|
248
|
-
try:
|
|
249
|
-
filepath.read_text() # Just check it's readable
|
|
250
|
-
except Exception:
|
|
251
|
-
pass
|
|
252
|
-
|
|
253
|
-
# Write the file
|
|
254
|
-
try:
|
|
255
|
-
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
256
|
-
filepath.write_text(content)
|
|
257
|
-
|
|
258
|
-
# Log the operation
|
|
259
|
-
self.undo._log_operation(
|
|
260
|
-
{
|
|
261
|
-
"type": "write",
|
|
262
|
-
"file": str(filepath.relative_to(self.undo.project_dir)),
|
|
263
|
-
"size": len(content),
|
|
264
|
-
"backup": (
|
|
265
|
-
str(backup_path.relative_to(self.undo.undo_dir)) if backup_path else None
|
|
266
|
-
),
|
|
267
|
-
"timestamp": datetime.now().isoformat(),
|
|
268
|
-
}
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
return True, "File written successfully"
|
|
272
|
-
|
|
273
|
-
except Exception as e:
|
|
274
|
-
return False, f"Failed to write file: {e}"
|
|
275
|
-
|
|
276
|
-
async def safe_delete(self, filepath: Path) -> Tuple[bool, str]:
|
|
277
|
-
"""Delete file with automatic local backup."""
|
|
278
|
-
filepath = Path(filepath).resolve()
|
|
279
|
-
|
|
280
|
-
if not filepath.exists():
|
|
281
|
-
return False, "File does not exist"
|
|
282
|
-
|
|
283
|
-
# Create backup before delete
|
|
284
|
-
backup_path = self.undo.backup_file(filepath)
|
|
285
|
-
|
|
286
|
-
try:
|
|
287
|
-
filepath.unlink()
|
|
288
|
-
|
|
289
|
-
# Log the operation
|
|
290
|
-
self.undo._log_operation(
|
|
291
|
-
{
|
|
292
|
-
"type": "delete",
|
|
293
|
-
"file": str(filepath.relative_to(self.undo.project_dir)),
|
|
294
|
-
"backup": (
|
|
295
|
-
str(backup_path.relative_to(self.undo.undo_dir)) if backup_path else None
|
|
296
|
-
),
|
|
297
|
-
"timestamp": datetime.now().isoformat(),
|
|
298
|
-
}
|
|
299
|
-
)
|
|
300
|
-
|
|
301
|
-
return True, f"File deleted (backup available in {self.undo.UNDO_DIR_NAME}/)"
|
|
302
|
-
|
|
303
|
-
except Exception as e:
|
|
304
|
-
return False, f"Failed to delete file: {e}"
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
def get_project_undo_service(state_manager: StateManager) -> ProjectUndoService:
|
|
308
|
-
"""Get or create project-local undo service."""
|
|
309
|
-
if not hasattr(state_manager.session, "project_undo"):
|
|
310
|
-
state_manager.session.project_undo = ProjectUndoService(state_manager)
|
|
311
|
-
return state_manager.session.project_undo
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"""TinyAgent tool implementations with decorators."""
|
|
2
|
-
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
|
-
from tinyagent import tool
|
|
6
|
-
|
|
7
|
-
from tunacode.exceptions import ToolExecutionError
|
|
8
|
-
from tunacode.ui import console as ui
|
|
9
|
-
|
|
10
|
-
# Import the existing tool classes to reuse their logic
|
|
11
|
-
from .read_file import ReadFileTool
|
|
12
|
-
from .run_command import RunCommandTool
|
|
13
|
-
from .update_file import UpdateFileTool
|
|
14
|
-
from .write_file import WriteFileTool
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@tool
|
|
18
|
-
async def read_file(filepath: str) -> str:
|
|
19
|
-
"""Read the contents of a file.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
filepath: The path to the file to read.
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
The contents of the file.
|
|
26
|
-
|
|
27
|
-
Raises:
|
|
28
|
-
Exception: If file cannot be read.
|
|
29
|
-
"""
|
|
30
|
-
tool_instance = ReadFileTool(ui)
|
|
31
|
-
try:
|
|
32
|
-
result = await tool_instance.execute(filepath)
|
|
33
|
-
return result
|
|
34
|
-
except ToolExecutionError as e:
|
|
35
|
-
# tinyAgent expects exceptions to be raised, not returned as strings
|
|
36
|
-
raise Exception(str(e))
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@tool
|
|
40
|
-
async def write_file(filepath: str, content: str) -> str:
|
|
41
|
-
"""Write content to a file.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
filepath: The path to the file to write.
|
|
45
|
-
content: The content to write to the file.
|
|
46
|
-
|
|
47
|
-
Returns:
|
|
48
|
-
Success message.
|
|
49
|
-
|
|
50
|
-
Raises:
|
|
51
|
-
Exception: If file cannot be written.
|
|
52
|
-
"""
|
|
53
|
-
tool_instance = WriteFileTool(ui)
|
|
54
|
-
try:
|
|
55
|
-
result = await tool_instance.execute(filepath, content)
|
|
56
|
-
return result
|
|
57
|
-
except ToolExecutionError as e:
|
|
58
|
-
raise Exception(str(e))
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
@tool
|
|
62
|
-
async def update_file(filepath: str, old_content: str, new_content: str) -> str:
|
|
63
|
-
"""Update specific content in a file.
|
|
64
|
-
|
|
65
|
-
Args:
|
|
66
|
-
filepath: The path to the file to update.
|
|
67
|
-
old_content: The content to find and replace.
|
|
68
|
-
new_content: The new content to insert.
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
Success message.
|
|
72
|
-
|
|
73
|
-
Raises:
|
|
74
|
-
Exception: If file cannot be updated.
|
|
75
|
-
"""
|
|
76
|
-
tool_instance = UpdateFileTool(ui)
|
|
77
|
-
try:
|
|
78
|
-
result = await tool_instance.execute(filepath, old_content, new_content)
|
|
79
|
-
return result
|
|
80
|
-
except ToolExecutionError as e:
|
|
81
|
-
raise Exception(str(e))
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
@tool
|
|
85
|
-
async def run_command(command: str, timeout: Optional[int] = None) -> str:
|
|
86
|
-
"""Run a shell command.
|
|
87
|
-
|
|
88
|
-
Args:
|
|
89
|
-
command: The command to run.
|
|
90
|
-
timeout: Optional timeout in seconds.
|
|
91
|
-
|
|
92
|
-
Returns:
|
|
93
|
-
The command output.
|
|
94
|
-
|
|
95
|
-
Raises:
|
|
96
|
-
Exception: If command fails.
|
|
97
|
-
"""
|
|
98
|
-
tool_instance = RunCommandTool(ui)
|
|
99
|
-
try:
|
|
100
|
-
result = await tool_instance.execute(command, timeout)
|
|
101
|
-
return result
|
|
102
|
-
except ToolExecutionError as e:
|
|
103
|
-
raise Exception(str(e))
|
tunacode/utils/lazy_imports.py
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"""Lazy imports for heavy modules to improve startup time."""
|
|
2
|
-
|
|
3
|
-
import sys
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
5
|
-
|
|
6
|
-
if TYPE_CHECKING:
|
|
7
|
-
# For type checking only
|
|
8
|
-
import prompt_toolkit # noqa: F401
|
|
9
|
-
import rich # noqa: F401
|
|
10
|
-
from prompt_toolkit import PromptSession # noqa: F401
|
|
11
|
-
from prompt_toolkit.completion import Completer # noqa: F401
|
|
12
|
-
from rich.console import Console # noqa: F401
|
|
13
|
-
from rich.markdown import Markdown # noqa: F401
|
|
14
|
-
from rich.panel import Panel # noqa: F401
|
|
15
|
-
from rich.table import Table # noqa: F401
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def lazy_import(module_name: str):
|
|
19
|
-
"""Lazy import a module."""
|
|
20
|
-
if module_name not in sys.modules:
|
|
21
|
-
__import__(module_name)
|
|
22
|
-
return sys.modules[module_name]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# Lazy accessors
|
|
26
|
-
def get_rich():
|
|
27
|
-
"""Get rich module lazily."""
|
|
28
|
-
return lazy_import("rich")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def get_rich_console():
|
|
32
|
-
"""Get rich console lazily."""
|
|
33
|
-
rich_console = lazy_import("rich.console")
|
|
34
|
-
return rich_console.Console
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def get_rich_table():
|
|
38
|
-
"""Get rich table lazily."""
|
|
39
|
-
return lazy_import("rich.table").Table
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def get_rich_panel():
|
|
43
|
-
"""Get rich panel lazily."""
|
|
44
|
-
return lazy_import("rich.panel").Panel
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def get_rich_markdown():
|
|
48
|
-
"""Get rich markdown lazily."""
|
|
49
|
-
return lazy_import("rich.markdown").Markdown
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def get_prompt_toolkit():
|
|
53
|
-
"""Get prompt_toolkit lazily."""
|
|
54
|
-
return lazy_import("prompt_toolkit")
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def get_prompt_session():
|
|
58
|
-
"""Get PromptSession lazily."""
|
|
59
|
-
return lazy_import("prompt_toolkit").PromptSession
|
tunacode/utils/regex_cache.py
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"""Pre-compiled regex patterns for better performance."""
|
|
2
|
-
|
|
3
|
-
import re
|
|
4
|
-
|
|
5
|
-
# Command patterns
|
|
6
|
-
MODEL_COMMAND_PATTERN = re.compile(r"(?:^|\n)\s*(?:/model|/m)\s+\S*$")
|
|
7
|
-
COMMAND_START_PATTERN = re.compile(r"^/\w+")
|
|
8
|
-
|
|
9
|
-
# File reference patterns
|
|
10
|
-
FILE_REF_PATTERN = re.compile(r"@([^\s]+)")
|
|
11
|
-
FILE_PATH_PATTERN = re.compile(r"^[a-zA-Z0-9_\-./]+$")
|
|
12
|
-
|
|
13
|
-
# Code patterns
|
|
14
|
-
IMPORT_PATTERN = re.compile(r"^\s*(?:from|import)\s+\S+")
|
|
15
|
-
FUNCTION_DEF_PATTERN = re.compile(r"^\s*def\s+(\w+)\s*\(")
|
|
16
|
-
CLASS_DEF_PATTERN = re.compile(r"^\s*class\s+(\w+)")
|
|
17
|
-
|
|
18
|
-
# Environment variable patterns
|
|
19
|
-
ENV_VAR_PATTERN = re.compile(r"\$\{(\w+)(?::([^}]*))?\}")
|
|
20
|
-
API_KEY_PATTERN = re.compile(r"_API_KEY$")
|
|
21
|
-
|
|
22
|
-
# Common text patterns
|
|
23
|
-
WHITESPACE_PATTERN = re.compile(r"\s+")
|
|
24
|
-
WORD_BOUNDARY_PATTERN = re.compile(r"\b\w+\b")
|
|
25
|
-
LINE_SPLIT_PATTERN = re.compile(r"\r?\n")
|
|
26
|
-
|
|
27
|
-
# Tool output patterns
|
|
28
|
-
ANSI_ESCAPE_PATTERN = re.compile(r"\x1b\[[0-9;]*m")
|
|
29
|
-
ERROR_PATTERN = re.compile(r"(?i)(error|exception|failed|failure):\s*(.+)")
|
|
30
|
-
|
|
31
|
-
# Model name patterns
|
|
32
|
-
MODEL_PROVIDER_PATTERN = re.compile(r"^(\w+):(.+)$")
|
|
33
|
-
OPENROUTER_MODEL_PATTERN = re.compile(r"^openrouter:(.+)$")
|