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

@@ -39,7 +39,7 @@ class TaskResult:
39
39
 
40
40
  task_id: str
41
41
  task_title: str
42
- result_data: Any
42
+ result_data: object
43
43
  status: str # completed, failed, partial
44
44
  timestamp: datetime = field(default_factory=datetime.now)
45
45
  error: Optional[str] = None
@@ -50,7 +50,7 @@ class TaskResult:
50
50
  class AggregatedResult:
51
51
  """Aggregated result from multiple tasks."""
52
52
 
53
- primary_result: Any
53
+ primary_result: object
54
54
  task_results: List[TaskResult]
55
55
  strategy_used: AggregationStrategy
56
56
  conflicts_resolved: int = 0
@@ -73,7 +73,7 @@ class TaskDecomposer:
73
73
  # Get the main agent
74
74
  agent = self.state_manager.session.agents.get("main")
75
75
  if not agent:
76
- logger.warning("Main agent not available, using heuristic decomposition")
76
+ logger.debug("Main agent not available, using heuristic decomposition")
77
77
  return self._heuristic_decomposition(task_description)
78
78
 
79
79
  # Build context-aware prompt
@@ -32,7 +32,7 @@ class TaskNode:
32
32
  iteration_budget: int = 10
33
33
  subtasks: List["TaskNode"] = field(default_factory=list)
34
34
  status: str = "pending" # pending, in_progress, completed, failed
35
- result: Optional[Any] = None
35
+ result: Optional[object] = None
36
36
  error: Optional[str] = None
37
37
  context: Dict[str, Any] = field(default_factory=dict)
38
38
  depth: int = 0
@@ -128,15 +128,17 @@ class RecursiveTaskExecutor:
128
128
  and depth < self.max_depth - 1
129
129
  ):
130
130
  # Decompose into subtasks
131
- logger.info(
132
- f"Decomposing complex task (score: {complexity_result.complexity_score:.2f})"
133
- )
131
+ if self.state_manager.session.show_thoughts:
132
+ logger.info(
133
+ f"Decomposing complex task (score: {complexity_result.complexity_score:.2f})"
134
+ )
134
135
  return await self._execute_with_decomposition(task_node, complexity_result)
135
136
  else:
136
137
  # Execute directly
137
- logger.info(
138
- f"Executing task directly (score: {complexity_result.complexity_score:.2f})"
139
- )
138
+ if self.state_manager.session.show_thoughts:
139
+ logger.info(
140
+ f"Executing task directly (score: {complexity_result.complexity_score:.2f})"
141
+ )
140
142
  return await self._execute_directly(task_node)
141
143
 
142
144
  except Exception as e:
@@ -255,7 +257,8 @@ Respond in JSON format:
255
257
  )
256
258
 
257
259
  for i, (subtask_desc, budget) in enumerate(zip(subtasks, subtask_budgets)):
258
- logger.info(f"Executing subtask {i + 1}/{len(subtasks)}: {subtask_desc[:50]}...")
260
+ if self.state_manager.session.show_thoughts:
261
+ logger.info(f"Executing subtask {i + 1}/{len(subtasks)}: {subtask_desc[:50]}...")
259
262
 
260
263
  # Create subtask context
