tunacode-cli 0.0.11__py3-none-any.whl → 0.0.12__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.

Files changed (43) hide show
  1. tunacode/cli/commands.py +3 -4
  2. tunacode/cli/main.py +5 -4
  3. tunacode/cli/repl.py +2 -13
  4. tunacode/cli/textual_app.py +423 -0
  5. tunacode/cli/textual_bridge.py +158 -0
  6. tunacode/configuration/defaults.py +3 -4
  7. tunacode/configuration/models.py +3 -3
  8. tunacode/configuration/settings.py +2 -2
  9. tunacode/constants.py +2 -10
  10. tunacode/core/agents/main.py +3 -3
  11. tunacode/core/setup/__init__.py +0 -2
  12. tunacode/core/setup/agent_setup.py +3 -3
  13. tunacode/core/setup/base.py +3 -3
  14. tunacode/core/setup/coordinator.py +2 -2
  15. tunacode/core/setup/environment_setup.py +2 -2
  16. tunacode/core/setup/git_safety_setup.py +1 -2
  17. tunacode/core/state.py +3 -3
  18. tunacode/setup.py +2 -3
  19. tunacode/tools/base.py +4 -43
  20. tunacode/tools/read_file.py +2 -2
  21. tunacode/tools/run_command.py +2 -2
  22. tunacode/tools/update_file.py +3 -3
  23. tunacode/tools/write_file.py +3 -3
  24. tunacode/ui/completers.py +1 -1
  25. tunacode/ui/console.py +2 -3
  26. tunacode/ui/constants.py +1 -1
  27. tunacode/ui/decorators.py +2 -2
  28. tunacode/ui/input.py +1 -1
  29. tunacode/ui/keybindings.py +1 -1
  30. tunacode/ui/output.py +11 -4
  31. tunacode/ui/panels.py +3 -4
  32. tunacode/ui/prompt_manager.py +1 -1
  33. tunacode/ui/validators.py +1 -1
  34. tunacode/utils/diff_utils.py +3 -3
  35. {tunacode_cli-0.0.11.dist-info → tunacode_cli-0.0.12.dist-info}/METADATA +94 -40
  36. tunacode_cli-0.0.12.dist-info/RECORD +65 -0
  37. tunacode/core/setup/undo_setup.py +0 -33
  38. tunacode/services/undo_service.py +0 -244
  39. tunacode_cli-0.0.11.dist-info/RECORD +0 -65
  40. {tunacode_cli-0.0.11.dist-info → tunacode_cli-0.0.12.dist-info}/WHEEL +0 -0
  41. {tunacode_cli-0.0.11.dist-info → tunacode_cli-0.0.12.dist-info}/entry_points.txt +0 -0
  42. {tunacode_cli-0.0.11.dist-info → tunacode_cli-0.0.12.dist-info}/licenses/LICENSE +0 -0
  43. {tunacode_cli-0.0.11.dist-info → tunacode_cli-0.0.12.dist-info}/top_level.txt +0 -0
tunacode/cli/commands.py CHANGED
@@ -1,4 +1,4 @@
1
- """Command system for Sidekick CLI."""
1
+ """Command system for TunaCode CLI."""
2
2
 
3
3
  from abc import ABC, abstractmethod
4
4
  from dataclasses import dataclass
@@ -248,14 +248,13 @@ class BranchCommand(SimpleCommand):
248
248
 
249
249
  async def execute(self, args: List[str], context: CommandContext) -> None:
250
250
  import subprocess
251
-
252
- from ..services.undo_service import is_in_git_project
251
+ import os
253
252
 
254
253
  if not args:
255
254
  await ui.error("Usage: /branch <branch-name>")
256
255
  return
257
256
 
258
- if not is_in_git_project():
257
+ if not os.path.exists(".git"):
259
258
  await ui.error("Not a git repository")
260
259
  return
261
260
 
tunacode/cli/main.py CHANGED
@@ -1,8 +1,7 @@
1
1
  """
2
- Module: sidekick.cli.main
2
+ Module: tunacode.cli.main
3
3
 
4
- CLI entry point and main command handling for the Sidekick application.
5
- Manages application startup, version checking, and REPL initialization.
4
+ Enhanced CLI entry point with better styling while staying CLI-based.
6
5
  """
