tunacode-cli 0.0.17__py3-none-any.whl → 0.0.19__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 (47) hide show
  1. tunacode/cli/commands.py +73 -41
  2. tunacode/cli/main.py +29 -26
  3. tunacode/cli/repl.py +91 -37
  4. tunacode/cli/textual_app.py +69 -66
  5. tunacode/cli/textual_bridge.py +33 -32
  6. tunacode/configuration/settings.py +2 -9
  7. tunacode/constants.py +2 -4
  8. tunacode/context.py +1 -1
  9. tunacode/core/agents/__init__.py +12 -0
  10. tunacode/core/agents/main.py +89 -63
  11. tunacode/core/agents/orchestrator.py +99 -0
  12. tunacode/core/agents/planner_schema.py +9 -0
  13. tunacode/core/agents/readonly.py +51 -0
  14. tunacode/core/background/__init__.py +0 -0
  15. tunacode/core/background/manager.py +36 -0
  16. tunacode/core/llm/__init__.py +0 -0
  17. tunacode/core/llm/planner.py +63 -0
  18. tunacode/core/setup/config_setup.py +79 -44
  19. tunacode/core/setup/coordinator.py +20 -13
  20. tunacode/core/setup/git_safety_setup.py +35 -49
  21. tunacode/core/state.py +2 -9
  22. tunacode/exceptions.py +0 -2
  23. tunacode/prompts/system.txt +179 -69
  24. tunacode/tools/__init__.py +10 -1
  25. tunacode/tools/base.py +1 -1
  26. tunacode/tools/bash.py +5 -5
  27. tunacode/tools/grep.py +210 -250
  28. tunacode/tools/read_file.py +2 -8
  29. tunacode/tools/run_command.py +4 -11
  30. tunacode/tools/update_file.py +2 -6
  31. tunacode/ui/completers.py +32 -31
  32. tunacode/ui/console.py +3 -3
  33. tunacode/ui/input.py +8 -5
  34. tunacode/ui/keybindings.py +1 -3
  35. tunacode/ui/lexers.py +16 -16
  36. tunacode/ui/output.py +2 -2
  37. tunacode/ui/panels.py +8 -8
  38. tunacode/ui/prompt_manager.py +19 -7
  39. tunacode/utils/import_cache.py +11 -0
  40. tunacode/utils/user_configuration.py +24 -2
  41. {tunacode_cli-0.0.17.dist-info → tunacode_cli-0.0.19.dist-info}/METADATA +68 -11
  42. tunacode_cli-0.0.19.dist-info/RECORD +75 -0
  43. tunacode_cli-0.0.17.dist-info/RECORD +0 -67
  44. {tunacode_cli-0.0.17.dist-info → tunacode_cli-0.0.19.dist-info}/WHEEL +0 -0
  45. {tunacode_cli-0.0.17.dist-info → tunacode_cli-0.0.19.dist-info}/entry_points.txt +0 -0
  46. {tunacode_cli-0.0.17.dist-info → tunacode_cli-0.0.19.dist-info}/licenses/LICENSE +0 -0
  47. {tunacode_cli-0.0.17.dist-info → tunacode_cli-0.0.19.dist-info}/top_level.txt +0 -0
@@ -7,14 +7,8 @@ Provides safe file reading with size limits and proper error handling.
7
7
 
8
8
  import os
9
9
 
10
- from tunacode.constants import (
11
- ERROR_FILE_DECODE,
12
- ERROR_FILE_DECODE_DETAILS,
13
- ERROR_FILE_NOT_FOUND,
14
- ERROR_FILE_TOO_LARGE,
15
- MAX_FILE_SIZE,
16
- MSG_FILE_SIZE_LIMIT,
17
- )
10
+ from tunacode.constants import (ERROR_FILE_DECODE, ERROR_FILE_DECODE_DETAILS, ERROR_FILE_NOT_FOUND,
11
+ ERROR_FILE_TOO_LARGE, MAX_FILE_SIZE, MSG_FILE_SIZE_LIMIT)
18
12
  from tunacode.exceptions import ToolExecutionError
