tunacode-cli 0.0.55__py3-none-any.whl → 0.0.78.6__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 (114) hide show
  1. tunacode/cli/commands/__init__.py +2 -2
  2. tunacode/cli/commands/implementations/__init__.py +2 -3
  3. tunacode/cli/commands/implementations/command_reload.py +48 -0
  4. tunacode/cli/commands/implementations/debug.py +2 -2
  5. tunacode/cli/commands/implementations/development.py +10 -8
  6. tunacode/cli/commands/implementations/model.py +357 -29
  7. tunacode/cli/commands/implementations/quickstart.py +43 -0
  8. tunacode/cli/commands/implementations/system.py +96 -3
  9. tunacode/cli/commands/implementations/template.py +0 -2
  10. tunacode/cli/commands/registry.py +139 -5
  11. tunacode/cli/commands/slash/__init__.py +32 -0
  12. tunacode/cli/commands/slash/command.py +157 -0
  13. tunacode/cli/commands/slash/loader.py +135 -0
  14. tunacode/cli/commands/slash/processor.py +294 -0
  15. tunacode/cli/commands/slash/types.py +93 -0
  16. tunacode/cli/commands/slash/validator.py +400 -0
  17. tunacode/cli/main.py +23 -2
  18. tunacode/cli/repl.py +217 -190
  19. tunacode/cli/repl_components/command_parser.py +38 -4
  20. tunacode/cli/repl_components/error_recovery.py +85 -4
  21. tunacode/cli/repl_components/output_display.py +12 -1
  22. tunacode/cli/repl_components/tool_executor.py +1 -1
  23. tunacode/configuration/defaults.py +12 -3
  24. tunacode/configuration/key_descriptions.py +284 -0
  25. tunacode/configuration/settings.py +0 -1
  26. tunacode/constants.py +12 -40
  27. tunacode/core/agents/__init__.py +43 -2
  28. tunacode/core/agents/agent_components/__init__.py +7 -0
  29. tunacode/core/agents/agent_components/agent_config.py +249 -55
  30. tunacode/core/agents/agent_components/agent_helpers.py +43 -13
  31. tunacode/core/agents/agent_components/node_processor.py +179 -139
  32. tunacode/core/agents/agent_components/response_state.py +123 -6
  33. tunacode/core/agents/agent_components/state_transition.py +116 -0
  34. tunacode/core/agents/agent_components/streaming.py +296 -0
  35. tunacode/core/agents/agent_components/task_completion.py +19 -6
  36. tunacode/core/agents/agent_components/tool_buffer.py +21 -1
  37. tunacode/core/agents/agent_components/tool_executor.py +10 -0
  38. tunacode/core/agents/main.py +522 -370
  39. tunacode/core/agents/main_legact.py +538 -0
  40. tunacode/core/agents/prompts.py +66 -0
  41. tunacode/core/agents/utils.py +29 -121
  42. tunacode/core/code_index.py +83 -29
  43. tunacode/core/setup/__init__.py +0 -2
  44. tunacode/core/setup/config_setup.py +110 -20
  45. tunacode/core/setup/config_wizard.py +230 -0
  46. tunacode/core/setup/coordinator.py +14 -5
  47. tunacode/core/state.py +16 -20
  48. tunacode/core/token_usage/usage_tracker.py +5 -3
  49. tunacode/core/tool_authorization.py +352 -0
  50. tunacode/core/tool_handler.py +67 -40
  51. tunacode/exceptions.py +119 -5
  52. tunacode/prompts/system.xml +751 -0
  53. tunacode/services/mcp.py +125 -7
  54. tunacode/setup.py +5 -25
  55. tunacode/tools/base.py +163 -0
  56. tunacode/tools/bash.py +110 -1
  57. tunacode/tools/glob.py +332 -34
  58. tunacode/tools/grep.py +179 -82
  59. tunacode/tools/grep_components/result_formatter.py +98 -4
  60. tunacode/tools/list_dir.py +132 -2
  61. tunacode/tools/prompts/bash_prompt.xml +72 -0
  62. tunacode/tools/prompts/glob_prompt.xml +45 -0
  63. tunacode/tools/prompts/grep_prompt.xml +98 -0
  64. tunacode/tools/prompts/list_dir_prompt.xml +31 -0
  65. tunacode/tools/prompts/react_prompt.xml +23 -0
  66. tunacode/tools/prompts/read_file_prompt.xml +54 -0
  67. tunacode/tools/prompts/run_command_prompt.xml +64 -0
  68. tunacode/tools/prompts/update_file_prompt.xml +53 -0
  69. tunacode/tools/prompts/write_file_prompt.xml +37 -0
  70. tunacode/tools/react.py +153 -0
  71. tunacode/tools/read_file.py +91 -0
  72. tunacode/tools/run_command.py +114 -0
  73. tunacode/tools/schema_assembler.py +167 -0
  74. tunacode/tools/update_file.py +94 -0
  75. tunacode/tools/write_file.py +86 -0
  76. tunacode/tools/xml_helper.py +83 -0
  77. tunacode/tutorial/__init__.py +9 -0
  78. tunacode/tutorial/content.py +98 -0
  79. tunacode/tutorial/manager.py +182 -0
  80. tunacode/tutorial/steps.py +124 -0
  81. tunacode/types.py +20 -27
  82. tunacode/ui/completers.py +434 -50
  83. tunacode/ui/config_dashboard.py +585 -0
  84. tunacode/ui/console.py +63 -11
  85. tunacode/ui/input.py +20 -3
  86. tunacode/ui/keybindings.py +7 -4
  87. tunacode/ui/model_selector.py +395 -0
  88. tunacode/ui/output.py +40 -19
  89. tunacode/ui/panels.py +212 -43
  90. tunacode/ui/path_heuristics.py +91 -0
  91. tunacode/ui/prompt_manager.py +5 -1
  92. tunacode/ui/tool_ui.py +33 -10
  93. tunacode/utils/api_key_validation.py +93 -0
  94. tunacode/utils/config_comparator.py +340 -0
  95. tunacode/utils/json_utils.py +206 -0
  96. tunacode/utils/message_utils.py +14 -4
  97. tunacode/utils/models_registry.py +593 -0
  98. tunacode/utils/ripgrep.py +332 -9
  99. tunacode/utils/text_utils.py +18 -1
  100. tunacode/utils/user_configuration.py +45 -0
  101. tunacode_cli-0.0.78.6.dist-info/METADATA +260 -0
  102. tunacode_cli-0.0.78.6.dist-info/RECORD +158 -0
  103. {tunacode_cli-0.0.55.dist-info → tunacode_cli-0.0.78.6.dist-info}/WHEEL +1 -2
  104. tunacode/cli/commands/implementations/todo.py +0 -217
  105. tunacode/context.py +0 -71
  106. tunacode/core/setup/git_safety_setup.py +0 -182
  107. tunacode/prompts/system.md +0 -731
  108. tunacode/tools/read_file_async_poc.py +0 -196
  109. tunacode/tools/todo.py +0 -349
  110. tunacode_cli-0.0.55.dist-info/METADATA +0 -322
  111. tunacode_cli-0.0.55.dist-info/RECORD +0 -126
  112. tunacode_cli-0.0.55.dist-info/top_level.txt +0 -1
  113. {tunacode_cli-0.0.55.dist-info → tunacode_cli-0.0.78.6.dist-info}/entry_points.txt +0 -0
  114. {tunacode_cli-0.0.55.dist-info → tunacode_cli-0.0.78.6.dist-info}/licenses/LICENSE +0 -0