7
6
 
8
7
  import asyncio
@@ -17,7 +16,7 @@ from tunacode.ui import console as ui
17
16
  from tunacode.utils.system import check_for_updates
18
17
 
19
18
  app_settings = ApplicationSettings()
20
- app = typer.Typer(help=app_settings.name)
19
+ app = typer.Typer(help="🐟 TunaCode - Your AI-powered development assistant")
21
20
  state_manager = StateManager()
22
21
 
23
22
 
@@ -29,6 +28,8 @@ def main(
29
28
  model: str = typer.Option(None, "--model", help="Default model to use (e.g., openai/gpt-4)"),
30
29
  key: str = typer.Option(None, "--key", help="API key for the provider"),
31
30
  ):
31
+ """🚀 Start TunaCode - Your AI-powered development assistant"""
32
+
32
33
  if version:
33
34
  asyncio.run(ui.version())
34
35
  return
tunacode/cli/repl.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
- Module: sidekick.cli.repl
2
+ Module: tunacode.cli.repl
3
3
 
4
- Interactive REPL (Read-Eval-Print Loop) implementation for Sidekick.
4
+ Interactive REPL (Read-Eval-Print Loop) implementation for TunaCode.
5
5
  Handles user input, command processing, and agent interaction in an interactive shell.
6
6
  """
7
7
 
@@ -207,18 +207,7 @@ async def repl(state_manager: StateManager):
207
207
  action = None
208
208
 
209
209
  # Professional startup information
210
- await ui.info("TunaCode v0.1 - Beta Release")
211
- await ui.muted("• Caution: This tool can modify your codebase")
212
210
  await ui.muted(f"• Model: {state_manager.session.current_model}")
213
- await ui.line()
214
-
215
- # Important safety warning
216
- await ui.warning("⚠️ IMPORTANT: The /undo command has been removed for safety reasons")
217
- await ui.muted("• Always use git branches before making major changes")
218
- await ui.muted("• Use '/branch <name>' to create a new branch for experiments")
219
- await ui.muted("• Commit your work frequently to preserve changes")
220
- await ui.line()
221
-
222
211
  await ui.success("Ready to assist with your development")
223
212
  await ui.line()
224
213
 
@@ -0,0 +1,423 @@
1
+ """
2
+ Modern Textual-based TUI for TunaCode.
3
+
4
+ Provides a rich, interactive terminal user interface with:
5
+ - Split-pane layout with chat history and input
6
+ - Sidebar with model info and commands
7
+ - Modern dialog boxes for tool confirmations
8
+ - Real-time status updates
9
+ """
10
+
11
+ import asyncio
12
+ from typing import Optional
13
+
14
+ from rich.markdown import Markdown
15
+ from textual import on, work
16
+ from textual.app import App, ComposeResult
17
+ from textual.binding import Binding
18
+ from textual.containers import Container, Horizontal, Vertical, VerticalScroll
19
+ from textual.message import Message
20
+ from textual.reactive import reactive
21
+ from textual.widgets import (
22
+ Button, Footer, Header, Input, Label, Markdown as MarkdownWidget,
23
+ Static, TextArea, TabbedContent, TabPane
24
+ )
25
+
26
+ from tunacode.configuration.settings import ApplicationSettings
27
+ from tunacode.core.state import StateManager
28
+ from tunacode.setup import setup
29
+ from tunacode.utils.system import check_for_updates
30
+
31
+
32
+ class ChatMessage(Static):
33
+ """A single chat message widget."""
34
+
35
+ def __init__(self, sender: str, content: str, message_type: str = "user"):
36
+ super().__init__()
37
+ self.sender = sender
38
+ self.content = content
39
+ self.message_type = message_type
40
+
41
+ def compose(self) -> ComposeResult:
42
+ """Compose the chat message."""
43
+ if self.message_type == "user":
44
+ yield Static(f"[bold cyan]❯ You[/bold cyan]\n{self.content}", classes="user-message")
45
+ elif self.message_type == "agent":
46
+ yield Static(f"[bold green]🤖 TunaCode[/bold green]\n{self.content}", classes="agent-message")
47
+ elif self.message_type == "system":
48
+ yield Static(f"[bold yellow]⚠️ System[/bold yellow]\n{self.content}", classes="system-message")
49
+ elif self.message_type == "tool":
50
+ yield Static(f"[bold magenta]🔧 Tool[/bold magenta]\n{self.content}", classes="tool-message")
51
+
52
+
53
+ class Sidebar(Container):
54
+ """Sidebar with model info and commands."""
55
+
56
+ def __init__(self, state_manager: StateManager):
57
+ super().__init__()
58
+ self.state_manager = state_manager
59
+
60
+ def compose(self) -> ComposeResult:
61
+ """Compose the sidebar."""
62
+ yield Static("[bold]TunaCode[/bold]", classes="sidebar-title")
63
+ yield Static(f"Model: {self.state_manager.session.current_model}", id="current-model")
64
+ yield Static("", classes="spacer")
65
+
66
+ yield Static("[bold]Commands[/bold]", classes="section-title")
67
+ yield Static("/help - Show help", classes="command-item")
68
+ yield Static("/clear - Clear chat", classes="command-item")
69
+ yield Static("/model - Switch model", classes="command-item")
70
+ yield Static("/yolo - Toggle confirmations", classes="command-item")
71
+ yield Static("", classes="spacer")
72
+
73
+ yield Static("[bold]Status[/bold]", classes="section-title")
74
+ yield Static("● Ready", id="status", classes="status-ready")
75
+
76
+
77
+ class ChatHistory(VerticalScroll):
78
+ """Scrollable chat history container."""
79
+
80
+ def add_message(self, sender: str, content: str, message_type: str = "user") -> None:
81
+ """Add a new message to the chat history."""
82
+ message = ChatMessage(sender, content, message_type)
83
+ self.mount(message)
84
+ self.scroll_end(animate=True)
85
+
86
+
87
+ class InputArea(Container):
88
+ """Input area with text area and send button."""
89
+
90
+ class SendMessage(Message):
91
+ """Message sent when user submits input."""
92
+
93
+ def __init__(self, content: str) -> None:
94
+ self.content = content
95
+ super().__init__()
96
+
97
+ def compose(self) -> ComposeResult:
98
+ """Compose the input area."""
99
+ with Horizontal():
100
+ yield TextArea(id="message-input")
101
+ yield Button("Send", id="send-button", variant="primary")
102
+
103
+ @on(Button.Pressed, "#send-button")
104
+ def send_message(self) -> None:
105
+ """Send the current message."""
106
+ text_area = self.query_one("#message-input", TextArea)
107
+ content = text_area.text.strip()
108
+ if content:
109
+ self.post_message(self.SendMessage(content))
110
+ text_area.clear()
111
+
112
+ def on_key(self, event) -> None:
113
+ """Handle key events."""
114
+ if event.key == "ctrl+enter":
115
+ self.send_message()
116
+
117
+
118
+ class TunaCodeApp(App):
119
+ """Main TunaCode Textual application."""
120
+
121
+ CSS = """
122
+ Sidebar {
123
+ width: 30;
124
+ background: $surface;
125
+ border: thick $primary;
126
+ padding: 1;
127
+ }
128
+
129
+ .sidebar-title {
130
+ text-align: center;
131
+ color: $primary;
132
+ margin-bottom: 1;
133
+ }
134
+
135
+ .section-title {
136
+ color: $accent;
137
+ margin: 1 0;
138
+ }
139
+
140
+ .command-item {
141
+ color: $text-muted;
142
+ margin-left: 1;
143
+ }
144
+
145
+ .status-ready {
146
+ color: $success;
147
+ }
148
+
149
+ .status-busy {
150
+ color: $warning;
151
+ }
152
+
153
+ .status-error {
154
+ color: $error;
155
+ }
156
+
157
+ ChatHistory {
158
+ border: thick $primary;
159
+ padding: 1;
160
+ height: 1fr;
161
+ }
162
+
163
+ .user-message {
164
+ background: $surface;
165
+ border-left: thick $primary;
166
+ padding: 1;
167
+ margin: 1 0;
168
+ }
169
+
170
+ .agent-message {
171
+ background: $surface;
172
+ border-left: thick $success;
173
+ padding: 1;
174
+ margin: 1 0;
175
+ }
176
+
177
+ .system-message {
178
+ background: $surface;
179
+ border-left: thick $warning;
180
+ padding: 1;
181
+ margin: 1 0;
182
+ }
183
+
184
+ .tool-message {
185
+ background: $surface;
186
+ border-left: thick $accent;
187
+ padding: 1;
188
+ margin: 1 0;
189
+ }
190
+
191
+ InputArea {
192
+ height: 5;
193
+ padding: 1;
194
+ }
195
+
196
+ #message-input {
197
+ height: 3;
198
+ }
199
+
200
+ #send-button {
201
+ width: 10;
202
+ margin-left: 1;
203
+ }
204
+ """
205
+
206
+ BINDINGS = [
207
+ Binding("ctrl+c", "quit", "Quit"),
208
+ Binding("ctrl+l", "clear_chat", "Clear"),
209
+ Binding("f1", "help", "Help"),
210
+ Binding("f2", "model_info", "Model"),
211
+ ]
212
+
213
+ def __init__(self, state_manager: StateManager):
214
+ super().__init__()
215
+ self.state_manager = state_manager
216
+ self.chat_history: Optional[ChatHistory] = None
217
+ self.sidebar: Optional[Sidebar] = None
218
+ self.input_area: Optional[InputArea] = None
219
+
220
+ def compose(self) -> ComposeResult:
221
+ """Compose the main application layout."""
222
+ yield Header()
223
+
224
+ with Horizontal():
225
+ self.sidebar = Sidebar(self.state_manager)
226
+ yield self.sidebar
227
+
228
+ with Vertical():
229
+ self.chat_history = ChatHistory()
230
+ yield self.chat_history
231
+
232
+ self.input_area = InputArea()
233
+ yield self.input_area
234
+
235
+ yield Footer()
236
+
237
+ def on_mount(self) -> None:
238
+ """Called when the app is mounted."""
239
+ # Add welcome messages
240
+ self.chat_history.add_message(
241
+ "System",
242
+ "Welcome to TunaCode v0.11 - Your AI-powered development assistant",
243
+ "system"
244
+ )
245
+ self.chat_history.add_message(
246
+ "System",
247
+ f"Current model: {self.state_manager.session.current_model}",
248
+ "system"
249
+ )
250
+ self.chat_history.add_message(
251
+ "System",
252
+ "⚠️ IMPORTANT: Always use git branches before making major changes\nType '/help' for available commands",
253
+ "system"
254
+ )
255
+
256
+ @on(InputArea.SendMessage)
257
+ async def handle_message(self, message: InputArea.SendMessage) -> None:
258
+ """Handle incoming messages from the input area."""
259
+ content = message.content
260
+
261
+ # Add user message to chat
262
+ self.chat_history.add_message("You", content, "user")
263
+
264
+ # Update status
265
+ status_widget = self.sidebar.query_one("#status", Static)
266
+ status_widget.update("● Processing...")
267
+ status_widget.classes = "status-busy"
268
+
269
+ if content.startswith("/"):
270
+ await self.handle_command(content)
271
+ else:
272
+ await self.handle_user_input(content)
273
+
274
+ # Reset status
275
+ status_widget.update("● Ready")
276
+ status_widget.classes = "status-ready"
277
+
278
+ async def handle_command(self, command: str) -> None:
279
+ """Handle slash commands."""
280
+ if command == "/help":
281
+ help_text = """Available Commands:
282
+
283
+ /help - Show this help message
284
+ /clear - Clear chat history
285
+ /model - Show current model info
286
+ /yolo - Toggle confirmation prompts
287
+ /quit - Exit the application
288
+
289
+ Keyboard Shortcuts:
290
+ Ctrl+C - Quit
291
+ Ctrl+L - Clear chat
292
+ F1 - Help
293
+ F2 - Model info
294
+ Ctrl+Enter - Send message"""
295
+ self.chat_history.add_message("System", help_text, "system")
296
+
297
+ elif command == "/clear":
298
+ await self.action_clear_chat()
299
+
300
+ elif command == "/model":
301
+ model_info = f"Current model: {self.state_manager.session.current_model}"
302
+ self.chat_history.add_message("System", model_info, "system")
303
+
304
+ elif command == "/yolo":
305
+ # Toggle yolo mode
306
+ current_state = getattr(self.state_manager.session, 'yolo_mode', False)
307
+ self.state_manager.session.yolo_mode = not current_state
308
+ new_state = "enabled" if not current_state else "disabled"
309
+ self.chat_history.add_message("System", f"Confirmation prompts {new_state}", "system")
310
+
311
+ elif command == "/quit":
312
+ await self.action_quit()
313
+
314
+ else:
315
+ self.chat_history.add_message("System", f"Unknown command: {command}", "system")
316
+
317
+ async def handle_user_input(self, text: str) -> None:
318
+ """Handle regular user input."""
319
+ try:
320
+ # Use the bridge to process the input
321
+ if not hasattr(self, '_bridge'):
322
+ from tunacode.cli.textual_bridge import TextualAgentBridge
323
+ self._bridge = TextualAgentBridge(self.state_manager, self._bridge_message_callback)
324
+
325
+ # Process the request
326
+ response = await self._bridge.process_user_input(text)
327
+
328
+ # Add the agent's response to chat
329
+ self.chat_history.add_message("TunaCode", response, "agent")
330
+
331
+ except Exception as e:
332
+ self.chat_history.add_message("System", f"Error: {str(e)}", "system")
333
+
334
+ async def _bridge_message_callback(self, message_type: str, content: str) -> None:
335
+ """Callback for bridge to send messages to the UI."""
336
+ self.chat_history.add_message("System", content, message_type)
337
+
338
+ def action_clear_chat(self) -> None:
339
+ """Clear the chat history."""
340
+ self.chat_history.remove_children()
341
+ self.chat_history.add_message("System", "Chat cleared", "system")
342
+
343
+ def action_help(self) -> None:
344
+ """Show help information."""
345
+ self.handle_command("/help")
346
+
347
+ def action_model_info(self) -> None:
348
+ """Show model information."""
349
+ self.handle_command("/model")
350
+
351
+
352
+ async def run_textual_app(state_manager: StateManager) -> None:
353
+ """Run the Textual application."""
354
+ app = TunaCodeApp(state_manager)
355
+ await app.run_async()
356
+
357
+
358
+ def main():
359
+ """Main entry point for the Textual app."""
360
+ import sys
361
+
362
+ # Handle command line arguments
363
+ version_flag = "--version" in sys.argv or "-v" in sys.argv
364
+ if version_flag:
365
+ from tunacode.constants import APP_VERSION
366
+ print(f"TunaCode v{APP_VERSION}")
367
+ return
368
+
369
+ # Initialize state manager
370
+ state_manager = StateManager()
371
+ app_settings = ApplicationSettings()
372
+
373
+ # Show banner
374
+ print("🐟 TunaCode - Modern AI Development Assistant")
375
+ print("=" * 50)
376
+
377
+ # Check for updates
378
+ has_update, latest_version = check_for_updates()
379
+ if has_update:
380
+ print(f"📦 Update available: v{latest_version}")
381
+ print("Run: pip install --upgrade tunacode-cli")
382
+ print()
383
+
384
+ # Parse CLI arguments for configuration
385
+ cli_config = {}
386
+ args = sys.argv[1:]
387
+ i = 0
388
+ while i < len(args):
389
+ if args[i] == "--model" and i + 1 < len(args):
390
+ cli_config["model"] = args[i + 1]
391
+ i += 2
392
+ elif args[i] == "--key" and i + 1 < len(args):
393
+ cli_config["key"] = args[i + 1]
394
+ i += 2
395
+ elif args[i] == "--baseurl" and i + 1 < len(args):
396
+ cli_config["baseurl"] = args[i + 1]
397
+ i += 2
398
+ elif args[i] == "--setup":
399
+ cli_config["setup"] = True
400
+ i += 1
401
+ else:
402
+ i += 1
403
+
404
+ async def run_app():
405
+ try:
406
+ # Run setup
407
+ run_setup = cli_config.get("setup", False)
408
+ await setup(run_setup, state_manager, cli_config)
409
+
410
+ # Run the Textual app
411
+ await run_textual_app(state_manager)
412
+
413
+ except KeyboardInterrupt:
414
+ print("\n👋 Goodbye!")
415
+ except Exception as e:
416
+ print(f"❌ Error: {e}")
417
+
418
+ # Run the async app
419
+ asyncio.run(run_app())
420
+
421
+
422
+ if __name__ == "__main__":
423
+ main()
@@ -0,0 +1,158 @@
1
+ """
2
+ Bridge module to integrate existing TunaCode agent logic with Textual UI.
3
+
4
+ This module adapts the existing REPL and agent processing logic to work
5
+ with the new Textual-based interface while maintaining compatibility.
6
+ """
7
+
8
+ import asyncio
9
+ from typing import Callable, Optional
10
+ from asyncio.exceptions import CancelledError
11
+
12
+ from pydantic_ai.exceptions import UnexpectedModelBehavior
13
+
14
+ from tunacode.core.agents import main as agent
15
+ from tunacode.core.agents.main import patch_tool_messages
16
+ from tunacode.core.tool_handler import ToolHandler
17
+ from tunacode.exceptions import AgentError, UserAbortError, ValidationError
18
+ from tunacode.types import StateManager
19
+ from tunacode.cli.commands import CommandRegistry
20
+ from tunacode.cli.repl import _parse_args
21
+ from tunacode.ui.tool_ui import ToolUI
22
+
23
+
24
+ class TextualAgentBridge:
25
+ """Bridge between Textual UI and existing agent logic."""
26
+
27
+ def __init__(self, state_manager: StateManager, message_callback: Callable):
28
+ self.state_manager = state_manager
29
+ self.message_callback = message_callback
30
+ self.tool_ui = ToolUI()
31
+ self.command_registry = CommandRegistry()
32
+ self.command_registry.register_all_default_commands()
33
+
34
+ async def process_user_input(self, text: str) -> str:
35
+ """Process user input and return the agent's response."""
36
+ if text.startswith("/"):
37
+ return await self._handle_command(text)
38
+ else:
39
+ return await self._process_agent_request(text)
40
+
41
+ async def _handle_command(self, command: str) -> str:
42
+ """Handle slash commands."""
43
+ try:
44
+ from tunacode.types import CommandContext
45
+
46
+ # Create command context
47
+ context = CommandContext(
48
+ state_manager=self.state_manager,
49
+ process_request=self._process_agent_request
50
+ )
51
+
52
+ # Set the process_request callback for commands that need it
53
+ self.command_registry.set_process_request_callback(self._process_agent_request)
54
+
55
+ # Execute the command
56
+ result = await self.command_registry.execute(command, context)
57
+
58
+ if result == "restart":
59
+ return "Application restart requested."
60
+ elif result:
61
+ return str(result)
62
+ else:
63
+ return f"Command '{command}' executed successfully."
64
+
65
+ except ValidationError as e:
66
+ return f"Error: {str(e)}"
67
+ except Exception as e:
68
+ return f"Command error: {str(e)}"
69
+
70
+ async def _process_agent_request(self, text: str) -> str:
71
+ """Process input using the agent."""
72
+ try:
73
+ # Expand @file references before sending to the agent
74
+ try:
75
+ from tunacode.utils.text_utils import expand_file_refs
76
+ text = expand_file_refs(text)
77
+ except ValueError as e:
78
+ return f"Error: {str(e)}"
79
+
80
+ # Notify UI about processing start
81
+ await self.message_callback("tool", "Processing request...")
82
+
83
+ # Create a tool callback that integrates with Textual
84
+ def tool_callback_with_state(part, node):
85
+ return self._tool_handler(part, node)
86
+
87
+ # Get or create agent instance
88
+ instance = agent.get_or_create_agent(
89
+ self.state_manager.session.current_model,
90
+ self.state_manager
91
+ )
92
+
93
+ # Process the request
94
+ async with instance.run_mcp_servers():
95
+ res = await agent.process_request(
96
+ self.state_manager.session.current_model,
97
+ text,
98
+ self.state_manager,
99
+ tool_callback=tool_callback_with_state,
100
+ )
101
+
102
+ if res and res.result:
103
+ return res.result.output
104
+ else:
105
+ return "Request processed (no output)."
106
+
107
+ except CancelledError:
108
+ return "Request cancelled."
109
+ except UserAbortError:
110
+ return "Operation aborted."
111
+ except UnexpectedModelBehavior as e:
112
+ error_message = str(e)
113
+ patch_tool_messages(error_message, self.state_manager)
114
+ return f"Model error: {error_message}"
115
+ except Exception as e:
116
+ # Wrap unexpected exceptions in AgentError for better tracking
117
+ agent_error = AgentError(f"Agent processing failed: {str(e)}")
118
+ agent_error.__cause__ = e
119
+ return f"Error: {str(e)}"
120
+
121
+ async def _tool_handler(self, part, node):
122
+ """Handle tool execution with Textual UI integration."""
123
+ await self.message_callback("tool", f"Tool: {part.tool_name}")
124
+
125
+ try:
126
+ # Create tool handler with state
127
+ tool_handler = ToolHandler(self.state_manager)
128
+ args = _parse_args(part.args)
129
+
130
+ # Check if confirmation is needed
131
+ if tool_handler.should_confirm(part.tool_name):
132
+ # Create confirmation request
133
+ request = tool_handler.create_confirmation_request(part.tool_name, args)
134
+
135
+ # For now, show a simple confirmation in the UI
136
+ # In a full implementation, this would show a proper modal dialog
137
+ await self.message_callback("system", f"Tool confirmation: {part.tool_name} with args: {args}")
138
+
139
+ # For demo purposes, auto-approve (in production, this should be interactive)
140
+ if not tool_handler.process_confirmation(True, part.tool_name):
141
+ raise UserAbortError("User aborted tool execution.")
142
+
143
+ except UserAbortError:
144
+ patch_tool_messages("Operation aborted by user.", self.state_manager)
145
+ raise
146
+
147
+
148
+ class TextualToolConfirmation:
149
+ """Handle tool confirmations in Textual UI."""
150
+
151
+ def __init__(self, app_instance):
152
+ self.app = app_instance
153
+
154
+ async def show_confirmation(self, tool_name: str, args: dict) -> bool:
155
+ """Show tool confirmation dialog and return user's choice."""
156
+ # This would show a modal dialog in the Textual app
157
+ # For now, return True (auto-approve)
158
+ return True
@@ -1,9 +1,8 @@
1
1
  """
2
- Module: sidekick.configuration.defaults
2
+ Module: tunacode.configuration.defaults
3
3
 
4
- Default configuration values for the Sidekick CLI.
5
- Provides baseline settings for user configuration including API keys,
6
- tool settings, and MCP servers.
4
+ Default configuration values for the TunaCode CLI.
5
+ Provides sensible defaults for user configuration and environment variables.
7
6
  """
8
7
 
9
8
  from tunacode.constants import GUIDE_FILE_NAME, TOOL_READ_FILE
@@ -1,8 +1,8 @@
1
1
  """
2
- Module: sidekick.configuration.models
2
+ Module: tunacode.configuration.models
3
3
 
4
- Configuration model definitions and model registry for AI models.
5
- Manages available AI models, their configurations, and pricing information.
4
+ Configuration data models and model registry for TunaCode CLI.
5
+ Defines available AI models and their configurations.
6
6
  """
7
7
 
8
8
  from tunacode.types import ModelConfig, ModelName, ModelPricing