19
13
  from tunacode.tools.base import FileBasedTool
20
14
  from tunacode.types import ToolResult
@@ -7,17 +7,10 @@ Provides controlled shell command execution with output capture and truncation.
7
7
 
8
8
  import subprocess
9
9
 
10
- from tunacode.constants import (
11
- CMD_OUTPUT_FORMAT,
12
- CMD_OUTPUT_NO_ERRORS,
13
- CMD_OUTPUT_NO_OUTPUT,
14
- CMD_OUTPUT_TRUNCATED,
15
- COMMAND_OUTPUT_END_SIZE,
16
- COMMAND_OUTPUT_START_INDEX,
17
- COMMAND_OUTPUT_THRESHOLD,
18
- ERROR_COMMAND_EXECUTION,
19
- MAX_COMMAND_OUTPUT,
20
- )
10
+ from tunacode.constants import (CMD_OUTPUT_FORMAT, CMD_OUTPUT_NO_ERRORS, CMD_OUTPUT_NO_OUTPUT,
11
+ CMD_OUTPUT_TRUNCATED, COMMAND_OUTPUT_END_SIZE,
12
+ COMMAND_OUTPUT_START_INDEX, COMMAND_OUTPUT_THRESHOLD,
13
+ ERROR_COMMAND_EXECUTION, MAX_COMMAND_OUTPUT)
21
14
  from tunacode.exceptions import ToolExecutionError
22
15
  from tunacode.tools.base import BaseTool
23
16
  from tunacode.types import ToolResult
@@ -21,9 +21,7 @@ class UpdateFileTool(FileBasedTool):
21
21
  def tool_name(self) -> str:
22
22
  return "Update"
23
23
 