@@ -6,6 +6,7 @@ Error recovery utilities for the REPL.
6
6
 
7
7
  import logging
8
8
 
9
+ import tunacode.core.agents as agent_api
9
10
  from tunacode.types import StateManager
10
11
  from tunacode.ui import console as ui
11
12
 
@@ -14,6 +15,79 @@ from .tool_executor import tool_handler
14
15
  logger = logging.getLogger(__name__)
15
16
 
16
17
  MSG_JSON_RECOVERY = "Recovered using JSON tool parsing"
18
+ MSG_JSON_ARGS_RECOVERY = "Recovered from malformed tool arguments"
19
+
20
+
21
+ async def attempt_json_args_recovery(e: Exception, state_manager: StateManager) -> bool:
22
+ """
23
+ Attempt to recover from JSON parsing errors in tool arguments.
24
+
25
+ This handles cases where the model emits concatenated JSON objects
26
+ or other malformed JSON in tool call arguments.
27
+
28
+ Returns:
29
+ bool: True if recovery was successful, False otherwise
30
+ """
31
+ error_str = str(e).lower()
32
+
33
+ # Check if this is a JSON parsing error with tool arguments
34
+ if not any(
35
+ keyword in error_str for keyword in ["invalid json", "extra data", "jsondecodeerror"]
36
+ ):
37
+ return False
38
+
39
+ if not state_manager.session.messages:
40
+ return False
41
+
42
+ last_msg = state_manager.session.messages[-1]
43
+ if not hasattr(last_msg, "parts"):
44
+ return False
45
+
46
+ # Look for tool call parts with malformed args
47
+ for part in last_msg.parts:
48
+ if hasattr(part, "tool_name") and hasattr(part, "args"):
49
+ # This is a structured tool call with potentially malformed args
50
+ try:
51
+ from tunacode.utils.json_utils import split_concatenated_json
52
+
53
+ # Try to split concatenated JSON objects in the args
54
+ if isinstance(part.args, str):
55
+ logger.info(f"Attempting to recover malformed args for tool {part.tool_name}")
56
+
57
+ try:
58
+ json_objects = split_concatenated_json(part.args)
59
+ if json_objects:
60
+ # Use the first object as the args
61
+ part.args = json_objects[0]
62
+
63
+ # Execute the recovered tool call
64
+ await tool_handler(part, state_manager)
65
+
66
+ await ui.warning(f"Warning: {MSG_JSON_ARGS_RECOVERY}")
67
+ logger.info(
68
+ f"Successfully recovered tool {part.tool_name} with "
69
+ f"split JSON args",
70
+ extra={
71
+ "original_args": part.args,
72
+ "recovered_args": json_objects[0],
73
+ },
74
+ )
75
+ return True
76
+
77
+ except Exception as split_exc:
78
+ logger.debug(f"Failed to split JSON args: {split_exc}")
79
+ continue
80
+
81
+ except Exception as recovery_exc:
82
+ logger.error(
83
+ f"Error during JSON args recovery for tool "
84
+ f"{getattr(part, 'tool_name', 'unknown')}",
85
+ exc_info=True,
86
+ extra={"recovery_exception": str(recovery_exc)},
87
+ )
88
+ continue
89
+
90
+ return False
17
91
 