261
264
  subtask_context = {
tunacode/ui/input.py CHANGED
@@ -75,12 +75,13 @@ async def multiline_input(
75
75
  state_manager: Optional[StateManager] = None, command_registry=None
76
76
  ) -> str:
77
77
  """Get multiline input from the user with @file completion and highlighting."""
78
- kb = create_key_bindings()
78
+ kb = create_key_bindings(state_manager)
79
79
  placeholder = formatted_text(
80
80
  (
81
81
  "<darkgrey>"
82
82
  "<bold>Enter</bold> to submit • "
83
83
  "<bold>Esc + Enter</bold> for new line • "
84
+ "<bold>Esc</bold> to cancel • "
84
85
  "<bold>/help</bold> for commands"
85
86
  "</darkgrey>"
86
87
  )
@@ -1,9 +1,15 @@
1
1
  """Key binding handlers for TunaCode UI."""
2
2
 
3
+ import logging
4
+
3
5
  from prompt_toolkit.key_binding import KeyBindings
4
6
 
7
+ from ..core.state import StateManager
8
+
9
+ logger = logging.getLogger(__name__)
5
10
 
6
- def create_key_bindings() -> KeyBindings:
11
+
12
+ def create_key_bindings(state_manager: StateManager = None) -> KeyBindings:
7
13
  """Create and configure key bindings for the UI."""
8
14
  kb = KeyBindings()
9
15
 
@@ -22,4 +28,14 @@ def create_key_bindings() -> KeyBindings:
22
28
  """Insert a newline when escape then enter is pressed."""
23
29
  event.current_buffer.insert_text("\n")
24
30
 
31
+ @kb.add("escape")
32
+ def _escape(event):
33
+ """Immediately interrupts the current operation."""
34
+ current_task = state_manager.session.current_task if state_manager else None
35
+ if current_task and not current_task.done():
36
+ logger.debug("Interrupting current task")
37
+ current_task.cancel()
38
+ else:
39
+ logger.debug("Escape key pressed outside task context")
40
+
25
41
  return kb
tunacode/ui/panels.py CHANGED
@@ -9,6 +9,7 @@ from rich.padding import Padding
9
9
  from rich.panel import Panel
10
10
  from rich.pretty import Pretty
11
11
  from rich.table import Table
12
+ from rich.text import Text
12
13
 
13
14
  from tunacode.configuration.models import ModelRegistry
14
15
  from tunacode.constants import (
@@ -89,9 +90,15 @@ class StreamingAgentPanel:
89
90
  # Use the UI_THINKING_MESSAGE constant instead of hardcoded text
90
91
  from tunacode.constants import UI_THINKING_MESSAGE
91
92
 
92
- markdown_content = Markdown(self.content or UI_THINKING_MESSAGE)
93
+ # If no content, show thinking message with Rich markup
94
+ if not self.content:
95
+ content_to_display = Text.from_markup(UI_THINKING_MESSAGE)
96
+ else:
97
+ # Normal content is markdown
98
+ content_to_display = Markdown(self.content)
99
+
93
100
  panel_obj = Panel(
94
- Padding(markdown_content, (0, 1, 0, 1)),
101
+ Padding(content_to_display, (0, 1, 0, 1)),
95
102
  title=f"[bold]{self.title}[/bold]",
96
103
  title_align="left",
97
104
  border_style=colors.primary,
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tunacode-cli
3
- Version: 0.0.44
3
+ Version: 0.0.46
4
4
  Summary: Your agentic CLI developer.
5
5
  Author-email: larock22 <noreply@github.com>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/larock22/tunacode
8
- Project-URL: Repository, https://github.com/larock22/tunacode
6
+ License: MIT
7
+ Project-URL: Homepage, https://tunacode.xyz/
8
+ Project-URL: Repository, https://github.com/alchemiststudiosDOTai/tunacode
9
+ Project-URL: Issues, https://github.com/alchemiststudiosDOTai/tunacode/issues
10
+ Project-URL: Documentation, https://github.com/alchemiststudiosDOTai/tunacode#readme
9
11
  Keywords: cli,agent,development,automation
10
12
  Classifier: Development Status :: 4 - Beta
11
13
  Classifier: Intended Audience :: Developers
@@ -16,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
16
18
  Classifier: Programming Language :: Python :: 3.13
17
19
  Classifier: Topic :: Software Development
18
20
  Classifier: Topic :: Utilities
19
- Requires-Python: >=3.10
21
+ Requires-Python: <3.14,>=3.10
20
22
  Description-Content-Type: text/markdown
21
23
  License-File: LICENSE
22
24
  Requires-Dist: typer==0.15.3
@@ -25,8 +27,6 @@ Requires-Dist: pydantic-ai[logfire]==0.2.6
25
27
  Requires-Dist: pygments==2.19.1
26
28
  Requires-Dist: rich==14.0.0
27
29
  Requires-Dist: tiktoken>=0.5.2
28
- Requires-Dist: dspy-ai>=0.1.0
29
- Requires-Dist: python-dotenv>=1.0.0
30
30
  Provides-Extra: dev
31
31
  Requires-Dist: build; extra == "dev"
32
32
  Requires-Dist: ruff; extra == "dev"
@@ -48,7 +48,7 @@ Dynamic: license-file
48
48
 
49
49
  **AI-powered CLI coding assistant**
50
50
 
51
- ![Demo](docs/assets/demo.gif)
51
+ ![TunaCode Example](assets/tunacode_example.png)
52
52
 
53
53
  </div>
54
54
 
@@ -64,6 +64,29 @@ wget -qO- https://raw.githubusercontent.com/alchemiststudiosDOTai/tunacode/maste
64
64
  pip install tunacode-cli
65
65
  ```
66
66
 
67
+ ## Development Installation
68
+
69
+ For contributors and developers who want to work on TunaCode:
70
+
71
+ ```bash
72
+ # Clone the repository
73
+ git clone https://github.com/alchemiststudiosDOTai/tunacode.git
74
+ cd tunacode
75
+
76
+ # Quick setup (recommended)
77
+ ./scripts/setup_dev_env.sh
78
+
79
+ # Or manual setup
80
+ python3 -m venv venv
81
+ source venv/bin/activate # On Windows: venv\Scripts\activate
82
+ pip install -e ".[dev]"
83
+
84
+ # Verify installation
85
+ python -m tunacode --version
86
+ ```
87
+
88
+ See [Development Guide](docs/DEVELOPMENT.md) for detailed instructions.
89
+
67
90
  ## Configuration
68
91
 
69
92
  Choose your AI provider and set your API key:
@@ -141,6 +164,7 @@ _Note: While the tool is fully functional, we're focusing on stability and core
141
164
  - [**Advanced Configuration**](docs/ADVANCED-CONFIG.md) - Provider setup, MCP, customization
142
165
  - [**Architecture**](docs/ARCHITECTURE.md) - Source code organization and design
143
166
  - [**Development**](docs/DEVELOPMENT.md) - Contributing and development setup
167
+ - [**Troubleshooting**](docs/TROUBLESHOOTING.md) - Common issues and solutions
144
168
 
145
169
  ## Links
146
170
 
@@ -1,5 +1,5 @@
1
1
  tunacode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- tunacode/constants.py,sha256=e5ew-znjhA7bItbVdVDnF51SR7xhvENeVOR541rc9_E,5168
2
+ tunacode/constants.py,sha256=o1NSdboUcXjPgeYQJ7sCC7HtStJP2e8fE9lo0Jio0N4,5168
3
3
  tunacode/context.py,sha256=_gXVCyjU052jlyRAl9tklZSwl5U_zI_EIX8XN87VVWE,2786
4
4
  tunacode/exceptions.py,sha256=oDO1SVKOgjcKIylwqdbqh_g5my4roU5mB9Nv4n_Vb0s,3877
5
5
  tunacode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -7,7 +7,7 @@ tunacode/setup.py,sha256=XPt4eAK-qcIZQv64jGZ_ryxcImDwps9OmXjJfIS1xcs,1899
7
7
  tunacode/types.py,sha256=Czq7jYXHq7fZQtyqkCN5_7eEu1wyYUcB50C6v3sTWDw,8188
8
8
  tunacode/cli/__init__.py,sha256=zgs0UbAck8hfvhYsWhWOfBe5oK09ug2De1r4RuQZREA,55
9
9
  tunacode/cli/main.py,sha256=erP6jNXcxVQOVn8sm6uNaLEAYevniVXl6Sem872mW68,2755
10
- tunacode/cli/repl.py,sha256=CMvl5wlxj7hr45mxil26fwbmWpDYxRsRVS9aIGJvKK0,18525
10
+ tunacode/cli/repl.py,sha256=rSsUievdOdTkxzHNIEgUHbuWbk0ki78743Wz5hVFe9s,18917
11
11
  tunacode/cli/commands/__init__.py,sha256=zmE9JcJ9Qd2xJhgdS4jMDJOoZsrAZmL5MAFxbKkk7F8,1670
12
12
  tunacode/cli/commands/base.py,sha256=GxUuDsDSpz0iXryy8MrEw88UM3C3yxL__kDK1QhshoA,2517
13
13
  tunacode/cli/commands/registry.py,sha256=XVuLpp5S4Fw7GfIZfLrVZFo4jMLMNmYNpYN7xWgXyOk,8223
@@ -19,7 +19,7 @@ tunacode/cli/commands/implementations/model.py,sha256=uthx6IX9KwgwywNTDklkJpqCba
19
19
  tunacode/cli/commands/implementations/system.py,sha256=2cGw5iCJO3aNhXTFF28CgAIyLgslvHmpfyL2ZHVB6oQ,7903
20
20
  tunacode/cli/commands/implementations/todo.py,sha256=Dtz5bgcuK2VXGPWEBBZQgnWUMYkRXNzTGf_qkVPLF2U,8125
21
21
  tunacode/configuration/__init__.py,sha256=MbVXy8bGu0yKehzgdgZ_mfWlYGvIdb1dY2Ly75nfuPE,17
22
- tunacode/configuration/defaults.py,sha256=Qny32BhbSeRBrE9baWZcAit3XFXgWuXFMDJUZudj55M,922
22
+ tunacode/configuration/defaults.py,sha256=lK_qf3BexmoQh7lbtxYG_ef0Kq3WyiLGOYmiVDO_amQ,840
23
23
  tunacode/configuration/models.py,sha256=buH8ZquvcYI3OQBDIZeJ08cu00rSCeNABtUwl3VQS0E,4103
24
24
  tunacode/configuration/settings.py,sha256=KoN0u6GG3Hh_TWt02D_wpRfbACYri3gCDTXHtJfHl2w,994
25
25
  tunacode/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -27,18 +27,16 @@ tunacode/core/code_index.py,sha256=jgAx3lSWP_DwnyiP5Jkm1YvX4JJyI4teMzlNrJSpEOA,1
27
27
  tunacode/core/state.py,sha256=2lsDgq0uIIc_MnXPE9SG_1fYFBDWWlgqgqm2Ik1iFBs,5599
28
28
  tunacode/core/tool_handler.py,sha256=BPjR013OOO0cLXPdLeL2FDK0ixUwOYu59FfHdcdFhp4,2277
29
29
  tunacode/core/agents/__init__.py,sha256=UUJiPYb91arwziSpjd7vIk7XNGA_4HQbsOIbskSqevA,149
30
- tunacode/core/agents/dspy_integration.py,sha256=h3gJ-qr0fXpB2CRaU-MVv_8xG--ah-8nra7WO960Gbo,9152
31
- tunacode/core/agents/dspy_tunacode.py,sha256=tIluqDsHAv3nbNYtqFh4oyZA9yqMrnyY2G-QOpkTLCs,17398
32
- tunacode/core/agents/main.py,sha256=xYzstmOJgxC1aJ2VbMWw5cevPp87IjpdWU45xydAKMQ,48882
30
+ tunacode/core/agents/main.py,sha256=BRy3joJnn4GpUW47PyhiKK1JLMSamlK7wDMLwAGzSG0,47441
33
31
  tunacode/core/agents/utils.py,sha256=7kJAiUlkyWO3-b4T07XsGgycVrcNhv3NEPLdaktBnP4,12847
34
32
  tunacode/core/background/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
33
  tunacode/core/background/manager.py,sha256=rJdl3eDLTQwjbT7VhxXcJbZopCNR3M8ZGMbmeVnwwMc,1126
36
34
  tunacode/core/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
35
  tunacode/core/recursive/__init__.py,sha256=S9_dN0faJqam3Pnaum9PRC8Hd90bpES8syFgAD8-QbI,446
38
- tunacode/core/recursive/aggregator.py,sha256=iKKKd_tZT1sXaPjpjRVVoCG8zFfuxilktSGUdalzTUM,15719
36
+ tunacode/core/recursive/aggregator.py,sha256=KlWajEyT8OokWeY6ZwQ4EsVu05V4KeH3o2cYUz-Ce1Y,15725
39
37
  tunacode/core/recursive/budget.py,sha256=0wY6xSrZKmudUwthwX1mlcF5yat2_y3fNu5shX_IzvA,14131
40
- tunacode/core/recursive/decomposer.py,sha256=7JbRPPXK083jipaaMSpnWIH4wtJC_yjhxBY-ZSkKqr0,14552
41
- tunacode/core/recursive/executor.py,sha256=5UcFPK57KP2WhtUeHrBPE3BxkKsZ7QxoMaSUHTYrsoE,16436
38
+ tunacode/core/recursive/decomposer.py,sha256=k8rgaPEMMb2mZ9W0m_Yg0oEBqbYpqDnK0zyTkx6eTPs,14550
39
+ tunacode/core/recursive/executor.py,sha256=7taFsjYjSfL8OUQLM1ZVn9Xix7YoputFBAZSo9tDtDM,16646
42
40
  tunacode/core/recursive/hierarchy.py,sha256=67tdDCKB5mTSn6VIwm-knEHB_VKUkeljFdwH62U5chE,15648
43
41
  tunacode/core/setup/__init__.py,sha256=lzdpY6rIGf9DDlDBDGFvQZaSOQeFsNglHbkpq1-GtU8,376
44
42
  tunacode/core/setup/agent_setup.py,sha256=trELO8cPnWo36BBnYmXDEnDPdhBg0p-VLnx9A8hSSSQ,1401
@@ -50,8 +48,6 @@ tunacode/core/setup/git_safety_setup.py,sha256=CRIqrQt0QUJQRS344njty_iCqTorrDhHl
50
48
  tunacode/core/token_usage/api_response_parser.py,sha256=CTtqGaFaxpkzkW3TEbe00QJzyRULpWN1EQxIYMleseg,1622
51
49
  tunacode/core/token_usage/cost_calculator.py,sha256=oQPMphGhqIt7NKdOg1o5Zbo59_nwFWmRJMQ30ViiCWs,1835
52
50
  tunacode/core/token_usage/usage_tracker.py,sha256=uKCpdZgmDfLauwsawSCifMu0kJE3lAnK7Sjd-0KYUgA,3894
53
- tunacode/prompts/dspy_task_planning.md,sha256=RNimkmnFcNgskwQrguGb3wB8A-Zngp6Qc9lXfPj61OU,2512
54
- tunacode/prompts/dspy_tool_selection.md,sha256=CPBHrI4QlWD2mzNdoVkFK7GydgCLW9Bi20sD8ZEysRo,3070
55
51
  tunacode/prompts/system.md,sha256=hXpjZ8Yiv2Acr2_6EmC2uOklP8FbmvyYR9oais-1KLk,16290
56
52
  tunacode/services/__init__.py,sha256=w_E8QK6RnvKSvU866eDe8BCRV26rAm4d3R-Yg06OWCU,19
57
53
  tunacode/services/mcp.py,sha256=R48X73KQjQ9vwhBrtbWHSBl-4K99QXmbIhh5J_1Gezo,3046
@@ -72,11 +68,11 @@ tunacode/ui/completers.py,sha256=Jx1zyCESwdm_4ZopvCBtb0bCJF-bRy8aBWG2yhPQtDc,487
72
68
  tunacode/ui/console.py,sha256=icb7uYrV8XmZg9glreEy5MrvDkmrKxbf_ZkNqElN1uE,2120
73
69
  tunacode/ui/constants.py,sha256=A76B_KpM8jCuBYRg4cPmhi8_j6LLyWttO7_jjv47r3w,421
74
70
  tunacode/ui/decorators.py,sha256=e2KM-_pI5EKHa2M045IjUe4rPkTboxaKHXJT0K3461g,1914
75
- tunacode/ui/input.py,sha256=E_zAJqNYoAVFA-j4xE9Qgs22y-GrdSZNqiseX-Or0ho,2955
76
- tunacode/ui/keybindings.py,sha256=h0MlD73CW_3i2dQzb9EFSPkqy0raZ_isgjxUiA9u6ts,691
71
+ tunacode/ui/input.py,sha256=x_7G9VVdvydpEk2kcyG-OBKND5lL5ADbiGcDXF1n5UA,3014
72
+ tunacode/ui/keybindings.py,sha256=tn0q0eRw72j8xPWX0673Xc-vmwlvFEyuhy3C451ntfE,1233
77
73
  tunacode/ui/lexers.py,sha256=tmg4ic1enyTRLzanN5QPP7D_0n12YjX_8ZhsffzhXA4,1340
78
74
  tunacode/ui/output.py,sha256=51O0VHajte4dXHK5Az5SSP4IOb2q5SbCwvqdAoxyg7c,5665
79
- tunacode/ui/panels.py,sha256=ckL-TYxYWlpBAFj8SC9Od8vrW7Kf5N92bZRYBWR14jE,8338
75
+ tunacode/ui/panels.py,sha256=dBZEVIJqliWreY-hz6HpW5rdBmPOJZ6sPrv8KQ3eNhk,8570
80
76
  tunacode/ui/prompt_manager.py,sha256=U2cntB34vm-YwOj3gzFRUK362zccrz8pigQfpxr5sv8,4650
81
77
  tunacode/ui/recursive_progress.py,sha256=V0dGpJWt19TVArOYcQ3Lki8cR3ZepFT6iGwnChSFhFI,12906
82
78
  tunacode/ui/tool_ui.py,sha256=qp1aZUpLO5UOdJziY8tw0URC8gjoWoSKdGu5y2wuTUU,7013
@@ -95,9 +91,9 @@ tunacode/utils/system.py,sha256=FSoibTIH0eybs4oNzbYyufIiV6gb77QaeY2yGqW39AY,1138
95
91
  tunacode/utils/text_utils.py,sha256=6YBD9QfkDO44-6jxnwRWIpmfIifPG-NqMzy_O2NAouc,7277
96
92
  tunacode/utils/token_counter.py,sha256=lLbkrNUraRQn5RMhwnGurqq1RHFDyn4AaFhruONWIxo,2690
97
93
  tunacode/utils/user_configuration.py,sha256=Ilz8dpGVJDBE2iLWHAPT0xR8D51VRKV3kIbsAz8Bboc,3275
98
- tunacode_cli-0.0.44.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
99
- tunacode_cli-0.0.44.dist-info/METADATA,sha256=MNphC8b7Fo9Q2kGx7pWgCkqM5k9dNaON0d02wKOu-9o,5203
100
- tunacode_cli-0.0.44.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
101
- tunacode_cli-0.0.44.dist-info/entry_points.txt,sha256=hbkytikj4dGu6rizPuAd_DGUPBGF191RTnhr9wdhORY,51
102
- tunacode_cli-0.0.44.dist-info/top_level.txt,sha256=lKy2P6BWNi5XSA4DHFvyjQ14V26lDZctwdmhEJrxQbU,9
103
- tunacode_cli-0.0.44.dist-info/RECORD,,
94
+ tunacode_cli-0.0.46.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
95
+ tunacode_cli-0.0.46.dist-info/METADATA,sha256=kO5Xol8ooArWqyfluRNjVq9t3MmZh9J02-ySYlpW_WA,5902
96
+ tunacode_cli-0.0.46.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
97
+ tunacode_cli-0.0.46.dist-info/entry_points.txt,sha256=hbkytikj4dGu6rizPuAd_DGUPBGF191RTnhr9wdhORY,51
98
+ tunacode_cli-0.0.46.dist-info/top_level.txt,sha256=lKy2P6BWNi5XSA4DHFvyjQ14V26lDZctwdmhEJrxQbU,9
99
+ tunacode_cli-0.0.46.dist-info/RECORD,,
@@ -1,223 +0,0 @@
1
- """DSPy Integration for TunaCode - Enhanced Tool Selection and Task Planning.
2
-
3
- This module integrates DSPy's optimized prompts and tool selection logic
4
- into TunaCode's agent system for 3x performance improvements.
5
- """
6
-
7
- import logging
8
- import os
9
- import re
10
- from pathlib import Path
11
- from typing import Any, Dict, List, Optional
12
-
13
- from tunacode.core.state import StateManager
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- class DSPyIntegration:
19
- """Integrates DSPy optimization into TunaCode's agent system."""
20
-
21
- def __init__(self, state_manager: StateManager):
22
- self.state_manager = state_manager
23
- self._dspy_agent = None
24
- self._tool_selection_prompt = None
25
- self._task_planning_prompt = None
26
- self._load_prompts()
27
-
28
- def _load_prompts(self):
29
- """Load DSPy-optimized prompts from files."""
30
- prompts_dir = Path(__file__).parent.parent.parent / "prompts"
31
-
32
- try:
33
- # Load tool selection prompt
34
- tool_selection_path = prompts_dir / "dspy_tool_selection.md"
35
- if tool_selection_path.exists():
36
- self._tool_selection_prompt = tool_selection_path.read_text(encoding="utf-8")
37
- logger.debug("Loaded DSPy tool selection prompt")
38
-
39
- # Load task planning prompt
40
- task_planning_path = prompts_dir / "dspy_task_planning.md"
41
- if task_planning_path.exists():
42
- self._task_planning_prompt = task_planning_path.read_text(encoding="utf-8")
43
- logger.debug("Loaded DSPy task planning prompt")
44
- except Exception as e:
45
- logger.error(f"Failed to load DSPy prompts: {e}")
46
-
47
- def get_dspy_agent(self, api_key: Optional[str] = None):
48
- """Get or create the DSPy agent instance."""
49
- if self._dspy_agent is None:
50
- try:
51
- # Import DSPy components
52
- from tunacode.core.agents.dspy_tunacode import create_optimized_agent
53
-
54
- # Use API key from environment or config
55
- if not api_key:
56
- api_key = os.getenv(
57
- "OPENROUTER_API_KEY"
58
- ) or self.state_manager.session.user_config.get("env", {}).get(
59
- "OPENROUTER_API_KEY"
60
- )
61
-
62
- if api_key:
63
- self._dspy_agent = create_optimized_agent(api_key)
64
- logger.info("DSPy agent initialized successfully")
65
- else:
66
- logger.warning("No OpenRouter API key found for DSPy optimization")
67
- except Exception as e:
68
- logger.error(f"Failed to initialize DSPy agent: {e}")
69
-
70
- return self._dspy_agent
71
-
72
- def enhance_system_prompt(self, base_prompt: str) -> str:
73
- """Enhance the system prompt with DSPy optimizations."""
74
- if not self._tool_selection_prompt:
75
- return base_prompt
76
-
77
- # Extract the learned patterns from DSPy prompts
78
- enhanced_sections = []
79
-
80
- # Add DSPy tool selection insights
81
- enhanced_sections.append("\n\n# DSPy-Optimized Tool Selection Patterns\n")
82
- enhanced_sections.append("**Based on learned optimization patterns:**\n")
83
- enhanced_sections.append("- Always batch 3-4 read-only tools for parallel execution")
84
- enhanced_sections.append("- Group grep, list_dir, glob, read_file operations together")
85
- enhanced_sections.append("- Execute write/modify operations sequentially")
86
- enhanced_sections.append("- Use Chain of Thought reasoning for tool selection\n")
87
-
88
- # Add specific examples from DSPy prompt
89
- if "Example" in self._tool_selection_prompt:
90
- enhanced_sections.append("\n## Optimal Tool Batching Examples:\n")
91
- # Extract examples section
92
- examples_match = re.search(
93
- r"### Example.*?(?=###|\Z)", self._tool_selection_prompt, re.DOTALL
94
- )
95
- if examples_match:
96
- enhanced_sections.append(examples_match.group(0))
97
-
98
- return base_prompt + "".join(enhanced_sections)
99
-
100
- def should_use_task_planner(self, user_request: str) -> bool:
101
- """Determine if the request is complex enough for task planning."""
102
- complex_indicators = [
103
- "implement",
104
- "create",
105
- "build",
106
- "refactor",
107
- "add feature",
108
- "fix all",
109
- "update multiple",
110
- "migrate",
111
- "integrate",
112
- "debug",
113
- "optimize performance",
114
- "authentication",
115
- "setup",
116
- ]
117
-
118
- request_lower = user_request.lower()
119
-
120
- # Check for multiple files
121
- file_pattern = r"\b\w+\.\w+\b"
122
- files_mentioned = len(re.findall(file_pattern, user_request)) > 2
123
-
124
- # Check for complex keywords
125
- has_complex_keyword = any(indicator in request_lower for indicator in complex_indicators)
126
-
127
- # Check for multiple operations
128
- operation_words = ["and", "then", "also", "after", "before", "plus"]
129
- has_multiple_ops = sum(1 for word in operation_words if word in request_lower) >= 2
130
-
131
- return files_mentioned or has_complex_keyword or has_multiple_ops
132
-
133
- def optimize_tool_selection(
134
- self, user_request: str, tools_to_execute: List[Dict[str, Any]]
135
- ) -> List[List[Dict[str, Any]]]:
136
- """Optimize tool selection using DSPy patterns.
137
-
138
- Returns tool calls organized in optimal batches for parallel execution.
139
- """
140
- if not tools_to_execute:
141
- return []
142
-
143
- # Try to use DSPy agent if available
144
- dspy_agent = self.get_dspy_agent()
145
- if dspy_agent:
146
- try:
147
- result = dspy_agent(user_request, self.state_manager.session.cwd or ".")
148
- if hasattr(result, "tool_batches") and result.tool_batches:
149
- return result.tool_batches
150
- except Exception as e:
151
- logger.debug(f"DSPy optimization failed, using fallback: {e}")
152
-
153
- # Fallback: Apply DSPy-learned patterns manually
154
- return self._apply_dspy_patterns(tools_to_execute)
155
-
156
- def _apply_dspy_patterns(self, tools: List[Dict[str, Any]]) -> List[List[Dict[str, Any]]]:
157
- """Apply DSPy-learned batching patterns manually."""
158
- from tunacode.constants import READ_ONLY_TOOLS
159
-
160
- batches = []
161
- current_batch = []
162
-
163
- for tool in tools:
164
- tool_name = tool.get("tool", "")
165
-
166
- if tool_name in READ_ONLY_TOOLS:
167
- current_batch.append(tool)
168
- # Optimal batch size is 3-4 tools
169
- if len(current_batch) >= 4:
170
- batches.append(current_batch)
171
- current_batch = []
172
- else:
173
- # Flush current batch if any
174
- if current_batch:
175
- batches.append(current_batch)
176
- current_batch = []
177
- # Add write/execute tool as single batch
178
- batches.append([tool])
179
-
180
- # Add remaining tools
181
- if current_batch:
182
- batches.append(current_batch)
183
-
184
- return batches
185
-
186
- def get_task_breakdown(self, complex_request: str) -> Optional[Dict[str, Any]]:
187
- """Get task breakdown for complex requests using DSPy."""
188
- dspy_agent = self.get_dspy_agent()
189
- if not dspy_agent:
190
- return None
191
-
192
- try:
193
- result = dspy_agent(complex_request, self.state_manager.session.cwd or ".")
194
- if result.get("is_complex") and result.get("subtasks"):
195
- return {
196
- "subtasks": result["subtasks"],
197
- "total_tool_calls": result.get("total_tool_calls", 0),
198
- "requires_todo": result.get("requires_todo", False),
199
- "parallelization_opportunities": result.get("parallelization_opportunities", 0),
200
- }
201
- except Exception as e:
202
- logger.error(f"Failed to get task breakdown: {e}")
203
-
204
- return None
205
-
206
- def format_chain_of_thought(self, request: str, tools: List[str]) -> str:
207
- """Format a Chain of Thought reasoning for tool selection."""
208
- reasoning = f"Let's think step by step about '{request}':\n"
209
-
210
- if "search" in request.lower() or "find" in request.lower():
211
- reasoning += "1. This requires searching for information\n"
212
- reasoning += "2. I'll use grep for content search and glob for file patterns\n"
213
- reasoning += "3. These read-only tools can be executed in parallel\n"
214
- elif "read" in request.lower() or "show" in request.lower():
215
- reasoning += "1. This requires reading file contents\n"
216
- reasoning += "2. I'll batch multiple read_file operations together\n"
217
- reasoning += "3. All reads can happen in parallel for speed\n"
218
- elif "fix" in request.lower() or "update" in request.lower():
219
- reasoning += "1. First, I need to understand the current state\n"
220
- reasoning += "2. I'll search and read relevant files in parallel\n"
221
- reasoning += "3. Then make modifications sequentially for safety\n"
222
-
223
- return reasoning