24
- async def _execute(
25
- self, filepath: str, target: str, patch: str
26
- ) -> ToolResult:
24
+ async def _execute(self, filepath: str, target: str, patch: str) -> ToolResult:
27
25
  """Update an existing file by replacing a target text block with a patch.
28
26
 
29
27
  Args:
@@ -73,9 +71,7 @@ class UpdateFileTool(FileBasedTool):
73
71
 
74
72
  return f"File '{filepath}' updated successfully."
75
73
 
76
- def _format_args(
77
- self, filepath: str, target: str = None, patch: str = None
78
- ) -> str:
74
+ def _format_args(self, filepath: str, target: str = None, patch: str = None) -> str:
79
75
  """Format arguments, truncating target and patch for display."""
80
76
  args = [repr(filepath)]
81
77
 
tunacode/ui/completers.py CHANGED
@@ -11,53 +11,52 @@ from ..cli.commands import CommandRegistry
11
11
 
12
12
  class CommandCompleter(Completer):
13
13
  """Completer for slash commands."""
14
-
14
+
15
15
  def __init__(self, command_registry: Optional[CommandRegistry] = None):
16
16
  self.command_registry = command_registry
17
-
17
+
18
18
  def get_completions(
19
19
  self, document: Document, complete_event: CompleteEvent
20
20
  ) -> Iterable[Completion]:
21
21
  """Get completions for slash commands."""
22
22
  # Get the text before cursor
23
23
  text = document.text_before_cursor
24
-
24
+
25
25
  # Check if we're at the start of a line or after whitespace
26
- if text and not text.isspace() and text[-1] != '\n':
26
+ if text and not text.isspace() and text[-1] != "\n":
27
27
  # Only complete commands at the start of input or after a newline
28
- last_newline = text.rfind('\n')
29
- line_start = text[last_newline + 1:] if last_newline >= 0 else text
30
-
28
+ last_newline = text.rfind("\n")
29
+ line_start = text[last_newline + 1 :] if last_newline >= 0 else text
30
+
31
31
  # Skip if not at the beginning of a line
32
- if line_start and not line_start.startswith('/'):
32
+ if line_start and not line_start.startswith("/"):
33
33
  return
34
-
34
+
35
35
  # Get the word before cursor
36
36
  word_before_cursor = document.get_word_before_cursor(WORD=True)
37
-
37
+
38
38
  # Only complete if word starts with /
39
- if not word_before_cursor.startswith('/'):
39
+ if not word_before_cursor.startswith("/"):
40
40
  return
41
-
41
+
42
42
  # Get command names from registry
43
43
  if self.command_registry:
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',
48
- '/branch', '/compact', '/model']
49
-
47
+ command_names = ["/help", "/clear", "/dump", "/yolo", "/branch", "/compact", "/model"]
48
+
50
49
  # Get the partial command (without /)
51
50
  partial = word_before_cursor[1:].lower()
52
-
51
+
53
52
  # Yield completions for matching commands
54
53
  for cmd in command_names:
55
- if cmd.startswith('/') and cmd[1:].lower().startswith(partial):
54
+ if cmd.startswith("/") and cmd[1:].lower().startswith(partial):
56
55
  yield Completion(
57
56
  text=cmd,
58
57
  start_position=-len(word_before_cursor),
59
58
  display=cmd,
60
- display_meta='command'
59
+ display_meta="command",
61
60
  )
62
61
 
63
62
 
@@ -70,14 +69,14 @@ class FileReferenceCompleter(Completer):
70
69
  """Get completions for @file references."""
71
70
  # Get the word before cursor
72
71
  word_before_cursor = document.get_word_before_cursor(WORD=True)
73
-
72
+
74
73
  # Check if we're in an @file reference
75
74
  if not word_before_cursor.startswith("@"):
76
75
  return
77
-
76
+
78
77
  # Get the path part after @
79
78
  path_part = word_before_cursor[1:] # Remove @
80
-
79
+
81
80
  # Determine directory and prefix
82
81
  if "/" in path_part:
83
82
  # Path includes directory
@@ -87,18 +86,18 @@ class FileReferenceCompleter(Completer):
87
86
  # Just filename, search in current directory
88
87
  dir_path = "."
89
88
  prefix = path_part
90
-
89
+
91
90
  # Get matching files
92
91
  try:
93
92
  if os.path.exists(dir_path) and os.path.isdir(dir_path):
94
93
  for item in sorted(os.listdir(dir_path)):
95
94
  if item.startswith(prefix):
96
95
  full_path = os.path.join(dir_path, item) if dir_path != "." else item
97
-
96
+
98
97
  # Skip hidden files unless explicitly requested
99
98
  if item.startswith(".") and not prefix.startswith("."):
100
99
  continue
101
-
100
+
102
101
  # Add / for directories
103
102
  if os.path.isdir(full_path):
104
103
  display = item + "/"
@@ -106,15 +105,15 @@ class FileReferenceCompleter(Completer):
106
105
  else:
107
106
  display = item
108
107
  completion = full_path
109
-
108
+
110
109
  # Calculate how much to replace
111
110
  start_position = -len(path_part)
112
-
111
+
113
112
  yield Completion(
114
113
  text=completion,
115
114
  start_position=start_position,
116
115
  display=display,
117
- display_meta="dir" if os.path.isdir(full_path) else "file"
116
+ display_meta="dir" if os.path.isdir(full_path) else "file",
118
117
  )
119
118
  except (OSError, PermissionError):
120
119
  # Silently ignore inaccessible directories
@@ -123,7 +122,9 @@ class FileReferenceCompleter(Completer):
123
122
 
124
123
  def create_completer(command_registry: Optional[CommandRegistry] = None) -> Completer:
125
124
  """Create a merged completer for both commands and file references."""
126
- return merge_completers([
127
- CommandCompleter(command_registry),
128
- FileReferenceCompleter(),
129
- ])
125
+ return merge_completers(
126
+ [
127
+ CommandCompleter(command_registry),
128
+ FileReferenceCompleter(),
129
+ ]
130
+ )
tunacode/ui/console.py CHANGED
@@ -9,8 +9,9 @@ from rich.markdown import Markdown
9
9
  # Import and re-export all functions from specialized modules
10
10
  from .input import formatted_text, input, multiline_input
11
11
  from .keybindings import create_key_bindings
12
- from .output import (banner, clear, info, line, muted, print, show_update_message, spinner, success,
13
- sync_print, update_available, usage, version, warning)
12
+ from .output import (banner, clear, info, line, muted, print, spinner, success, sync_print,
13
+ update_available, usage, version, warning)
14
+ # Patch banner to use sync fast version
14
15
  from .panels import (agent, dump_messages, error, help, models, panel, sync_panel,
15
16
  sync_tool_confirm, tool_confirm)
16
17
  from .prompt_manager import PromptConfig, PromptManager
@@ -46,7 +47,6 @@ __all__ = [
46
47
  "line",
47
48
  "muted",
48
49
  "print",
49
- "show_update_message",
50
50
  "spinner",
51
51
  "success",
52
52
  "sync_print",
tunacode/ui/input.py CHANGED
@@ -72,7 +72,9 @@ async def input(
72
72
  return await manager.get_input(session_key, pretext, config)
73
73
 
74
74
 
75
- async def multiline_input(state_manager: Optional[StateManager] = None, command_registry=None) -> str:
75
+ async def multiline_input(
76
+ state_manager: Optional[StateManager] = None, command_registry=None
77
+ ) -> str:
76
78
  """Get multiline input from the user with @file completion and highlighting."""
77
79
  kb = create_key_bindings()
78
80
  placeholder = formatted_text(
@@ -85,11 +87,12 @@ async def multiline_input(state_manager: Optional[StateManager] = None, command_
85
87
  )
86
88
  )
87
89
  return await input(
88
- "multiline",
89
- key_bindings=kb,
90
- multiline=True,
90
+ "multiline",
91
+ pretext="❯ ", # Default prompt
92
+ key_bindings=kb,
93
+ multiline=True,
91
94
  placeholder=placeholder,
92
95
  completer=create_completer(command_registry),
93
96
  lexer=FileReferenceLexer(),
94
- state_manager=state_manager
97
+ state_manager=state_manager,
95
98
  )
@@ -7,8 +7,6 @@ def create_key_bindings() -> KeyBindings:
7
7
  """Create and configure key bindings for the UI."""
8
8
  kb = KeyBindings()
9
9
 
10
-
11
-
12
10
  @kb.add("enter")
13
11
  def _submit(event):
14
12
  """Submit the current buffer."""
@@ -18,7 +16,7 @@ def create_key_bindings() -> KeyBindings:
18
16
  def _newline(event):
19
17
  """Insert a newline character."""
20
18
  event.current_buffer.insert_text("\n")
21
-
19
+
22
20
  @kb.add("escape", "enter")
23
21
  def _escape_enter(event):
24
22
  """Insert a newline when escape then enter is pressed."""
tunacode/ui/lexers.py CHANGED
@@ -8,39 +8,39 @@ from prompt_toolkit.lexers import Lexer
8
8
 
9
9
  class FileReferenceLexer(Lexer):
10
10
  """Lexer that highlights @file references in light blue."""
11
-
11
+
12
12
  # Pattern to match @file references
13
- FILE_REF_PATTERN = re.compile(r'@([\w./_-]+)')
14
-
13
+ FILE_REF_PATTERN = re.compile(r"@([\w./_-]+)")
14
+
15
15
  def lex_document(self, document):
16
16
  """Return a formatted text list for the given document."""
17
- lines = document.text.split('\n')
18
-
17
+ lines = document.text.split("\n")
18
+
19
19
  def get_line_tokens(line_number):
20
20
  """Get tokens for a specific line."""
21
21
  if line_number >= len(lines):
22
22
  return []
23
-
23
+
24
24
  line = lines[line_number]
25
25
  tokens = []
26
26
  last_end = 0
27
-
27
+
28
28
  # Find all @file references in the line
29
29
  for match in self.FILE_REF_PATTERN.finditer(line):
30
30
  start, end = match.span()
31
-
31
+
32
32
  # Add text before the match
33
33
  if start > last_end:
34
- tokens.append(('', line[last_end:start]))
35
-
34
+ tokens.append(("", line[last_end:start]))
35
+
36
36
  # Add the @file reference with styling
37
- tokens.append(('class:file-reference', match.group(0)))
37
+ tokens.append(("class:file-reference", match.group(0)))
38
38
  last_end = end
39
-
39
+
40
40
  # Add remaining text
41
41
  if last_end < len(line):
42
- tokens.append(('', line[last_end:]))
43
-
42
+ tokens.append(("", line[last_end:]))
43
+
44
44
  return tokens
45
-
46
- return get_line_tokens
45
+
46
+ return get_line_tokens
tunacode/ui/output.py CHANGED
@@ -72,9 +72,9 @@ async def version() -> None:
72
72
 
73
73
  async def banner() -> None:
74
74
  """Display the application banner."""
75
- console.clear()
75
+ await run_in_terminal(lambda: console.clear())
76
76
  banner_padding = Padding(BANNER, (2, 0, 1, 0))
77
- await print(banner_padding)
77
+ await run_in_terminal(lambda: console.print(banner_padding))
78
78
 
79
79
 
80
80
  async def clear() -> None:
tunacode/ui/panels.py CHANGED
@@ -11,10 +11,10 @@ from rich.table import Table
11
11
 
12
12
  from tunacode.configuration.models import ModelRegistry
13
13
  from tunacode.constants import (APP_NAME, CMD_CLEAR, CMD_COMPACT, CMD_DUMP, CMD_EXIT, CMD_HELP,
14
- CMD_MODEL, CMD_YOLO, DESC_CLEAR, DESC_COMPACT, DESC_DUMP,
15
- DESC_EXIT, DESC_HELP, DESC_MODEL, DESC_MODEL_DEFAULT,
16
- DESC_MODEL_SWITCH, DESC_YOLO, PANEL_AVAILABLE_COMMANDS,
17
- PANEL_ERROR, PANEL_MESSAGE_HISTORY, PANEL_MODELS, UI_COLORS)
14
+ CMD_MODEL, CMD_YOLO, DESC_CLEAR, DESC_COMPACT, DESC_DUMP, DESC_EXIT,
15
+ DESC_HELP, DESC_MODEL, DESC_MODEL_DEFAULT, DESC_MODEL_SWITCH,
16
+ DESC_YOLO, PANEL_AVAILABLE_COMMANDS, PANEL_ERROR,
17
+ PANEL_MESSAGE_HISTORY, PANEL_MODELS, UI_COLORS)
18
18
  from tunacode.core.state import StateManager
19
19
  from tunacode.utils.file_utils import DotDict
20
20
 
@@ -39,12 +39,12 @@ async def panel(
39
39
  """Display a rich panel with modern styling."""
40
40
  border_style = border_style or kwargs.get("style") or colors.border
41
41
  panel_obj = Panel(
42
- Padding(text, (0, 1, 0, 1)),
43
- title=f"[bold]{title}[/bold]",
44
- title_align="left",
42
+ Padding(text, (0, 1, 0, 1)),
43
+ title=f"[bold]{title}[/bold]",
44
+ title_align="left",
45
45
  border_style=border_style,
46
46
  padding=(0, 1),
47
- box=ROUNDED # Use ROUNDED box style
47
+ box=ROUNDED, # Use ROUNDED box style
48
48
  )
49
49
  await print(Padding(panel_obj, (top, right, bottom, left)), **kwargs)
50
50
 
@@ -4,7 +4,7 @@ from dataclasses import dataclass
4
4
  from typing import Optional
5
5
 
6
6
  from prompt_toolkit.completion import Completer
7
- from prompt_toolkit.formatted_text import FormattedText
7
+ from prompt_toolkit.formatted_text import HTML, FormattedText
8
8
  from prompt_toolkit.key_binding import KeyBindings
9
9
  from prompt_toolkit.lexers import Lexer
10
10
  from prompt_toolkit.shortcuts import PromptSession
@@ -42,12 +42,14 @@ class PromptManager:
42
42
  self.state_manager = state_manager
43
43
  self._temp_sessions = {} # For when no state manager is available
44
44
  self._style = self._create_style()
45
-
45
+
46
46
  def _create_style(self) -> Style:
47
47
  """Create the style for the prompt with file reference highlighting."""
48
- return Style.from_dict({
49
- 'file-reference': UI_COLORS.get('file_ref', 'light_blue'),
50
- })
48
+ return Style.from_dict(
49
+ {
50
+ "file-reference": UI_COLORS.get("file_ref", "light_blue"),
51
+ }
52
+ )
51
53
 
52
54
  def get_session(self, session_key: str, config: PromptConfig) -> PromptSession:
53
55
  """Get or create a prompt session.