18
92
 
19
93
  async def attempt_tool_recovery(e: Exception, state_manager: StateManager) -> bool:
@@ -25,9 +99,16 @@ async def attempt_tool_recovery(e: Exception, state_manager: StateManager) -> bo
25
99
  """
26
100
  error_str = str(e).lower()
27
101
  tool_keywords = ["tool", "function", "call", "schema"]
28
- if not any(keyword in error_str for keyword in tool_keywords):
102
+ json_keywords = ["json", "invalid json", "jsondecodeerror", "extra data", "validation"]
103
+ recovery_keywords = tool_keywords + json_keywords
104
+
105
+ if not any(keyword in error_str for keyword in recovery_keywords):
29
106
  return False
30
107
 
108
+ # First, try JSON args recovery for structured tool calls with malformed args
109
+ if await attempt_json_args_recovery(e, state_manager):
110
+ return True
111
+
31
112
  if not state_manager.session.messages:
32
113
  return False
33
114
 
@@ -48,17 +129,17 @@ async def attempt_tool_recovery(e: Exception, state_manager: StateManager) -> bo
48
129
  },
49
130
  )
50
131
  await ui.muted(
51
- f"⚠️ Model response error. Attempting to recover by parsing tools from text: {str(e)[:100]}..."
132
+ f"⚠️ Model response error. Attempting to recover by parsing tools "
133
+ f"from text: {str(e)[:100]}..."
52
134
  )
53
135
 
54
136
  try:
55
- from tunacode.core.agents.main import extract_and_execute_tool_calls
56
137
 
57
138
  def tool_callback_with_state(tool_part, _node):
58
139
  return tool_handler(tool_part, state_manager)
59
140
 
60
141
  # This function now returns the number of tools found
61
- tools_found = await extract_and_execute_tool_calls(
142
+ tools_found = await agent_api.extract_and_execute_tool_calls(
62
143
  content_to_parse, tool_callback_with_state, state_manager
63
144
  )
64
145
 
@@ -10,7 +10,7 @@ from tunacode.ui import console as ui
10
10
  MSG_REQUEST_COMPLETED = "Request completed"
11
11
 
12
12
 
13
- async def display_agent_output(res, enable_streaming: bool) -> None:
13
+ async def display_agent_output(res, enable_streaming: bool, state_manager=None) -> None:
14
14
  """Display agent output using guard clauses to flatten nested conditionals."""
15
15
  if enable_streaming:
16
16
  return
@@ -30,4 +30,15 @@ async def display_agent_output(res, enable_streaming: bool) -> None:
30
30
  if '"tool_uses"' in output:
31
31
  return
32
32
 
33
+ # Filter out system prompts and tool definitions
34
+ if any(
35
+ phrase in output
36
+ for phrase in [
37
+ "namespace functions {",
38
+ "namespace multi_tool_use {",
39
+ "You are trained on data up to",
40
+ ]
41
+ ):
42
+ return
43
+
33
44
  await ui.agent(output)
@@ -9,7 +9,7 @@ from asyncio.exceptions import CancelledError
9
9
 
10
10
  from prompt_toolkit.application import run_in_terminal
11
11
 
12
- from tunacode.core.agents.main import patch_tool_messages
12
+ from tunacode.core.agents import patch_tool_messages
13
13
  from tunacode.core.tool_handler import ToolHandler
14
14
  from tunacode.exceptions import UserAbortError
15
15
  from tunacode.types import StateManager
@@ -5,11 +5,11 @@ Default configuration values for the TunaCode CLI.
5
5
  Provides sensible defaults for user configuration and environment variables.
6
6
  """
