tunacode-cli 0.0.11__py3-none-any.whl → 0.0.13__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 +3 -4
- tunacode/cli/main.py +5 -4
- tunacode/cli/repl.py +2 -13
- tunacode/cli/textual_app.py +423 -0
- tunacode/cli/textual_bridge.py +158 -0
- tunacode/configuration/defaults.py +3 -4
- tunacode/configuration/models.py +3 -3
- tunacode/configuration/settings.py +2 -2
- tunacode/constants.py +2 -10
- tunacode/core/agents/main.py +5 -3
- tunacode/core/setup/__init__.py +0 -2
- tunacode/core/setup/agent_setup.py +3 -3
- tunacode/core/setup/base.py +3 -3
- tunacode/core/setup/coordinator.py +2 -2
- tunacode/core/setup/environment_setup.py +2 -2
- tunacode/core/setup/git_safety_setup.py +1 -2
- tunacode/core/state.py +3 -3
- tunacode/setup.py +2 -3
- tunacode/tools/__init__.py +1 -0
- tunacode/tools/base.py +4 -43
- tunacode/tools/bash.py +252 -0
- tunacode/tools/read_file.py +2 -2
- tunacode/tools/run_command.py +2 -2
- tunacode/tools/update_file.py +3 -3
- tunacode/tools/write_file.py +3 -3
- tunacode/ui/completers.py +1 -1
- tunacode/ui/console.py +2 -3
- tunacode/ui/constants.py +1 -1
- tunacode/ui/decorators.py +2 -2
- tunacode/ui/input.py +1 -1
- tunacode/ui/keybindings.py +1 -1
- tunacode/ui/output.py +11 -4
- tunacode/ui/panels.py +3 -4
- tunacode/ui/prompt_manager.py +1 -1
- tunacode/ui/validators.py +1 -1
- tunacode/utils/diff_utils.py +3 -3
- {tunacode_cli-0.0.11.dist-info → tunacode_cli-0.0.13.dist-info}/METADATA +94 -40
- tunacode_cli-0.0.13.dist-info/RECORD +66 -0
- tunacode/core/setup/undo_setup.py +0 -33
- tunacode/services/undo_service.py +0 -244
- tunacode_cli-0.0.11.dist-info/RECORD +0 -65
- {tunacode_cli-0.0.11.dist-info → tunacode_cli-0.0.13.dist-info}/WHEEL +0 -0
- {tunacode_cli-0.0.11.dist-info → tunacode_cli-0.0.13.dist-info}/entry_points.txt +0 -0
- {tunacode_cli-0.0.11.dist-info → tunacode_cli-0.0.13.dist-info}/licenses/LICENSE +0 -0
- {tunacode_cli-0.0.11.dist-info → tunacode_cli-0.0.13.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Module: tunacode.configuration.settings
|
|
3
3
|
|
|
4
|
-
Application settings management for the
|
|
5
|
-
|
|
4
|
+
Application settings management for the TunaCode CLI.
|
|
5
|
+
Handles configuration paths, model registries, and application metadata.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from pathlib import Path
|
tunacode/constants.py
CHANGED
|
@@ -7,7 +7,7 @@ Centralizes all magic strings, UI text, error messages, and application constant
|
|
|
7
7
|
|
|
8
8
|
# Application info
|
|
9
9
|
APP_NAME = "TunaCode"
|
|
10
|
-
APP_VERSION = "0.0.
|
|
10
|
+
APP_VERSION = "0.0.13"
|
|
11
11
|
|
|
12
12
|
# File patterns
|
|
13
13
|
GUIDE_FILE_PATTERN = "{name}.md"
|
|
@@ -35,7 +35,6 @@ CMD_HELP = "/help"
|
|
|
35
35
|
CMD_CLEAR = "/clear"
|
|
36
36
|
CMD_DUMP = "/dump"
|
|
37
37
|
CMD_YOLO = "/yolo"
|
|
38
|
-
CMD_UNDO = "/undo"
|
|
39
38
|
CMD_COMPACT = "/compact"
|
|
40
39
|
CMD_MODEL = "/model"
|
|
41
40
|
CMD_EXIT = "exit"
|
|
@@ -46,7 +45,6 @@ DESC_HELP = "Show this help message"
|
|
|
46
45
|
DESC_CLEAR = "Clear the conversation history"
|
|
47
46
|
DESC_DUMP = "Show the current conversation history"
|
|
48
47
|
DESC_YOLO = "Toggle confirmation prompts on/off"
|
|
49
|
-
DESC_UNDO = "Undo the last file change"
|
|
50
48
|
DESC_COMPACT = "Summarize the conversation context"
|
|
51
49
|
DESC_MODEL = "List available models"
|
|
52
50
|
DESC_MODEL_SWITCH = "Switch to a specific model"
|
|
@@ -56,7 +54,7 @@ DESC_EXIT = "Exit the application"
|
|
|
56
54
|
# Command Configuration
|
|
57
55
|
COMMAND_PREFIX = "/"
|
|
58
56
|
COMMAND_CATEGORIES = {
|
|
59
|
-
"state": ["yolo"
|
|
57
|
+
"state": ["yolo"],
|
|
60
58
|
"debug": ["dump", "compact"],
|
|
61
59
|
"ui": ["clear", "help"],
|
|
62
60
|
"config": ["model"],
|
|
@@ -110,7 +108,6 @@ ERROR_COMMAND_NOT_FOUND = "Error: Command not found or failed to execute:"
|
|
|
110
108
|
ERROR_COMMAND_EXECUTION = (
|
|
111
109
|
"Error: Command not found or failed to execute: {command}. Details: {error}"
|
|
112
110
|
)
|
|
113
|
-
ERROR_UNDO_INIT = "Error initializing undo system: {e}"
|
|
114
111
|
|
|
115
112
|
# Command output messages
|
|
116
113
|
CMD_OUTPUT_NO_OUTPUT = "No output."
|
|
@@ -118,11 +115,6 @@ CMD_OUTPUT_NO_ERRORS = "No errors."
|
|
|
118
115
|
CMD_OUTPUT_FORMAT = "STDOUT:\n{output}\n\nSTDERR:\n{error}"
|
|
119
116
|
CMD_OUTPUT_TRUNCATED = "\n...\n[truncated]\n...\n"
|
|
120
117
|
|
|
121
|
-
# Undo system messages
|
|
122
|
-
UNDO_DISABLED_HOME = "Undo system disabled, running from home directory"
|
|
123
|
-
UNDO_DISABLED_NO_GIT = "⚠️ Not in a git repository - undo functionality will be limited"
|
|
124
|
-
UNDO_INITIAL_COMMIT = "Initial commit for tunacode undo history"
|
|
125
|
-
UNDO_GIT_TIMEOUT = "Git initialization timed out"
|
|
126
118
|
|
|
127
119
|
# Log/status messages
|
|
128
120
|
MSG_UPDATE_AVAILABLE = "Update available: v{latest_version}"
|
tunacode/core/agents/main.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""Module:
|
|
1
|
+
"""Module: tunacode.core.agents.main
|
|
2
2
|
|
|
3
|
-
Main agent functionality and coordination for the
|
|
4
|
-
|
|
3
|
+
Main agent functionality and coordination for the TunaCode CLI.
|
|
4
|
+
Handles agent creation, configuration, and request processing.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from datetime import datetime, timezone
|
|
@@ -12,6 +12,7 @@ from pydantic_ai.messages import ModelRequest, ToolReturnPart
|
|
|
12
12
|
|
|
13
13
|
from tunacode.core.state import StateManager
|
|
14
14
|
from tunacode.services.mcp import get_mcp_servers
|
|
15
|
+
from tunacode.tools.bash import bash
|
|
15
16
|
from tunacode.tools.read_file import read_file
|
|
16
17
|
from tunacode.tools.run_command import run_command
|
|
17
18
|
from tunacode.tools.update_file import update_file
|
|
@@ -37,6 +38,7 @@ def get_or_create_agent(model: ModelName, state_manager: StateManager) -> Pydant
|
|
|
37
38
|
state_manager.session.agents[model] = Agent(
|
|
38
39
|
model=model,
|
|
39
40
|
tools=[
|
|
41
|
+
Tool(bash, max_retries=max_retries),
|
|
40
42
|
Tool(read_file, max_retries=max_retries),
|
|
41
43
|
Tool(run_command, max_retries=max_retries),
|
|
42
44
|
Tool(update_file, max_retries=max_retries),
|
tunacode/core/setup/__init__.py
CHANGED
|
@@ -4,7 +4,6 @@ from .config_setup import ConfigSetup
|
|
|
4
4
|
from .coordinator import SetupCoordinator
|
|
5
5
|
from .environment_setup import EnvironmentSetup
|
|
6
6
|
from .git_safety_setup import GitSafetySetup
|
|
7
|
-
from .undo_setup import UndoSetup
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
9
|
"BaseSetup",
|
|
@@ -12,6 +11,5 @@ __all__ = [
|
|
|
12
11
|
"ConfigSetup",
|
|
13
12
|
"EnvironmentSetup",
|
|
14
13
|
"GitSafetySetup",
|
|
15
|
-
"UndoSetup",
|
|
16
14
|
"AgentSetup",
|
|
17
15
|
]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""Module:
|
|
1
|
+
"""Module: tunacode.core.setup.agent_setup
|
|
2
2
|
|
|
3
|
-
Agent initialization and configuration for the
|
|
4
|
-
|
|
3
|
+
Agent initialization and configuration for the TunaCode CLI.
|
|
4
|
+
Sets up AI agents with proper model configurations and tools.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from typing import Any, Optional
|
tunacode/core/setup/base.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""Module:
|
|
1
|
+
"""Module: tunacode.core.setup.base
|
|
2
2
|
|
|
3
|
-
Base setup step abstraction for the
|
|
4
|
-
|
|
3
|
+
Base setup step abstraction for the TunaCode CLI initialization process.
|
|
4
|
+
Provides common interface and functionality for all setup steps.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from abc import ABC, abstractmethod
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""Module:
|
|
1
|
+
"""Module: tunacode.core.setup.coordinator
|
|
2
2
|
|
|
3
|
-
Setup orchestration and coordination for the
|
|
3
|
+
Setup orchestration and coordination for the TunaCode CLI.
|
|
4
4
|
Manages the execution order and validation of all registered setup steps.
|
|
5
5
|
"""
|
|
6
6
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Module: tunacode.core.setup.environment_setup
|
|
2
2
|
|
|
3
|
-
Environment detection and configuration for the
|
|
4
|
-
|
|
3
|
+
Environment detection and configuration for the TunaCode CLI.
|
|
4
|
+
Validates system requirements and environment variables.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os
|
|
@@ -133,8 +133,7 @@ class GitSafetySetup(BaseSetup):
|
|
|
133
133
|
await panel(
|
|
134
134
|
"⚠️ Working Without Safety Branch",
|
|
135
135
|
"You've chosen to work directly on your current branch.\n"
|
|
136
|
-
"TunaCode will modify files in place. Make sure you have backups
|
|
137
|
-
"You can always use /undo to revert changes.",
|
|
136
|
+
"TunaCode will modify files in place. Make sure you have backups!",
|
|
138
137
|
border_style="red"
|
|
139
138
|
)
|
|
140
139
|
# Save preference
|
tunacode/core/state.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""Module:
|
|
1
|
+
"""Module: tunacode.core.state
|
|
2
2
|
|
|
3
|
-
State management system for session data in
|
|
4
|
-
|
|
3
|
+
State management system for session data in TunaCode CLI.
|
|
4
|
+
Handles user preferences, conversation history, and runtime state.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import uuid
|
tunacode/setup.py
CHANGED
|
@@ -8,7 +8,7 @@ Provides high-level setup functions for initializing the application and its age
|
|
|
8
8
|
from typing import Any, Optional
|
|
9
9
|
|
|
10
10
|
from tunacode.core.setup import (AgentSetup, ConfigSetup, EnvironmentSetup, GitSafetySetup,
|
|
11
|
-
SetupCoordinator
|
|
11
|
+
SetupCoordinator)
|
|
12
12
|
from tunacode.core.state import StateManager
|
|
13
13
|
|
|
14
14
|
|
|
@@ -29,8 +29,7 @@ async def setup(run_setup: bool, state_manager: StateManager, cli_config: dict =
|
|
|
29
29
|
config_setup.cli_config = cli_config
|
|
30
30
|
coordinator.register_step(config_setup)
|
|
31
31
|
coordinator.register_step(EnvironmentSetup(state_manager))
|
|
32
|
-
coordinator.register_step(GitSafetySetup(state_manager))
|
|
33
|
-
coordinator.register_step(UndoSetup(state_manager))
|
|
32
|
+
coordinator.register_step(GitSafetySetup(state_manager))
|
|
34
33
|
|
|
35
34
|
# Run all setup steps
|
|
36
35
|
await coordinator.run_setup(force_setup=run_setup)
|
tunacode/tools/__init__.py
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""TunaCode tools package."""
|
tunacode/tools/base.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Base tool class for all
|
|
1
|
+
"""Base tool class for all TunaCode tools.
|
|
2
2
|
|
|
3
3
|
This module provides a base class that implements common patterns
|
|
4
4
|
for all tools including error handling, UI logging, and ModelRetry support.
|
|
@@ -13,7 +13,7 @@ from tunacode.types import FilePath, ToolName, ToolResult, UILogger
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class BaseTool(ABC):
|
|
16
|
-
"""Base class for all
|
|
16
|
+
"""Base class for all TunaCode tools providing common functionality."""
|
|
17
17
|
|
|
18
18
|
def __init__(self, ui_logger: UILogger | None = None):
|
|
19
19
|
"""Initialize the base tool.
|
|
@@ -42,11 +42,6 @@ class BaseTool(ABC):
|
|
|
42
42
|
if self.ui:
|
|
43
43
|
await self.ui.info(f"{self.tool_name}({self._format_args(*args, **kwargs)})")
|
|
44
44
|
result = await self._execute(*args, **kwargs)
|
|
45
|
-
|
|
46
|
-
# For file operations, try to create a git commit for undo tracking
|
|
47
|
-
if isinstance(self, FileBasedTool):
|
|
48
|
-
await self._commit_for_undo()
|
|
49
|
-
|
|
50
45
|
return result
|
|
51
46
|
except ModelRetry as e:
|
|
52
47
|
# Log as warning and re-raise for pydantic-ai
|
|
@@ -142,46 +137,12 @@ class FileBasedTool(BaseTool):
|
|
|
142
137
|
"""Base class for tools that work with files.
|
|
143
138
|
|
|
144
139
|
Provides common file-related functionality like:
|
|
145
|
-
- Path validation
|
|
140
|
+
- Path validation
|
|
146
141
|
- File existence checking
|
|
147
142
|
- Directory creation
|
|
148
143
|
- Encoding handling
|
|
149
|
-
-
|
|
144
|
+
- Enhanced error handling for file operations
|
|
150
145
|
"""
|
|
151
|
-
|
|
152
|
-
async def _commit_for_undo(self) -> None:
|
|
153
|
-
"""Create a git commit for undo tracking after file operations.
|
|
154
|
-
|
|
155
|
-
This method gracefully handles cases where git is not available:
|
|
156
|
-
- No git repository: Warns user about limited undo functionality
|
|
157
|
-
- Git command fails: Warns but doesn't break the main operation
|
|
158
|
-
- Any other error: Silently continues (file operation still succeeds)
|
|
159
|
-
"""
|
|
160
|
-
try:
|
|
161
|
-
# Import here to avoid circular imports
|
|
162
|
-
from tunacode.services.undo_service import commit_for_undo, is_in_git_project
|
|
163
|
-
|
|
164
|
-
# Check if we're in a git project first
|
|
165
|
-
if not is_in_git_project():
|
|
166
|
-
if self.ui:
|
|
167
|
-
await self.ui.muted("⚠️ No git repository - undo functionality limited")
|
|
168
|
-
return
|
|
169
|
-
|
|
170
|
-
# Try to create commit with tool name as prefix
|
|
171
|
-
success = commit_for_undo(message_prefix=f"tunacode {self.tool_name.lower()}")
|
|
172
|
-
if success and self.ui:
|
|
173
|
-
await self.ui.muted("• Git commit created for undo tracking")
|
|
174
|
-
elif self.ui:
|
|
175
|
-
await self.ui.muted("⚠️ Could not create git commit - undo may not work")
|
|
176
|
-
except Exception:
|
|
177
|
-
# Silently ignore commit errors - don't break the main file operation
|
|
178
|
-
# The file operation itself succeeded, we just can't track it for undo
|
|
179
|
-
if self.ui:
|
|
180
|
-
try:
|
|
181
|
-
await self.ui.muted("⚠️ Git commit failed - undo functionality limited")
|
|
182
|
-
except:
|
|
183
|
-
# Even the warning failed, just continue silently
|
|
184
|
-
pass
|
|
185
146
|
|
|
186
147
|
def _format_args(self, filepath: FilePath, *args, **kwargs) -> str:
|
|
187
148
|
"""Format arguments with filepath as first argument."""
|
tunacode/tools/bash.py
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: tunacode.tools.bash
|
|
3
|
+
|
|
4
|
+
Enhanced bash execution tool for agent operations in the TunaCode application.
|
|
5
|
+
Provides advanced shell command execution with working directory support,
|
|
6
|
+
environment variables, timeouts, and improved output handling.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
from typing import Dict, Optional
|
|
13
|
+
|
|
14
|
+
from pydantic_ai.exceptions import ModelRetry
|
|
15
|
+
|
|
16
|
+
from tunacode.constants import MAX_COMMAND_OUTPUT
|
|
17
|
+
from tunacode.exceptions import ToolExecutionError
|
|
18
|
+
from tunacode.tools.base import BaseTool
|
|
19
|
+
from tunacode.types import ToolResult
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BashTool(BaseTool):
|
|
23
|
+
"""Enhanced shell command execution tool with advanced features."""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def tool_name(self) -> str:
|
|
27
|
+
return "Bash"
|
|
28
|
+
|
|
29
|
+
async def _execute(
|
|
30
|
+
self,
|
|
31
|
+
command: str,
|
|
32
|
+
cwd: Optional[str] = None,
|
|
33
|
+
env: Optional[Dict[str, str]] = None,
|
|
34
|
+
timeout: Optional[int] = 30,
|
|
35
|
+
capture_output: bool = True,
|
|
36
|
+
) -> ToolResult:
|
|
37
|
+
"""Execute a bash command with enhanced features.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
command: The bash command to execute
|
|
41
|
+
cwd: Working directory for the command (defaults to current)
|
|
42
|
+
env: Additional environment variables to set
|
|
43
|
+
timeout: Command timeout in seconds (default 30, max 300)
|
|
44
|
+
capture_output: Whether to capture stdout/stderr (default True)
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
ToolResult: Formatted output with exit code, stdout, and stderr
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ModelRetry: For guidance on command failures
|
|
51
|
+
Exception: Any command execution errors
|
|
52
|
+
"""
|
|
53
|
+
# Validate and sanitize inputs
|
|
54
|
+
if timeout and (timeout < 1 or timeout > 300):
|
|
55
|
+
raise ModelRetry(
|
|
56
|
+
"Timeout must be between 1 and 300 seconds. "
|
|
57
|
+
"Use shorter timeouts for quick commands, longer for builds/tests."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Validate working directory if specified
|
|
61
|
+
if cwd and not os.path.isdir(cwd):
|
|
62
|
+
raise ModelRetry(
|
|
63
|
+
f"Working directory '{cwd}' does not exist. "
|
|
64
|
+
"Please verify the path or create the directory first."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Check for potentially destructive commands
|
|
68
|
+
destructive_patterns = ["rm -rf", "rm -r", "rm /", "dd if=", "mkfs", "fdisk"]
|
|
69
|
+
if any(pattern in command for pattern in destructive_patterns):
|
|
70
|
+
raise ModelRetry(
|
|
71
|
+
f"Command contains potentially destructive operations: {command}\n"
|
|
72
|
+
"Please confirm this is intentional and safe for your system."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Prepare environment
|
|
76
|
+
exec_env = os.environ.copy()
|
|
77
|
+
if env:
|
|
78
|
+
# Sanitize environment variables
|
|
79
|
+
for key, value in env.items():
|
|
80
|
+
if isinstance(key, str) and isinstance(value, str):
|
|
81
|
+
exec_env[key] = value
|
|
82
|
+
|
|
83
|
+
# Set working directory
|
|
84
|
+
exec_cwd = cwd or os.getcwd()
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
# Execute command with timeout
|
|
88
|
+
process = await asyncio.create_subprocess_shell(
|
|
89
|
+
command,
|
|
90
|
+
stdout=subprocess.PIPE if capture_output else None,
|
|
91
|
+
stderr=subprocess.PIPE if capture_output else None,
|
|
92
|
+
cwd=exec_cwd,
|
|
93
|
+
env=exec_env,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
stdout, stderr = await asyncio.wait_for(
|
|
98
|
+
process.communicate(), timeout=timeout
|
|
99
|
+
)
|
|
100
|
+
except asyncio.TimeoutError:
|
|
101
|
+
# Kill the process if it times out
|
|
102
|
+
process.kill()
|
|
103
|
+
await process.wait()
|
|
104
|
+
raise ModelRetry(
|
|
105
|
+
f"Command timed out after {timeout} seconds: {command}\n"
|
|
106
|
+
"Consider using a longer timeout or breaking the command into smaller parts."
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Decode output
|
|
110
|
+
stdout_text = stdout.decode("utf-8", errors="replace").strip() if stdout else ""
|
|
111
|
+
stderr_text = stderr.decode("utf-8", errors="replace").strip() if stderr else ""
|
|
112
|
+
|
|
113
|
+
# Format output
|
|
114
|
+
result = self._format_output(
|
|
115
|
+
command=command,
|
|
116
|
+
exit_code=process.returncode,
|
|
117
|
+
stdout=stdout_text,
|
|
118
|
+
stderr=stderr_text,
|
|
119
|
+
cwd=exec_cwd,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Handle non-zero exit codes as guidance, not failures
|
|
123
|
+
if process.returncode != 0 and stderr_text:
|
|
124
|
+
# Provide guidance for common error patterns
|
|
125
|
+
if "command not found" in stderr_text.lower():
|
|
126
|
+
raise ModelRetry(
|
|
127
|
+
f"Command '{command}' not found. "
|
|
128
|
+
"Check if the command is installed or use the full path."
|
|
129
|
+
)
|
|
130
|
+
elif "permission denied" in stderr_text.lower():
|
|
131
|
+
raise ModelRetry(
|
|
132
|
+
f"Permission denied for command '{command}'. "
|
|
133
|
+
"You may need elevated privileges or different file permissions."
|
|
134
|
+
)
|
|
135
|
+
elif "no such file or directory" in stderr_text.lower():
|
|
136
|
+
raise ModelRetry(
|
|
137
|
+
f"File or directory not found when running '{command}'. "
|
|
138
|
+
"Verify the path exists or create it first."
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return result
|
|
142
|
+
|
|
143
|
+
except FileNotFoundError:
|
|
144
|
+
raise ModelRetry(
|
|
145
|
+
f"Shell not found. Cannot execute command: {command}\n"
|
|
146
|
+
"This typically indicates a system configuration issue."
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def _format_output(
|
|
150
|
+
self,
|
|
151
|
+
command: str,
|
|
152
|
+
exit_code: int,
|
|
153
|
+
stdout: str,
|
|
154
|
+
stderr: str,
|
|
155
|
+
cwd: str,
|
|
156
|
+
) -> str:
|
|
157
|
+
"""Format command output in a consistent way.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
command: The executed command
|
|
161
|
+
exit_code: The process exit code
|
|
162
|
+
stdout: Standard output content
|
|
163
|
+
stderr: Standard error content
|
|
164
|
+
cwd: Working directory where command was executed
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
str: Formatted output string
|
|
168
|
+
"""
|
|
169
|
+
# Build the result
|
|
170
|
+
lines = [
|
|
171
|
+
f"Command: {command}",
|
|
172
|
+
f"Exit Code: {exit_code}",
|
|
173
|
+
f"Working Directory: {cwd}",
|
|
174
|
+
"",
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
# Add stdout if present
|
|
178
|
+
if stdout:
|
|
179
|
+
lines.extend(["STDOUT:", stdout, ""])
|
|
180
|
+
else:
|
|
181
|
+
lines.extend(["STDOUT:", "(no output)", ""])
|
|
182
|
+
|
|
183
|
+
# Add stderr if present
|
|
184
|
+
if stderr:
|
|
185
|
+
lines.extend(["STDERR:", stderr])
|
|
186
|
+
else:
|
|
187
|
+
lines.extend(["STDERR:", "(no errors)"])
|
|
188
|
+
|
|
189
|
+
result = "\n".join(lines)
|
|
190
|
+
|
|
191
|
+
# Truncate if too long
|
|
192
|
+
if len(result) > MAX_COMMAND_OUTPUT:
|
|
193
|
+
truncate_point = MAX_COMMAND_OUTPUT - 100 # Leave room for truncation message
|
|
194
|
+
result = result[:truncate_point] + "\n\n[... output truncated ...]"
|
|
195
|
+
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
def _format_args(
|
|
199
|
+
self,
|
|
200
|
+
command: str,
|
|
201
|
+
cwd: Optional[str] = None,
|
|
202
|
+
env: Optional[Dict[str, str]] = None,
|
|
203
|
+
timeout: Optional[int] = None,
|
|
204
|
+
**kwargs,
|
|
205
|
+
) -> str:
|
|
206
|
+
"""Format arguments for display in UI logging."""
|
|
207
|
+
args = [repr(command)]
|
|
208
|
+
|
|
209
|
+
if cwd:
|
|
210
|
+
args.append(f"cwd={repr(cwd)}")
|
|
211
|
+
if timeout:
|
|
212
|
+
args.append(f"timeout={timeout}")
|
|
213
|
+
if env:
|
|
214
|
+
env_summary = f"{len(env)} vars" if len(env) > 3 else str(env)
|
|
215
|
+
args.append(f"env={env_summary}")
|
|
216
|
+
|
|
217
|
+
return ", ".join(args)
|
|
218
|
+
|
|
219
|
+
def _get_error_context(self, command: str = None, **kwargs) -> str:
|
|
220
|
+
"""Get error context for bash execution."""
|
|
221
|
+
if command:
|
|
222
|
+
return f"executing bash command '{command}'"
|
|
223
|
+
return super()._get_error_context()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# Create the function that maintains the existing interface
|
|
227
|
+
async def bash(
|
|
228
|
+
command: str,
|
|
229
|
+
cwd: Optional[str] = None,
|
|
230
|
+
env: Optional[Dict[str, str]] = None,
|
|
231
|
+
timeout: Optional[int] = 30,
|
|
232
|
+
capture_output: bool = True,
|
|
233
|
+
) -> ToolResult:
|
|
234
|
+
"""
|
|
235
|
+
Execute a bash command with enhanced features.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
command (str): The bash command to execute
|
|
239
|
+
cwd (Optional[str]): Working directory for the command
|
|
240
|
+
env (Optional[Dict[str, str]]): Additional environment variables
|
|
241
|
+
timeout (Optional[int]): Command timeout in seconds (default 30, max 300)
|
|
242
|
+
capture_output (bool): Whether to capture stdout/stderr
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
ToolResult: Formatted output with exit code, stdout, and stderr
|
|
246
|
+
"""
|
|
247
|
+
tool = BashTool()
|
|
248
|
+
try:
|
|
249
|
+
return await tool.execute(command, cwd=cwd, env=env, timeout=timeout, capture_output=capture_output)
|
|
250
|
+
except ToolExecutionError as e:
|
|
251
|
+
# Return error message for pydantic-ai compatibility
|
|
252
|
+
return str(e)
|
tunacode/tools/read_file.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Module:
|
|
2
|
+
Module: tunacode.tools.read_file
|
|
3
3
|
|
|
4
|
-
File reading tool for agent operations in the
|
|
4
|
+
File reading tool for agent operations in the TunaCode application.
|
|
5
5
|
Provides safe file reading with size limits and proper error handling.
|
|
6
6
|
"""
|
|
7
7
|
|
tunacode/tools/run_command.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Module:
|
|
2
|
+
Module: tunacode.tools.run_command
|
|
3
3
|
|
|
4
|
-
Command execution tool for agent operations in the
|
|
4
|
+
Command execution tool for agent operations in the TunaCode application.
|
|
5
5
|
Provides controlled shell command execution with output capture and truncation.
|
|
6
6
|
"""
|
|
7
7
|
|
tunacode/tools/update_file.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Module:
|
|
2
|
+
Module: tunacode.tools.update_file
|
|
3
3
|
|
|
4
|
-
File update tool for agent operations in the
|
|
5
|
-
|
|
4
|
+
File update tool for agent operations in the TunaCode application.
|
|
5
|
+
Provides targeted file content modification with diff-based updates.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import os
|
tunacode/tools/write_file.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Module:
|
|
2
|
+
Module: tunacode.tools.write_file
|
|
3
3
|
|
|
4
|
-
File writing tool for agent operations in the
|
|
5
|
-
|
|
4
|
+
File writing tool for agent operations in the TunaCode application.
|
|
5
|
+
Provides safe file creation with conflict detection and encoding handling.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import os
|
tunacode/ui/completers.py
CHANGED
|
@@ -44,7 +44,7 @@ class CommandCompleter(Completer):
|
|
|
44
44
|
command_names = self.command_registry.get_command_names()
|
|
45
45
|
else:
|
|
46
46
|
# Fallback list of commands
|
|
47
|
-
command_names = ['/help', '/clear', '/dump', '/yolo',
|
|
47
|
+
command_names = ['/help', '/clear', '/dump', '/yolo',
|
|
48
48
|
'/branch', '/compact', '/model']
|
|
49
49
|
|
|
50
50
|
# Get the partial command (without /)
|
tunacode/ui/console.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
"""Main console coordination module for
|
|
1
|
+
"""Main console coordination module for TunaCode UI.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
backward compatibility while organizing code into focused modules.
|
|
3
|
+
Provides high-level console functions and coordinates between different UI components.
|
|
5
4
|
"""
|
|
6
5
|
|
|
7
6
|
from rich.console import Console as RichConsole
|
tunacode/ui/constants.py
CHANGED
tunacode/ui/decorators.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Module:
|
|
2
|
+
Module: tunacode.ui.decorators
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Decorators for UI functions, particularly for creating sync wrappers of async functions.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import asyncio
|
tunacode/ui/input.py
CHANGED
tunacode/ui/keybindings.py
CHANGED
tunacode/ui/output.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Output and display functions for
|
|
1
|
+
"""Output and display functions for TunaCode UI."""
|
|
2
2
|
|
|
3
3
|
from prompt_toolkit.application import run_in_terminal
|
|
4
4
|
from rich.console import Console
|
|
@@ -16,9 +16,16 @@ from .decorators import create_sync_wrapper
|
|
|
16
16
|
console = Console()
|
|
17
17
|
colors = DotDict(UI_COLORS)
|
|
18
18
|
|
|
19
|
-
BANNER = """[bold
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
BANNER = """[bold cyan]
|
|
20
|
+
████████╗██╗ ██╗███╗ ██╗ █████╗ ██████╗ ██████╗ ██████╗ ███████╗
|
|
21
|
+
╚══██╔══╝██║ ██║████╗ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗██╔════╝
|
|
22
|
+
██║ ██║ ██║██╔██╗ ██║███████║██║ ██║ ██║██║ ██║█████╗
|
|
23
|
+
██║ ██║ ██║██║╚██╗██║██╔══██║██║ ██║ ██║██║ ██║██╔══╝
|
|
24
|
+
██║ ╚██████╔╝██║ ╚████║██║ ██║╚██████╗╚██████╔╝██████╔╝███████╗
|
|
25
|
+
╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
|
|
26
|
+
[/bold cyan]
|
|
27
|
+
|
|
28
|
+
● Caution: This tool can modify your codebase - always use git branches"""
|
|
22
29
|
|
|
23
30
|
|
|
24
31
|
@create_sync_wrapper
|