@@ -98,10 +100,20 @@ class PromptManager:
98
100
  """
99
101
  session = self.get_session(session_key, config)
100
102
 
103
+ # Create a custom prompt that changes based on input
104
+ def get_prompt():
105
+ # Check if current buffer starts with "!"
106
+ if hasattr(session.app, "current_buffer") and session.app.current_buffer:
107
+ text = session.app.current_buffer.text
108
+ if text.startswith("!"):
109
+ # Use bright yellow background with black text for high visibility
110
+ return HTML('<style bg="#ffcc00" fg="black"><b> ◆ BASH MODE ◆ </b></style> ')
111
+ return HTML(prompt) if isinstance(prompt, str) else prompt
112
+
101
113
  try:
102
- # Get user input
114
+ # Get user input with dynamic prompt
103
115
  response = await session.prompt_async(
104
- prompt,
116
+ get_prompt,
105
117
  is_password=config.is_password,
106
118
  validator=config.validator,
107
119
  multiline=config.multiline,
@@ -0,0 +1,11 @@
1
+ """Utility for import caching and lazy importing heavy modules."""
2
+
3
+ import importlib
4
+
5
+ _import_cache = {}
6
+
7
+
8
+ def lazy_import(module_name: str):
9
+ if module_name not in _import_cache:
10
+ _import_cache[module_name] = importlib.import_module(module_name)
11
+ return _import_cache[module_name]
@@ -18,12 +18,34 @@ if TYPE_CHECKING:
18
18
  from tunacode.core.state import StateManager
19
19
 
20
20
 
21
+ import hashlib
22
+
23
+ _config_fingerprint = None
24
+ _config_cache = None
25
+
26
+
27
+ def compute_config_fingerprint(config_obj) -> str:
28
+ """Returns a short hash/fingerprint for a config object/searchable for fastpath usage."""
29
+ b = json.dumps(config_obj, sort_keys=True).encode()
30
+ return hashlib.sha1(b).hexdigest()[:12]
31
+
32
+
21
33
  def load_config() -> Optional[UserConfig]:
22
- """Load user config from file"""
34
+ """Load user config from file, using fingerprint fast path if available."""
35
+ global _config_fingerprint, _config_cache
23
36
  app_settings = ApplicationSettings()
24
37
  try:
25
38
  with open(app_settings.paths.config_file, "r") as f:
26
- return json.load(f)
39
+ raw = f.read()
40
+ loaded = json.loads(raw)
41
+ new_fp = hashlib.sha1(raw.encode()).hexdigest()[:12]
42
+ # If hash matches, return in-memory cached config object
43
+ if new_fp == _config_fingerprint and _config_cache is not None:
44
+ return _config_cache
45
+ # else, update fast path
46
+ _config_fingerprint = new_fp
47
+ _config_cache = loaded
48
+ return loaded
27
49
  except FileNotFoundError:
28
50
  return None
29
51
  except JSONDecodeError:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tunacode-cli
3
- Version: 0.0.17
3
+ Version: 0.0.19
4
4
  Summary: Your agentic CLI developer.
5
5
  Author-email: larock22 <noreply@github.com>
6
6
  License-Expression: MIT
@@ -58,14 +58,20 @@ Dynamic: license-file
58
58
 
59
59
  ---
60
60
 
61
- ### Recent Updates (v0.0.14)
62
-
63
- - **🔧 JSON Tool Parsing Fallback**: Automatic recovery when API providers fail with structured tool calling
64
- - **⚡ Enhanced Reliability**: Fixed parameter naming issues that caused tool schema errors
65
- - **🔄 Configuration Management**: New `/refresh` command to reload config without restart
66
- - **🧠 Improved ReAct Reasoning**: Enhanced iteration limits (now defaults to 20) and better thought processing
67
- - **🛠️ New Debug Commands**: `/parsetools` for manual JSON parsing, `/iterations` for controlling reasoning depth
68
- - **📊 Better Error Recovery**: Multiple fallback mechanisms for various failure scenarios
61
+ ### Recent Updates (v0.0.18)
62
+
63
+ - **Advanced Agent Orchestration**: New orchestrator system for complex multi-step tasks with planning visibility
64
+ - **Background Task Manager**: Asynchronous background processing for long-running operations
65
+ - **Read-Only Agent**: Specialized agent for safe codebase exploration without modification risks
66
+ - **Planning Transparency**: See the AI's planning process before execution with detailed task breakdowns
67
+ - **Shell Command Support**: Execute shell commands directly with `!command` or open interactive shell with `!`
68
+ - **Enhanced Bash Tool**: Advanced bash execution with timeouts, working directory, and environment variables
69
+ - **JSON Tool Parsing Fallback**: Automatic recovery when API providers fail with structured tool calling
70
+ - **Enhanced Reliability**: Fixed parameter naming issues that caused tool schema errors
71
+ - **Configuration Management**: New `/refresh` command to reload config without restart
72
+ - **Improved ReAct Reasoning**: Enhanced iteration limits (now defaults to 20) and better thought processing
73
+ - **New Debug Commands**: `/parsetools` for manual JSON parsing, `/iterations` for controlling reasoning depth
74
+ - **Better Error Recovery**: Multiple fallback mechanisms for various failure scenarios
69
75
 
70
76
  ### Core Features
71
77
 
@@ -84,6 +90,7 @@ Dynamic: license-file
84
90
  ### **Developer Tools**
85
91
 
86
92
  - 6 core tools: bash, grep, read_file, write_file, update_file, run_command
93
+ - Direct shell command execution with `!` prefix
87
94
  - MCP (Model Context Protocol) support
88
95
  - File operation confirmations with diffs
89
96
  - Per-project context guides (TUNACODE.md)
@@ -284,6 +291,8 @@ Learn more at [modelcontextprotocol.io](https://modelcontextprotocol.io/)
284
291
  | `/model <provider:name> default` | Set default model |
285
292
  | `/branch <name>` | Create and switch Git branch |
286
293
  | `/dump` | Show message history (debug) |
294
+ | `!<command>` | Run shell command |
295
+ | `!` | Open interactive shell |
287
296
  | `exit` | Exit application |
288
297
 
289
298
  ### Debug & Recovery Commands
@@ -298,6 +307,42 @@ Learn more at [modelcontextprotocol.io](https://modelcontextprotocol.io/)
298
307
 
299
308
  ---
300
309
 
310
+ ## Available Tools
311
+
312
+ ### Bash Tool
313
+ The enhanced bash tool provides advanced shell command execution with safety features:
314
+
315
+ - **Working Directory Support**: Execute commands in specific directories
316
+ - **Environment Variables**: Set custom environment variables for commands
317
+ - **Timeout Control**: Configurable timeouts (1-300 seconds) to prevent hanging
318
+ - **Output Capture**: Full stdout/stderr capture with truncation for large outputs
319
+ - **Safety Checks**: Warns about potentially destructive commands
320
+ - **Error Guidance**: Helpful error messages for common issues (command not found, permission denied, etc.)
321
+
322
+ **Example usage by the AI:**
323
+ ```python
324
+ # Simple command
325
+ await bash("ls -la")
326
+
327
+ # With working directory
328
+ await bash("npm install", cwd="/path/to/project")
329
+
330
+ # With timeout for long operations
331
+ await bash("npm run build", timeout=120)
332
+
333
+ # With environment variables
334
+ await bash("python script.py", env={"API_KEY": "secret"})
335
+ ```
336
+
337
+ ### Other Core Tools
338
+ - **grep**: Fast parallel content search across files
339
+ - **read_file**: Read file contents with line numbers
340
+ - **write_file**: Create new files (fails if file exists)
341
+ - **update_file**: Modify existing files with precise replacements
342
+ - **run_command**: Basic command execution (simpler than bash)
343
+
344
+ ---
345
+
301
346
  ## Reliability Features
302
347
 
303
348
  ### JSON Tool Parsing Fallback
@@ -360,7 +405,14 @@ src/tunacode/
360
405
 
361
406
  ├── core/ # Core Application Logic
362
407
  │ ├── agents/ # AI Agent System
363
- │ │ └── main.py # Primary agent implementation (pydantic-ai)
408
+ │ │ ├── main.py # Primary agent implementation (pydantic-ai)
409
+ │ │ ├── orchestrator.py # Complex task orchestration and planning
410
+ │ │ ├── planner_schema.py # Planning data models
411
+ │ │ └── readonly.py # Read-only agent for safe exploration
412
+ │ ├── background/ # Background Task Management
413
+ │ │ └── manager.py # Async background task execution
414
+ │ ├── llm/ # LLM Integration
415
+ │ │ └── planner.py # LLM-based task planning
364
416
  │ ├── setup/ # Application Setup & Initialization
365
417
  │ │ ├── agent_setup.py # Agent configuration
366
418
  │ │ ├── base.py # Setup step base class
@@ -409,7 +461,7 @@ src/tunacode/
409
461
  ├── exceptions.py # Custom exceptions
410
462
  ├── types.py # Type definitions
411
463
  └── prompts/
412
- └── system.txt # System prompts for AI agent
464
+ └── system.md # System prompts for AI agent
413
465
  ```
414
466
 
415
467
  ### Key Components
@@ -418,6 +470,8 @@ src/tunacode/
418
470
  | -------------------- | ------------------------ | ------------------------------- |
419
471
  | **CLI Layer** | Command parsing and REPL | `cli/main.py`, `cli/repl.py` |
420
472
  | **Agent System** | AI-powered assistance | `core/agents/main.py` |
473
+ | **Orchestrator** | Complex task planning | `core/agents/orchestrator.py` |
474
+ | **Background Tasks** | Async task execution | `core/background/manager.py` |
421
475
  | **Tool System** | File/command operations | `tools/*.py` |
422
476
  | **State Management** | Session state tracking | `core/state.py` |
423
477
  | **UI Framework** | Rich terminal interface | `ui/output.py`, `ui/console.py` |
@@ -482,6 +536,9 @@ While TunaCode builds on the foundation of sidekick-cli, we've made several arch
482
536
 
483
537
  - **JSON Tool Parsing Fallback**: Added fallback parsing for when API providers fail with structured tool calling
484
538
  - **Parallel Search Tools**: New `bash` and `grep` tools with parallel execution for codebase navigation
539
+ - **Agent Orchestration**: Advanced orchestrator for complex multi-step tasks with planning transparency
540
+ - **Background Processing**: Asynchronous task manager for long-running operations
541
+ - **Read-Only Agent**: Safe exploration mode that prevents accidental modifications
485
542
  - **ReAct Reasoning**: Implemented ReAct (Reasoning + Acting) patterns with configurable iteration limits
486
543
  - **Dynamic Configuration**: Added `/refresh` command and modified configuration management
487
544
  - **Safety Changes**: Removed automatic git commits and `/undo` command - requires explicit git usage