7
7
 
8
- from tunacode.constants import GUIDE_FILE_NAME, ToolName
8
+ from tunacode.constants import GUIDE_FILE_NAME
9
9
  from tunacode.types import UserConfig
10
10
 
11
11
  DEFAULT_USER_CONFIG: UserConfig = {
12
- "default_model": "openai:gpt-4.1",
12
+ "default_model": "openrouter:openai/gpt-4.1",
13
13
  "env": {
14
14
  "ANTHROPIC_API_KEY": "",
15
15
  "GEMINI_API_KEY": "",
@@ -19,11 +19,20 @@ DEFAULT_USER_CONFIG: UserConfig = {
19
19
  "settings": {
20
20
  "max_retries": 10,
21
21
  "max_iterations": 40,
22
- "tool_ignore": [ToolName.READ_FILE],
22
+ "tool_ignore": [],
23
23
  "guide_file": GUIDE_FILE_NAME,
24
24
  "fallback_response": True,
25
25
  "fallback_verbosity": "normal", # Options: minimal, normal, detailed
26
26
  "context_window_size": 200000,
27
+ "enable_streaming": True, # Always enable streaming
28
+ "ripgrep": {
29
+ "use_bundled": False, # Use system ripgrep binary
30
+ "timeout": 10, # Search timeout in seconds
31
+ "max_buffer_size": 1048576, # 1MB max output buffer
32
+ "max_results": 100, # Maximum results per search
33
+ "enable_metrics": False, # Enable performance metrics collection
34
+ "debug": False, # Enable debug logging for ripgrep operations
35
+ },
27
36
  },
28
37
  "mcpServers": {},
29
38
  }
@@ -0,0 +1,284 @@
1
+ """
2
+ Module: tunacode.configuration.key_descriptions
3
+
4
+ Educational descriptions and examples for configuration keys to help users
5
+ understand what each setting does and how to configure it properly.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from typing import Any, Dict, Optional
10
+
11
+
12
+ @dataclass
13
+ class KeyDescription:
14
+ """Description of a configuration key with examples and help text."""
15
+
16
+ name: str
17
+ description: str
18
+ example: Any
19
+ help_text: str
20
+ category: str
21
+ is_sensitive: bool = False
22
+ service_type: Optional[str] = None # For API keys: "openai", "anthropic", etc.
23
+
24
+
25
+ # Configuration key descriptions organized by category
26
+ CONFIG_KEY_DESCRIPTIONS: Dict[str, KeyDescription] = {
27
+ # Root level keys
28
+ "default_model": KeyDescription(
29
+ name="default_model",
30
+ description="Which AI model TunaCode uses by default",
31
+ example="openrouter:openai/gpt-4.1",
32
+ help_text="Format: provider:model-name. Examples: openai:gpt-4, "
33
+ "anthropic:claude-3-sonnet, google:gemini-pro",
34
+ category="AI Models",
35
+ ),
36
+ # Environment variables (API Keys)
37
+ "env.OPENAI_API_KEY": KeyDescription(
38
+ name="OPENAI_API_KEY",
39
+ description="Your OpenAI API key for GPT models",
40
+ example="sk-proj-abc123...",
41
+ help_text="Get this from https://platform.openai.com/api-keys. "
42
+ "Required for OpenAI models like GPT-4.",
43
+ category="API Keys",
44
+ is_sensitive=True,
45
+ service_type="openai",
46
+ ),
47
+ "env.ANTHROPIC_API_KEY": KeyDescription(
48
+ name="ANTHROPIC_API_KEY",
49
+ description="Your Anthropic API key for Claude models",
50
+ example="sk-ant-api03-abc123...",
51
+ help_text="Get this from https://console.anthropic.com/. Required for Claude models.",
52
+ category="API Keys",
53
+ is_sensitive=True,
54
+ service_type="anthropic",
55
+ ),
56
+ "env.OPENROUTER_API_KEY": KeyDescription(
57
+ name="OPENROUTER_API_KEY",
58
+ description="Your OpenRouter API key for accessing multiple models",
59
+ example="sk-or-v1-abc123...",
60
+ help_text="Get this from https://openrouter.ai/keys. "
61
+ "Gives access to many different AI models.",
62
+ category="API Keys",
63
+ is_sensitive=True,
64
+ service_type="openrouter",
65
+ ),
66
+ "env.GEMINI_API_KEY": KeyDescription(
67
+ name="GEMINI_API_KEY",
68
+ description="Your Google Gemini API key",
69
+ example="AIza123...",
70
+ help_text="Get this from Google AI Studio. Required for Gemini models.",
71
+ category="API Keys",
72
+ is_sensitive=True,
73
+ service_type="google",
74
+ ),
75
+ "env.OPENAI_BASE_URL": KeyDescription(
76
+ name="OPENAI_BASE_URL",
77
+ description="Custom API endpoint for OpenAI-compatible services",
78
+ example="https://api.cerebras.ai/v1",
79
+ help_text="Use this to connect to local models (LM Studio, Ollama) or "
80
+ "alternative providers like Cerebras.",
81
+ category="API Configuration",
82
+ ),
83
+ # Settings
84
+ "settings.max_retries": KeyDescription(
85
+ name="max_retries",
86
+ description="How many times to retry failed API calls",
87
+ example=10,
88
+ help_text="Higher values = more resilient to temporary API issues, "
89
+ "but slower when APIs are down.",
90
+ category="Behavior Settings",
91
+ ),
92
+ "settings.max_iterations": KeyDescription(
93
+ name="max_iterations",
94
+ description="Maximum conversation turns before stopping",
95
+ example=40,
96
+ help_text="Prevents infinite loops. TunaCode will stop after this many "
97
+ "back-and-forth exchanges.",
98
+ category="Behavior Settings",
99
+ ),
100
+ "settings.tool_ignore": KeyDescription(
101
+ name="tool_ignore",
102
+ description="List of tools TunaCode should not use",
103
+ example=["read_file", "write_file"],
104
+ help_text="Useful for restricting what TunaCode can do. Empty list means "
105
+ "all tools are available.",
106
+ category="Tool Configuration",
107
+ ),
108
+ "settings.guide_file": KeyDescription(
109
+ name="guide_file",
110
+ description="Name of your project guide file",
111
+ example="AGENTS.md",
112
+ help_text="TunaCode looks for this file to understand your project. "
113
+ "Usually AGENTS.md or README.md.",
114
+ category="Project Settings",
115
+ ),
116
+ "settings.fallback_response": KeyDescription(
117
+ name="fallback_response",
118
+ description="Whether to provide a response when tools fail",
119
+ example=True,
120
+ help_text="When true, TunaCode will try to help even if some tools don't work properly.",
121
+ category="Behavior Settings",
122
+ ),
123
+ "settings.fallback_verbosity": KeyDescription(
124
+ name="fallback_verbosity",
125
+ description="How detailed fallback responses should be",
126
+ example="normal",
127
+ help_text="Options: minimal, normal, detailed. Controls how much "
128
+ "TunaCode explains when things go wrong.",
129
+ category="Behavior Settings",
130
+ ),
131
+ "settings.context_window_size": KeyDescription(
132
+ name="context_window_size",
133
+ description="Maximum tokens TunaCode can use in one conversation",
134
+ example=200000,
135
+ help_text="Larger values = TunaCode remembers more context, but costs more. "
136
+ "Adjust based on your model's limits.",
137
+ category="Performance Settings",
138
+ ),
139
+ "settings.enable_streaming": KeyDescription(
140
+ name="enable_streaming",
141
+ description="Show AI responses as they're generated",
142
+ example=True,
143
+ help_text="When true, you see responses appear word-by-word. "
144
+ "When false, you wait for complete responses.",
145
+ category="User Experience",
146
+ ),
147
+ # Ripgrep settings
148
+ "settings.ripgrep.use_bundled": KeyDescription(
149
+ name="ripgrep.use_bundled",
150
+ description="Use TunaCode's built-in ripgrep instead of system version",
151
+ example=False,
152
+ help_text="Usually false is better - uses your system's ripgrep which may be newer/faster.",
153
+ category="Search Settings",
154
+ ),
155
+ "settings.ripgrep.timeout": KeyDescription(
156
+ name="ripgrep.timeout",
157
+ description="How long to wait for search results (seconds)",
158
+ example=10,
159
+ help_text="Prevents searches from hanging. Increase for very large codebases.",
160
+ category="Search Settings",
161
+ ),
162
+ "settings.ripgrep.max_buffer_size": KeyDescription(
163
+ name="ripgrep.max_buffer_size",
164
+ description="Maximum size of search results (bytes)",
165
+ example=1048576,
166
+ help_text="1MB by default. Prevents memory issues with huge search results.",
167
+ category="Search Settings",
168
+ ),
169
+ "settings.ripgrep.max_results": KeyDescription(
170
+ name="ripgrep.max_results",
171
+ description="Maximum number of search results to return",
172
+ example=100,
173
+ help_text="Prevents overwhelming output. Increase if you need more "
174
+ "comprehensive search results.",
175
+ category="Search Settings",
176
+ ),
177
+ "settings.ripgrep.enable_metrics": KeyDescription(
178
+ name="ripgrep.enable_metrics",
179
+ description="Collect performance data about searches",
180
+ example=False,
181
+ help_text="Enable for debugging search performance. Usually not needed.",
182
+ category="Search Settings",
183
+ ),
184
+ "settings.ripgrep.debug": KeyDescription(
185
+ name="ripgrep.debug",
186
+ description="Show detailed search debugging information",
187
+ example=False,
188
+ help_text="Enable for troubleshooting search issues. Creates verbose output.",
189
+ category="Search Settings",
190
+ ),
191
+ # Tutorial/onboarding settings
192
+ "settings.enable_tutorial": KeyDescription(
193
+ name="enable_tutorial",
194
+ description="Show tutorial prompts for new users",
195
+ example=True,
196
+ help_text="Helps new users learn TunaCode. Disable once you're comfortable with the tool.",
197
+ category="User Experience",
198
+ ),
199
+ "settings.first_installation_date": KeyDescription(
200
+ name="first_installation_date",
201
+ description="When TunaCode was first installed",
202
+ example="2025-09-11T11:50:40.167105",
203
+ help_text="Automatically set. Used for tracking usage patterns and showing relevant tips.",
204
+ category="System Information",
205
+ ),
206
+ "settings.tutorial_declined": KeyDescription(
207
+ name="tutorial_declined",
208
+ description="Whether user declined the tutorial",
209
+ example=True,
210
+ help_text="Automatically set when you skip the tutorial. Prevents repeated "
211
+ "tutorial prompts.",
212
+ category="User Experience",
213
+ ),
214
+ # MCP Servers
215
+ "mcpServers": KeyDescription(
216
+ name="mcpServers",
217
+ description="Model Context Protocol server configurations",
218
+ example={},
219
+ help_text="Advanced feature for connecting external tools and services. "
220
+ "Usually empty for basic usage.",
221
+ category="Advanced Features",
222
+ ),
223
+ }
224
+
225
+
226
+ def get_key_description(key_path: str) -> Optional[KeyDescription]:
227
+ """Get description for a configuration key by its path."""
228
+ return CONFIG_KEY_DESCRIPTIONS.get(key_path)
229
+
230
+
231
+ def get_service_type_for_api_key(key_name: str) -> Optional[str]:
232
+ """Determine the service type for an API key."""
233
+ service_mapping = {
234
+ "OPENAI_API_KEY": "openai",
235
+ "ANTHROPIC_API_KEY": "anthropic",
236
+ "OPENROUTER_API_KEY": "openrouter",
237
+ "GEMINI_API_KEY": "google",
238
+ }
239
+ return service_mapping.get(key_name)
240
+
241
+
242
+ def get_categories() -> Dict[str, list[KeyDescription]]:
243
+ """Get all configuration keys organized by category."""
244
+ categories: Dict[str, list[KeyDescription]] = {}
245
+
246
+ for desc in CONFIG_KEY_DESCRIPTIONS.values():
247
+ if desc.category not in categories:
248
+ categories[desc.category] = []
249
+ categories[desc.category].append(desc)
250
+
251
+ return categories
252
+
253
+
254
+ def get_configuration_glossary() -> str:
255
+ """Generate a glossary of configuration terms for the help section."""
256
+ glossary = """
257
+ [bold]Configuration Key Glossary[/bold]
258
+
259
+ [cyan]What are configuration keys?[/cyan]
260
+ Configuration keys are setting names (like 'default_model', 'max_retries') that
261
+ control how TunaCode behaves.
262
+ Think of them like preferences in any app - they let you customize TunaCode to work
263
+ the way you want.
264
+
265
+ [cyan]Key Categories:[/cyan]
266
+ • [yellow]AI Models[/yellow]: Which AI to use (GPT-4, Claude, etc.)
267
+ • [yellow]API Keys[/yellow]: Your credentials for AI services
268
+ • [yellow]Behavior Settings[/yellow]: How TunaCode acts (retries, iterations, etc.)
269
+ • [yellow]Tool Configuration[/yellow]: Which tools TunaCode can use
270
+ • [yellow]Performance Settings[/yellow]: Memory and speed optimizations
271
+ • [yellow]User Experience[/yellow]: Interface and tutorial preferences
272
+
273
+ [cyan]Common Examples:[/cyan]
274
+ • default_model → Which AI model to use by default
275
+ • max_retries → How many times to retry failed requests
276
+ • OPENAI_API_KEY → Your OpenAI account credentials
277
+ • tool_ignore → List of tools TunaCode shouldn't use
278
+ • context_window_size → How much conversation history to remember
279
+
280
+ [cyan]Default vs Custom:[/cyan]
281
+ • 📋 Default: TunaCode's built-in settings (work for most people)
282
+ • 🔧 Custom: Settings you've changed to fit your needs
283
+ """
284
+ return glossary.strip()
@@ -30,7 +30,6 @@ class ApplicationSettings:
30
30
  ToolName.LIST_DIR,
31
31
  ToolName.READ_FILE,
32
32
  ToolName.RUN_COMMAND,
33
- ToolName.TODO,
34
33
  ToolName.UPDATE_FILE,
35
34
  ToolName.WRITE_FILE,
36
35
  ]
tunacode/constants.py CHANGED
@@ -9,12 +9,12 @@ from enum import Enum
9
9
 
10
10
  # Application info
11
11
  APP_NAME = "TunaCode"
12
- APP_VERSION = "0.0.55"
12
+ APP_VERSION = "0.0.78.6"
13
13
 
14
14
 
15
15
  # File patterns
16
16
  GUIDE_FILE_PATTERN = "{name}.md"
17
- GUIDE_FILE_NAME = "TUNACODE.md"
17
+ GUIDE_FILE_NAME = "AGENTS.md"
18
18
  ENV_FILE = ".env"
19
19
  CONFIG_FILE_NAME = "tunacode.json"
20
20
 
@@ -43,7 +43,7 @@ class ToolName(str, Enum):
43
43
  GREP = "grep"
44
44
  LIST_DIR = "list_dir"
45
45
  GLOB = "glob"
46
- TODO = "todo"
46
+ REACT = "react"
47
47
 
48
48
 
49
49
  # Tool names (backward compatibility)
@@ -55,10 +55,16 @@ TOOL_BASH = ToolName.BASH
55
55
  TOOL_GREP = ToolName.GREP
56
56
  TOOL_LIST_DIR = ToolName.LIST_DIR
57
57
  TOOL_GLOB = ToolName.GLOB
58
- TOOL_TODO = ToolName.TODO
58
+ TOOL_REACT = ToolName.REACT
59
59
 
60
60
  # Tool categorization
61
- READ_ONLY_TOOLS = [ToolName.READ_FILE, ToolName.GREP, ToolName.LIST_DIR, ToolName.GLOB]
61
+ READ_ONLY_TOOLS = [
62
+ ToolName.READ_FILE,
63
+ ToolName.GREP,
64
+ ToolName.LIST_DIR,
65
+ ToolName.GLOB,
66
+ ToolName.REACT,
67
+ ]
62
68
  WRITE_TOOLS = [ToolName.WRITE_FILE, ToolName.UPDATE_FILE]
63
69
  EXECUTE_TOOLS = [ToolName.BASH, ToolName.RUN_COMMAND]
64
70
 
@@ -112,7 +118,7 @@ UI_COLORS = {
112
118
  }
113
119
 
114
120
  # UI text and formatting
115
- UI_PROMPT_PREFIX = "> "
121
+ UI_PROMPT_PREFIX = '<style fg="#00d7ff"><b>> </b></style>'
116
122
  UI_THINKING_MESSAGE = "[bold #00d7ff]Thinking...[/bold #00d7ff]"
117
123
  UI_DARKGREY_OPEN = "<darkgrey>"
118
124
  UI_DARKGREY_CLOSE = "</darkgrey>"
@@ -159,40 +165,6 @@ MSG_UPDATE_INSTRUCTION = "Exit, and run: [bold]pip install --upgrade tunacode-cl
159
165
  MSG_VERSION_DISPLAY = "TunaCode CLI {version}"
160
166
  MSG_FILE_SIZE_LIMIT = " Please specify a smaller file or use other tools to process it."
161
167
 
162
-
163
- class TodoStatus(str, Enum):
164
- """Enumeration of todo statuses."""
165
-
166
- PENDING = "pending"
167
- IN_PROGRESS = "in_progress"
168
- COMPLETED = "completed"
169
-
170
-
171
- class TodoPriority(str, Enum):
172
- """Enumeration of todo priorities."""
173
-
174
- HIGH = "high"
175
- MEDIUM = "medium"
176
- LOW = "low"
177
-
178
-
179
- # Todo-related constants (backward compatibility)
180
- TODO_STATUS_PENDING = TodoStatus.PENDING
181
- TODO_STATUS_IN_PROGRESS = TodoStatus.IN_PROGRESS
182
- TODO_STATUS_COMPLETED = TodoStatus.COMPLETED
183
- TODO_STATUSES = [TodoStatus.PENDING, TodoStatus.IN_PROGRESS, TodoStatus.COMPLETED]
184
-
185
- TODO_PRIORITY_HIGH = TodoPriority.HIGH
186
- TODO_PRIORITY_MEDIUM = TodoPriority.MEDIUM
187
- TODO_PRIORITY_LOW = TodoPriority.LOW
188
- TODO_PRIORITIES = [TodoPriority.HIGH, TodoPriority.MEDIUM, TodoPriority.LOW]
189
-
190
- # Maximum number of todos allowed per session
191
- MAX_TODOS_PER_SESSION = 100
192
-
193
- # Maximum length for todo content
194
- MAX_TODO_CONTENT_LENGTH = 500
195
-
196
168
  # JSON parsing retry configuration
197
169
  JSON_PARSE_MAX_RETRIES = 10
198
170
  JSON_PARSE_BASE_DELAY = 0.1 # Initial delay in seconds
@@ -1,8 +1,49 @@
1
- """Agent helper modules."""
1
+ """Public entry points for TunaCode agent orchestration."""
2
2
 
3
- from .main import get_or_create_agent, process_request
3
+ from . import main as main
4
+ from .agent_components import (
5
+ AgentRunWithState,
6
+ AgentRunWrapper,
7
+ ResponseState,
8
+ SimpleResult,
9
+ ToolBuffer,
10
+ _process_node,
11
+ check_task_completion,
12
+ execute_tools_parallel,
13
+ extract_and_execute_tool_calls,
14
+ get_model_messages,
15
+ get_or_create_agent,
16
+ parse_json_tool_calls,
17
+ patch_tool_messages,
18
+ )
19
+ from .main import (
20
+ check_query_satisfaction,
21
+ cleanup_mcp_servers,
22
+ get_agent_tool,
23
+ get_mcp_servers,
24
+ process_request,
25
+ register_mcp_agent,
26
+ )
4
27
 
5
28
  __all__ = [
6
29
  "process_request",
7
30
  "get_or_create_agent",
31
+ "extract_and_execute_tool_calls",
32
+ "parse_json_tool_calls",
33
+ "get_model_messages",
34
+ "patch_tool_messages",
35
+ "_process_node",
36
+ "ResponseState",
37
+ "SimpleResult",
38
+ "AgentRunWrapper",
39
+ "AgentRunWithState",
40
+ "ToolBuffer",
41
+ "check_task_completion",
42
+ "execute_tools_parallel",
43
+ "get_mcp_servers",
44
+ "cleanup_mcp_servers",
45
+ "register_mcp_agent",
46
+ "check_query_satisfaction",
47
+ "get_agent_tool",
48
+ "main",
8
49
  ]
@@ -1,5 +1,7 @@
1
1
  """Agent components package for modular agent functionality."""
2
2
 
3
+ from tunacode.ui.tool_descriptions import get_batch_description
4
+
3
5
  from .agent_config import get_or_create_agent
4
6
  from .agent_helpers import (
5
7
  create_empty_response_message,
@@ -11,12 +13,14 @@ from .agent_helpers import (
11
13
  get_tool_description,
12
14
  get_tool_summary,
13
15
  get_user_prompt_part_class,
16
+ handle_empty_response,
14
17
  )
15
18
  from .json_tool_parser import extract_and_execute_tool_calls, parse_json_tool_calls
16
19
  from .message_handler import get_model_messages, patch_tool_messages
17
20
  from .node_processor import _process_node
18
21
  from .response_state import ResponseState
19
22
  from .result_wrapper import AgentRunWithState, AgentRunWrapper, SimpleResult
23
+ from .streaming import stream_model_request_node
20
24
  from .task_completion import check_task_completion
21
25
  from .tool_buffer import ToolBuffer
22
26
  from .tool_executor import execute_tools_parallel
@@ -44,4 +48,7 @@ __all__ = [
44
48
  "get_tool_description",
45
49
  "get_tool_summary",
46
50
  "get_user_prompt_part_class",
51
+ "handle_empty_response",
52
+ "stream_model_request_node",
53
+ "get_batch_description",
47
54
  ]