tunacode-cli 0.0.40__py3-none-any.whl → 0.0.41__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.
- tunacode/cli/commands/__init__.py +2 -0
- tunacode/cli/commands/implementations/__init__.py +3 -0
- tunacode/cli/commands/implementations/debug.py +1 -1
- tunacode/cli/commands/implementations/todo.py +217 -0
- tunacode/cli/commands/registry.py +2 -0
- tunacode/cli/main.py +12 -5
- tunacode/cli/repl.py +197 -132
- tunacode/configuration/defaults.py +1 -0
- tunacode/configuration/models.py +6 -0
- tunacode/constants.py +22 -3
- tunacode/context.py +7 -3
- tunacode/core/agents/main.py +52 -9
- tunacode/core/setup/config_setup.py +5 -0
- tunacode/core/state.py +50 -1
- tunacode/core/token_usage/api_response_parser.py +44 -0
- tunacode/core/token_usage/cost_calculator.py +58 -0
- tunacode/core/token_usage/usage_tracker.py +98 -0
- tunacode/prompts/system.md +69 -5
- tunacode/tools/todo.py +343 -0
- tunacode/types.py +20 -1
- tunacode/ui/input.py +1 -1
- tunacode/ui/output.py +36 -0
- tunacode/utils/message_utils.py +17 -0
- tunacode/utils/token_counter.py +78 -8
- {tunacode_cli-0.0.40.dist-info → tunacode_cli-0.0.41.dist-info}/METADATA +2 -1
- {tunacode_cli-0.0.40.dist-info → tunacode_cli-0.0.41.dist-info}/RECORD +30 -26
- tunacode/cli/textual_app.py +0 -420
- tunacode/cli/textual_bridge.py +0 -161
- {tunacode_cli-0.0.40.dist-info → tunacode_cli-0.0.41.dist-info}/WHEEL +0 -0
- {tunacode_cli-0.0.40.dist-info → tunacode_cli-0.0.41.dist-info}/entry_points.txt +0 -0
- {tunacode_cli-0.0.40.dist-info → tunacode_cli-0.0.41.dist-info}/licenses/LICENSE +0 -0
- {tunacode_cli-0.0.40.dist-info → tunacode_cli-0.0.41.dist-info}/top_level.txt +0 -0
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
tunacode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
tunacode/constants.py,sha256=
|
|
3
|
-
tunacode/context.py,sha256=
|
|
2
|
+
tunacode/constants.py,sha256=gBTHjFJIlYIwg_85bzrFLBJFwJ10a6TUnyES7eBMZjI,4993
|
|
3
|
+
tunacode/context.py,sha256=_gXVCyjU052jlyRAl9tklZSwl5U_zI_EIX8XN87VVWE,2786
|
|
4
4
|
tunacode/exceptions.py,sha256=mTWXuWyr1k16CGLWN2tsthDGi7lbx1JK0ekIqogYDP8,3105
|
|
5
5
|
tunacode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
tunacode/setup.py,sha256=XPt4eAK-qcIZQv64jGZ_ryxcImDwps9OmXjJfIS1xcs,1899
|
|
7
|
-
tunacode/types.py,sha256=
|
|
7
|
+
tunacode/types.py,sha256=Czq7jYXHq7fZQtyqkCN5_7eEu1wyYUcB50C6v3sTWDw,8188
|
|
8
8
|
tunacode/cli/__init__.py,sha256=zgs0UbAck8hfvhYsWhWOfBe5oK09ug2De1r4RuQZREA,55
|
|
9
|
-
tunacode/cli/main.py,sha256=
|
|
10
|
-
tunacode/cli/repl.py,sha256=
|
|
11
|
-
tunacode/cli/
|
|
12
|
-
tunacode/cli/textual_bridge.py,sha256=LvqiTtF0hu3gNujzpKaW9h-m6xzEP3OH2M8KL2pCwRc,6333
|
|
13
|
-
tunacode/cli/commands/__init__.py,sha256=YMrLz7szrmseJCRZGGX6_TyO3dJU8_QDCOFEhRAztzo,1634
|
|
9
|
+
tunacode/cli/main.py,sha256=ypefhvSt9hXzNOv0WpR-PlkUOSGadvcFbUIRT13n9oo,2619
|
|
10
|
+
tunacode/cli/repl.py,sha256=Sg9qDbT9lVs4brWiLhHhGH8mt_5EQf9MdwgvhS1KbuI,18333
|
|
11
|
+
tunacode/cli/commands/__init__.py,sha256=zmE9JcJ9Qd2xJhgdS4jMDJOoZsrAZmL5MAFxbKkk7F8,1670
|
|
14
12
|
tunacode/cli/commands/base.py,sha256=GxUuDsDSpz0iXryy8MrEw88UM3C3yxL__kDK1QhshoA,2517
|
|
15
|
-
tunacode/cli/commands/registry.py,sha256=
|
|
16
|
-
tunacode/cli/commands/implementations/__init__.py,sha256=
|
|
13
|
+
tunacode/cli/commands/registry.py,sha256=XVuLpp5S4Fw7GfIZfLrVZFo4jMLMNmYNpYN7xWgXyOk,8223
|
|
14
|
+
tunacode/cli/commands/implementations/__init__.py,sha256=lMgLZRX9hnw-ftZu4ykqoJoHqkZ5Yu0lBvYuzHylm7Q,986
|
|
17
15
|
tunacode/cli/commands/implementations/conversation.py,sha256=EsnsZB6yyVI_sbNNMvk37tCz3iAj4E85R9ev696qeqg,4683
|
|
18
|
-
tunacode/cli/commands/implementations/debug.py,sha256=
|
|
16
|
+
tunacode/cli/commands/implementations/debug.py,sha256=hWr9DOIS-kn8z89IJZ6HuRkyN1tOsnFZg5qlB8YPbG8,6763
|
|
19
17
|
tunacode/cli/commands/implementations/development.py,sha256=kZRdVgReVmGU0uijFxtPio2RYkTrYMufOwgI1Aj1_NU,2729
|
|
20
18
|
tunacode/cli/commands/implementations/model.py,sha256=uthx6IX9KwgwywNTDklkJpqCbaTX9h1_p-eVmqL73WQ,2245
|
|
21
19
|
tunacode/cli/commands/implementations/system.py,sha256=2cGw5iCJO3aNhXTFF28CgAIyLgslvHmpfyL2ZHVB6oQ,7903
|
|
20
|
+
tunacode/cli/commands/implementations/todo.py,sha256=Dtz5bgcuK2VXGPWEBBZQgnWUMYkRXNzTGf_qkVPLF2U,8125
|
|
22
21
|
tunacode/configuration/__init__.py,sha256=MbVXy8bGu0yKehzgdgZ_mfWlYGvIdb1dY2Ly75nfuPE,17
|
|
23
|
-
tunacode/configuration/defaults.py,sha256=
|
|
24
|
-
tunacode/configuration/models.py,sha256=
|
|
22
|
+
tunacode/configuration/defaults.py,sha256=lK_qf3BexmoQh7lbtxYG_ef0Kq3WyiLGOYmiVDO_amQ,840
|
|
23
|
+
tunacode/configuration/models.py,sha256=buH8ZquvcYI3OQBDIZeJ08cu00rSCeNABtUwl3VQS0E,4103
|
|
25
24
|
tunacode/configuration/settings.py,sha256=KoN0u6GG3Hh_TWt02D_wpRfbACYri3gCDTXHtJfHl2w,994
|
|
26
25
|
tunacode/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
26
|
tunacode/core/code_index.py,sha256=jgAx3lSWP_DwnyiP5Jkm1YvX4JJyI4teMzlNrJSpEOA,15661
|
|
28
|
-
tunacode/core/state.py,sha256=
|
|
27
|
+
tunacode/core/state.py,sha256=hOLuvojJksd3GEezNT6jLPAsCcI-mZKldUUvlP4IKjw,3641
|
|
29
28
|
tunacode/core/tool_handler.py,sha256=BPjR013OOO0cLXPdLeL2FDK0ixUwOYu59FfHdcdFhp4,2277
|
|
30
29
|
tunacode/core/agents/__init__.py,sha256=UUJiPYb91arwziSpjd7vIk7XNGA_4HQbsOIbskSqevA,149
|
|
31
|
-
tunacode/core/agents/main.py,sha256=
|
|
30
|
+
tunacode/core/agents/main.py,sha256=xDHA9lqs089jNmTOdIX9W9BEOh28HbilZtyyOYqHNGQ,43101
|
|
32
31
|
tunacode/core/agents/utils.py,sha256=VaNsPB2l1dAP-VlS_QLRKvCb4NW0pXNRoxkh12AGXAg,10744
|
|
33
32
|
tunacode/core/background/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
33
|
tunacode/core/background/manager.py,sha256=rJdl3eDLTQwjbT7VhxXcJbZopCNR3M8ZGMbmeVnwwMc,1126
|
|
@@ -36,11 +35,14 @@ tunacode/core/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
36
35
|
tunacode/core/setup/__init__.py,sha256=lzdpY6rIGf9DDlDBDGFvQZaSOQeFsNglHbkpq1-GtU8,376
|
|
37
36
|
tunacode/core/setup/agent_setup.py,sha256=trELO8cPnWo36BBnYmXDEnDPdhBg0p-VLnx9A8hSSSQ,1401
|
|
38
37
|
tunacode/core/setup/base.py,sha256=cbyT2-xK2mWgH4EO17VfM_OM2bj0kT895NW2jSXbe3c,968
|
|
39
|
-
tunacode/core/setup/config_setup.py,sha256=
|
|
38
|
+
tunacode/core/setup/config_setup.py,sha256=ctf9GuN7niqCsRK5rbEzCbzsN0MrAOeWzKtwOnyJJmY,14654
|
|
40
39
|
tunacode/core/setup/coordinator.py,sha256=oVTN2xIeJERXitVJpkIk9tDGLs1D1bxIRmaogJwZJFI,2049
|
|
41
40
|
tunacode/core/setup/environment_setup.py,sha256=n3IrObKEynHZSwtUJ1FddMg2C4sHz7ca42awemImV8s,2225
|
|
42
41
|
tunacode/core/setup/git_safety_setup.py,sha256=CRIqrQt0QUJQRS344njty_iCqTorrDhHlXRuET7w0Tk,6714
|
|
43
|
-
tunacode/
|
|
42
|
+
tunacode/core/token_usage/api_response_parser.py,sha256=CTtqGaFaxpkzkW3TEbe00QJzyRULpWN1EQxIYMleseg,1622
|
|
43
|
+
tunacode/core/token_usage/cost_calculator.py,sha256=oQPMphGhqIt7NKdOg1o5Zbo59_nwFWmRJMQ30ViiCWs,1835
|
|
44
|
+
tunacode/core/token_usage/usage_tracker.py,sha256=uKCpdZgmDfLauwsawSCifMu0kJE3lAnK7Sjd-0KYUgA,3894
|
|
45
|
+
tunacode/prompts/system.md,sha256=hXpjZ8Yiv2Acr2_6EmC2uOklP8FbmvyYR9oais-1KLk,16290
|
|
44
46
|
tunacode/services/__init__.py,sha256=w_E8QK6RnvKSvU866eDe8BCRV26rAm4d3R-Yg06OWCU,19
|
|
45
47
|
tunacode/services/mcp.py,sha256=R48X73KQjQ9vwhBrtbWHSBl-4K99QXmbIhh5J_1Gezo,3046
|
|
46
48
|
tunacode/tools/__init__.py,sha256=ECBuUWWF1JjHW42CCceaPKgVTQyuljbz3RlhuA2fe2s,314
|
|
@@ -52,6 +54,7 @@ tunacode/tools/list_dir.py,sha256=1kNqzYCNlcA5rqXIEVqcjQy6QxlLZLj5AG6YIECfwio,72
|
|
|
52
54
|
tunacode/tools/read_file.py,sha256=z2omev9xzj4-0GG9mRssD13rj-Aa1c-pszFi2Z7Hxvk,3268
|
|
53
55
|
tunacode/tools/read_file_async_poc.py,sha256=2v2ckLQlwahgPGWGdE2c5Es37B35Y7zWdseZwT46E1E,6453
|
|
54
56
|
tunacode/tools/run_command.py,sha256=7UvXjFQI1Av4vceXx48MbQCTrsFNj4PlygTAAhNDYIA,4376
|
|
57
|
+
tunacode/tools/todo.py,sha256=bVbohgwKqvvTe8efxXrMZDQU8vdk4E3jF9Cj38dRq7k,12727
|
|
55
58
|
tunacode/tools/update_file.py,sha256=bW1MhTzRjBDjJzqQ6A1yCVEbkr1oIqtEC8uqcg_rfY4,3957
|
|
56
59
|
tunacode/tools/write_file.py,sha256=prL6u8XOi9ZyPU-YNlG9YMLbSLrDJXDRuDX73ncXh-k,2699
|
|
57
60
|
tunacode/ui/__init__.py,sha256=aRNE2pS50nFAX6y--rSGMNYwhz905g14gRd6g4BolYU,13
|
|
@@ -59,10 +62,10 @@ tunacode/ui/completers.py,sha256=Jx1zyCESwdm_4ZopvCBtb0bCJF-bRy8aBWG2yhPQtDc,487
|
|
|
59
62
|
tunacode/ui/console.py,sha256=YXNFlnV7n4wyaIy-VohzIMJJ71C7fzgcjuLheNIO-QU,2079
|
|
60
63
|
tunacode/ui/constants.py,sha256=A76B_KpM8jCuBYRg4cPmhi8_j6LLyWttO7_jjv47r3w,421
|
|
61
64
|
tunacode/ui/decorators.py,sha256=e2KM-_pI5EKHa2M045IjUe4rPkTboxaKHXJT0K3461g,1914
|
|
62
|
-
tunacode/ui/input.py,sha256=
|
|
65
|
+
tunacode/ui/input.py,sha256=E_zAJqNYoAVFA-j4xE9Qgs22y-GrdSZNqiseX-Or0ho,2955
|
|
63
66
|
tunacode/ui/keybindings.py,sha256=h0MlD73CW_3i2dQzb9EFSPkqy0raZ_isgjxUiA9u6ts,691
|
|
64
67
|
tunacode/ui/lexers.py,sha256=tmg4ic1enyTRLzanN5QPP7D_0n12YjX_8ZhsffzhXA4,1340
|
|
65
|
-
tunacode/ui/output.py,sha256=
|
|
68
|
+
tunacode/ui/output.py,sha256=9BnjBa9iCFqMK7FleGyAjVIi0FVIlO2ku5RXPj2Gn2s,5549
|
|
66
69
|
tunacode/ui/panels.py,sha256=IZpiWBb7jVXaycH5BPAnqTCs2-_ccJYq2V55MxkVHzQ,8199
|
|
67
70
|
tunacode/ui/prompt_manager.py,sha256=U2cntB34vm-YwOj3gzFRUK362zccrz8pigQfpxr5sv8,4650
|
|
68
71
|
tunacode/ui/tool_ui.py,sha256=S5-k1HwRlSqiQ8shGQ_QYGXQbuzb6Pg7u3CTqZwffdQ,6533
|
|
@@ -73,15 +76,16 @@ tunacode/utils/bm25.py,sha256=yq7KFWP3g_zIsjUV7l2hFPXYCzXyNQUInLU7u4qsc_4,1909
|
|
|
73
76
|
tunacode/utils/diff_utils.py,sha256=V9QqQ0q4MfabVTnWptF3IXDp3estnfOKcJtDe_Sj14I,2372
|
|
74
77
|
tunacode/utils/file_utils.py,sha256=AXiAJ_idtlmXEi9pMvwtfPy9Ys3yK-F4K7qb_NpwonU,923
|
|
75
78
|
tunacode/utils/import_cache.py,sha256=q_xjJbtju05YbFopLDSkIo1hOtCx3DOTl3GQE5FFDgs,295
|
|
79
|
+
tunacode/utils/message_utils.py,sha256=kM6VSS2Dudjplie009khHgmIRjDoBUzv6tvHcYNDAAE,586
|
|
76
80
|
tunacode/utils/ripgrep.py,sha256=AXUs2FFt0A7KBV996deS8wreIlUzKOlAHJmwrcAr4No,583
|
|
77
81
|
tunacode/utils/security.py,sha256=e_zo9VmcOKFjgFMr9GOBIFhAmND4PBlJZgY7zqnsGjI,6548
|
|
78
82
|
tunacode/utils/system.py,sha256=FSoibTIH0eybs4oNzbYyufIiV6gb77QaeY2yGqW39AY,11381
|
|
79
83
|
tunacode/utils/text_utils.py,sha256=6YBD9QfkDO44-6jxnwRWIpmfIifPG-NqMzy_O2NAouc,7277
|
|
80
|
-
tunacode/utils/token_counter.py,sha256=
|
|
84
|
+
tunacode/utils/token_counter.py,sha256=l5KemYLfsypAtAF_YrDtVKFtBEghydS_MA8c-8mpPvM,2721
|
|
81
85
|
tunacode/utils/user_configuration.py,sha256=Ilz8dpGVJDBE2iLWHAPT0xR8D51VRKV3kIbsAz8Bboc,3275
|
|
82
|
-
tunacode_cli-0.0.
|
|
83
|
-
tunacode_cli-0.0.
|
|
84
|
-
tunacode_cli-0.0.
|
|
85
|
-
tunacode_cli-0.0.
|
|
86
|
-
tunacode_cli-0.0.
|
|
87
|
-
tunacode_cli-0.0.
|
|
86
|
+
tunacode_cli-0.0.41.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
|
|
87
|
+
tunacode_cli-0.0.41.dist-info/METADATA,sha256=kkUdWvJ3JjXnCCF_Jr1zdgi9lSYEo8CJuibfVNau_U0,5137
|
|
88
|
+
tunacode_cli-0.0.41.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
89
|
+
tunacode_cli-0.0.41.dist-info/entry_points.txt,sha256=hbkytikj4dGu6rizPuAd_DGUPBGF191RTnhr9wdhORY,51
|
|
90
|
+
tunacode_cli-0.0.41.dist-info/top_level.txt,sha256=lKy2P6BWNi5XSA4DHFvyjQ14V26lDZctwdmhEJrxQbU,9
|
|
91
|
+
tunacode_cli-0.0.41.dist-info/RECORD,,
|
tunacode/cli/textual_app.py
DELETED
|
@@ -1,420 +0,0 @@
|
|
|
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 textual import on
|
|
15
|
-
from textual.app import App, ComposeResult
|
|
16
|
-
from textual.binding import Binding
|
|
17
|
-
from textual.containers import Container, Horizontal, Vertical, VerticalScroll
|
|
18
|
-
from textual.message import Message
|
|
19
|
-
from textual.widgets import Button, Footer, Header, Static, TextArea
|
|
20
|
-
|
|
21
|
-
from tunacode.core.state import StateManager
|
|
22
|
-
from tunacode.setup import setup
|
|
23
|
-
from tunacode.utils.system import check_for_updates
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class ChatMessage(Static):
|
|
27
|
-
"""A single chat message widget."""
|
|
28
|
-
|
|
29
|
-
def __init__(self, sender: str, content: str, message_type: str = "user"):
|
|
30
|
-
super().__init__()
|
|
31
|
-
self.sender = sender
|
|
32
|
-
self.content = content
|
|
33
|
-
self.message_type = message_type
|
|
34
|
-
|
|
35
|
-
def compose(self) -> ComposeResult:
|
|
36
|
-
"""Compose the chat message."""
|
|
37
|
-
if self.message_type == "user":
|
|
38
|
-
yield Static(f"[bold cyan]❯ You[/bold cyan]\n{self.content}", classes="user-message")
|
|
39
|
-
elif self.message_type == "agent":
|
|
40
|
-
yield Static(
|
|
41
|
-
f"[bold green]🤖 TunaCode[/bold green]\n{self.content}", classes="agent-message"
|
|
42
|
-
)
|
|
43
|
-
elif self.message_type == "system":
|
|
44
|
-
yield Static(
|
|
45
|
-
f"[bold yellow]⚠️ System[/bold yellow]\n{self.content}", classes="system-message"
|
|
46
|
-
)
|
|
47
|
-
elif self.message_type == "tool":
|
|
48
|
-
yield Static(
|
|
49
|
-
f"[bold magenta]🔧 Tool[/bold magenta]\n{self.content}", classes="tool-message"
|
|
50
|
-
)
|
|
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", "Welcome to TunaCode v0.11 - Your AI-powered development assistant", "system"
|
|
242
|
-
)
|
|
243
|
-
self.chat_history.add_message(
|
|
244
|
-
"System", f"Current model: {self.state_manager.session.current_model}", "system"
|
|
245
|
-
)
|
|
246
|
-
self.chat_history.add_message(
|
|
247
|
-
"System",
|
|
248
|
-
"⚠️ IMPORTANT: Always use git branches before making major changes\n"
|
|
249
|
-
"Type '/help' for available commands",
|
|
250
|
-
"system",
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
@on(InputArea.SendMessage)
|
|
254
|
-
async def handle_message(self, message: InputArea.SendMessage) -> None:
|
|
255
|
-
"""Handle incoming messages from the input area."""
|
|
256
|
-
content = message.content
|
|
257
|
-
|
|
258
|
-
# Add user message to chat
|
|
259
|
-
self.chat_history.add_message("You", content, "user")
|
|
260
|
-
|
|
261
|
-
# Update status
|
|
262
|
-
status_widget = self.sidebar.query_one("#status", Static)
|
|
263
|
-
status_widget.update("● Processing...")
|
|
264
|
-
status_widget.classes = "status-busy"
|
|
265
|
-
|
|
266
|
-
if content.startswith("/"):
|
|
267
|
-
await self.handle_command(content)
|
|
268
|
-
else:
|
|
269
|
-
await self.handle_user_input(content)
|
|
270
|
-
|
|
271
|
-
# Reset status
|
|
272
|
-
status_widget.update("● Ready")
|
|
273
|
-
status_widget.classes = "status-ready"
|
|
274
|
-
|
|
275
|
-
async def handle_command(self, command: str) -> None:
|
|
276
|
-
"""Handle slash commands."""
|
|
277
|
-
if command == "/help":
|
|
278
|
-
help_text = """Available Commands:
|
|
279
|
-
|
|
280
|
-
/help - Show this help message
|
|
281
|
-
/clear - Clear chat history
|
|
282
|
-
/model - Show current model info
|
|
283
|
-
/yolo - Toggle confirmation prompts
|
|
284
|
-
/quit - Exit the application
|
|
285
|
-
|
|
286
|
-
Keyboard Shortcuts:
|
|
287
|
-
Ctrl+C - Quit
|
|
288
|
-
Ctrl+L - Clear chat
|
|
289
|
-
F1 - Help
|
|
290
|
-
F2 - Model info
|
|
291
|
-
Ctrl+Enter - Send message"""
|
|
292
|
-
self.chat_history.add_message("System", help_text, "system")
|
|
293
|
-
|
|
294
|
-
elif command == "/clear":
|
|
295
|
-
await self.action_clear_chat()
|
|
296
|
-
|
|
297
|
-
elif command == "/model":
|
|
298
|
-
model_info = f"Current model: {self.state_manager.session.current_model}"
|
|
299
|
-
self.chat_history.add_message("System", model_info, "system")
|
|
300
|
-
|
|
301
|
-
elif command == "/yolo":
|
|
302
|
-
# Toggle yolo mode
|
|
303
|
-
current_state = getattr(self.state_manager.session, "yolo_mode", False)
|
|
304
|
-
self.state_manager.session.yolo_mode = not current_state
|
|
305
|
-
new_state = "enabled" if not current_state else "disabled"
|
|
306
|
-
self.chat_history.add_message("System", f"Confirmation prompts {new_state}", "system")
|
|
307
|
-
|
|
308
|
-
elif command == "/quit":
|
|
309
|
-
await self.action_quit()
|
|
310
|
-
|
|
311
|
-
else:
|
|
312
|
-
self.chat_history.add_message("System", f"Unknown command: {command}", "system")
|
|
313
|
-
|
|
314
|
-
async def handle_user_input(self, text: str) -> None:
|
|
315
|
-
"""Handle regular user input."""
|
|
316
|
-
try:
|
|
317
|
-
# Use the bridge to process the input
|
|
318
|
-
if not hasattr(self, "_bridge"):
|
|
319
|
-
from tunacode.cli.textual_bridge import TextualAgentBridge
|
|
320
|
-
|
|
321
|
-
self._bridge = TextualAgentBridge(self.state_manager, self._bridge_message_callback)
|
|
322
|
-
|
|
323
|
-
# Process the request
|
|
324
|
-
response = await self._bridge.process_user_input(text)
|
|
325
|
-
|
|
326
|
-
# Add the agent's response to chat
|
|
327
|
-
self.chat_history.add_message("TunaCode", response, "agent")
|
|
328
|
-
|
|
329
|
-
except Exception as e:
|
|
330
|
-
self.chat_history.add_message("System", f"Error: {str(e)}", "system")
|
|
331
|
-
|
|
332
|
-
async def _bridge_message_callback(self, message_type: str, content: str) -> None:
|
|
333
|
-
"""Callback for bridge to send messages to the UI."""
|
|
334
|
-
self.chat_history.add_message("System", content, message_type)
|
|
335
|
-
|
|
336
|
-
def action_clear_chat(self) -> None:
|
|
337
|
-
"""Clear the chat history."""
|
|
338
|
-
self.chat_history.remove_children()
|
|
339
|
-
self.chat_history.add_message("System", "Chat cleared", "system")
|
|
340
|
-
|
|
341
|
-
def action_help(self) -> None:
|
|
342
|
-
"""Show help information."""
|
|
343
|
-
self.handle_command("/help")
|
|
344
|
-
|
|
345
|
-
def action_model_info(self) -> None:
|
|
346
|
-
"""Show model information."""
|
|
347
|
-
self.handle_command("/model")
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
async def run_textual_app(state_manager: StateManager) -> None:
|
|
351
|
-
"""Run the Textual application."""
|
|
352
|
-
app = TunaCodeApp(state_manager)
|
|
353
|
-
await app.run_async()
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
def main():
|
|
357
|
-
"""Main entry point for the Textual app."""
|
|
358
|
-
import sys
|
|
359
|
-
|
|
360
|
-
# Handle command line arguments
|
|
361
|
-
version_flag = "--version" in sys.argv or "-v" in sys.argv
|
|
362
|
-
if version_flag:
|
|
363
|
-
from tunacode.constants import APP_VERSION
|
|
364
|
-
|
|
365
|
-
print(f"TunaCode v{APP_VERSION}")
|
|
366
|
-
return
|
|
367
|
-
|
|
368
|
-
# Initialize state manager
|
|
369
|
-
state_manager = StateManager()
|
|
370
|
-
# Show banner
|
|
371
|
-
print("🐟 TunaCode - Modern AI Development Assistant")
|
|
372
|
-
print("=" * 50)
|
|
373
|
-
|
|
374
|
-
# Check for updates
|
|
375
|
-
has_update, latest_version = check_for_updates()
|
|
376
|
-
if has_update:
|
|
377
|
-
print(f"📦 Update available: v{latest_version}")
|
|
378
|
-
print("Run: pip install --upgrade tunacode-cli")
|
|
379
|
-
print()
|
|
380
|
-
|
|
381
|
-
# Parse CLI arguments for configuration
|
|
382
|
-
cli_config = {}
|
|
383
|
-
args = sys.argv[1:]
|
|
384
|
-
i = 0
|
|
385
|
-
while i < len(args):
|
|
386
|
-
if args[i] == "--model" and i + 1 < len(args):
|
|
387
|
-
cli_config["model"] = args[i + 1]
|
|
388
|
-
i += 2
|
|
389
|
-
elif args[i] == "--key" and i + 1 < len(args):
|
|
390
|
-
cli_config["key"] = args[i + 1]
|
|
391
|
-
i += 2
|
|
392
|
-
elif args[i] == "--baseurl" and i + 1 < len(args):
|
|
393
|
-
cli_config["baseurl"] = args[i + 1]
|
|
394
|
-
i += 2
|
|
395
|
-
elif args[i] == "--setup":
|
|
396
|
-
cli_config["setup"] = True
|
|
397
|
-
i += 1
|
|
398
|
-
else:
|
|
399
|
-
i += 1
|
|
400
|
-
|
|
401
|
-
async def run_app():
|
|
402
|
-
try:
|
|
403
|
-
# Run setup
|
|
404
|
-
run_setup = cli_config.get("setup", False)
|
|
405
|
-
await setup(run_setup, state_manager, cli_config)
|
|
406
|
-
|
|
407
|
-
# Run the Textual app
|
|
408
|
-
await run_textual_app(state_manager)
|
|
409
|
-
|
|
410
|
-
except KeyboardInterrupt:
|
|
411
|
-
print("\n👋 Goodbye!")
|
|
412
|
-
except Exception as e:
|
|
413
|
-
print(f"❌ Error: {e}")
|
|
414
|
-
|
|
415
|
-
# Run the async app
|
|
416
|
-
asyncio.run(run_app())
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
if __name__ == "__main__":
|
|
420
|
-
main()
|