tunacode-cli 0.0.9__py3-none-any.whl → 0.0.11__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.
- tunacode/cli/commands.py +34 -165
- tunacode/cli/main.py +15 -38
- tunacode/cli/repl.py +24 -18
- tunacode/configuration/defaults.py +1 -1
- tunacode/configuration/models.py +4 -11
- tunacode/configuration/settings.py +10 -3
- tunacode/constants.py +6 -4
- tunacode/context.py +3 -1
- tunacode/core/agents/main.py +94 -52
- tunacode/core/setup/agent_setup.py +1 -1
- tunacode/core/setup/config_setup.py +161 -81
- tunacode/core/setup/coordinator.py +4 -2
- tunacode/core/setup/environment_setup.py +1 -1
- tunacode/core/setup/git_safety_setup.py +51 -39
- tunacode/exceptions.py +2 -0
- tunacode/prompts/system.txt +1 -1
- tunacode/services/undo_service.py +16 -13
- tunacode/setup.py +6 -2
- tunacode/tools/base.py +20 -11
- tunacode/tools/update_file.py +14 -24
- tunacode/tools/write_file.py +7 -9
- tunacode/ui/completers.py +33 -98
- tunacode/ui/input.py +9 -13
- tunacode/ui/keybindings.py +3 -1
- tunacode/ui/lexers.py +17 -16
- tunacode/ui/output.py +8 -14
- tunacode/ui/panels.py +7 -5
- tunacode/ui/prompt_manager.py +4 -8
- tunacode/ui/tool_ui.py +3 -3
- tunacode/utils/system.py +0 -40
- tunacode_cli-0.0.11.dist-info/METADATA +387 -0
- tunacode_cli-0.0.11.dist-info/RECORD +65 -0
- {tunacode_cli-0.0.9.dist-info → tunacode_cli-0.0.11.dist-info}/licenses/LICENSE +1 -1
- tunacode/cli/model_selector.py +0 -178
- tunacode/core/agents/tinyagent_main.py +0 -194
- tunacode/core/setup/optimized_coordinator.py +0 -73
- tunacode/services/enhanced_undo_service.py +0 -322
- tunacode/services/project_undo_service.py +0 -311
- tunacode/tools/tinyagent_tools.py +0 -103
- tunacode/utils/lazy_imports.py +0 -59
- tunacode/utils/regex_cache.py +0 -33
- tunacode_cli-0.0.9.dist-info/METADATA +0 -321
- tunacode_cli-0.0.9.dist-info/RECORD +0 -73
- {tunacode_cli-0.0.9.dist-info → tunacode_cli-0.0.11.dist-info}/WHEEL +0 -0
- {tunacode_cli-0.0.9.dist-info → tunacode_cli-0.0.11.dist-info}/entry_points.txt +0 -0
- {tunacode_cli-0.0.9.dist-info → tunacode_cli-0.0.11.dist-info}/top_level.txt +0 -0
tunacode/ui/completers.py
CHANGED
|
@@ -10,117 +10,54 @@ from ..cli.commands import CommandRegistry
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class CommandCompleter(Completer):
|
|
13
|
-
"""Completer for slash commands
|
|
14
|
-
|
|
13
|
+
"""Completer for slash commands."""
|
|
14
|
+
|
|
15
15
|
def __init__(self, command_registry: Optional[CommandRegistry] = None):
|
|
16
16
|
self.command_registry = command_registry
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
|
|
19
18
|
def get_completions(
|
|
20
19
|
self, document: Document, complete_event: CompleteEvent
|
|
21
20
|
) -> Iterable[Completion]:
|
|
22
|
-
"""Get completions for slash commands
|
|
21
|
+
"""Get completions for slash commands."""
|
|
23
22
|
# Get the text before cursor
|
|
24
23
|
text = document.text_before_cursor
|
|
25
|
-
|
|
26
|
-
# Check if we're completing model names after /model or /m command
|
|
27
|
-
if self._should_complete_model_names(text):
|
|
28
|
-
yield from self._get_model_completions(document, text)
|
|
29
|
-
return
|
|
30
|
-
|
|
24
|
+
|
|
31
25
|
# Check if we're at the start of a line or after whitespace
|
|
32
|
-
if text and not text.isspace() and text[-1] !=
|
|
26
|
+
if text and not text.isspace() and text[-1] != '\n':
|
|
33
27
|
# Only complete commands at the start of input or after a newline
|
|
34
|
-
last_newline = text.rfind(
|
|
35
|
-
line_start = text[last_newline + 1
|
|
36
|
-
|
|
28
|
+
last_newline = text.rfind('\n')
|
|
29
|
+
line_start = text[last_newline + 1:] if last_newline >= 0 else text
|
|
30
|
+
|
|
37
31
|
# Skip if not at the beginning of a line
|
|
38
|
-
if line_start and not line_start.startswith(
|
|
32
|
+
if line_start and not line_start.startswith('/'):
|
|
39
33
|
return
|
|
40
|
-
|
|
34
|
+
|
|
41
35
|
# Get the word before cursor
|
|
42
36
|
word_before_cursor = document.get_word_before_cursor(WORD=True)
|
|
43
|
-
|
|
37
|
+
|
|
44
38
|
# Only complete if word starts with /
|
|
45
|
-
if not word_before_cursor.startswith(
|
|
39
|
+
if not word_before_cursor.startswith('/'):
|
|
46
40
|
return
|
|
47
|
-
|
|
41
|
+
|
|
48
42
|
# Get command names from registry
|
|
49
43
|
if self.command_registry:
|
|
50
44
|
command_names = self.command_registry.get_command_names()
|
|
51
45
|
else:
|
|
52
46
|
# Fallback list of commands
|
|
53
|
-
command_names = [
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"/dump",
|
|
57
|
-
"/yolo",
|
|
58
|
-
"/undo",
|
|
59
|
-
"/branch",
|
|
60
|
-
"/compact",
|
|
61
|
-
"/model",
|
|
62
|
-
"/m",
|
|
63
|
-
"/init",
|
|
64
|
-
]
|
|
65
|
-
|
|
47
|
+
command_names = ['/help', '/clear', '/dump', '/yolo', '/undo',
|
|
48
|
+
'/branch', '/compact', '/model']
|
|
49
|
+
|
|
66
50
|
# Get the partial command (without /)
|
|
67
51
|
partial = word_before_cursor[1:].lower()
|
|
68
|
-
|
|
52
|
+
|
|
69
53
|
# Yield completions for matching commands
|
|
70
54
|
for cmd in command_names:
|
|
71
|
-
if cmd.startswith(
|
|
55
|
+
if cmd.startswith('/') and cmd[1:].lower().startswith(partial):
|
|
72
56
|
yield Completion(
|
|
73
57
|
text=cmd,
|
|
74
58
|
start_position=-len(word_before_cursor),
|
|
75
59
|
display=cmd,
|
|
76
|
-
display_meta=
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
def _should_complete_model_names(self, text: str) -> bool:
|
|
80
|
-
"""Check if we should complete model names."""
|
|
81
|
-
# Look for /model or /m command followed by space
|
|
82
|
-
import re
|
|
83
|
-
|
|
84
|
-
pattern = r"(?:^|\n)\s*(?:/model|/m)\s+\S*$"
|
|
85
|
-
return bool(re.search(pattern, text))
|
|
86
|
-
|
|
87
|
-
def _get_model_completions(self, document: Document, text: str) -> Iterable[Completion]:
|
|
88
|
-
"""Get completions for model names."""
|
|
89
|
-
# Lazy import and cache
|
|
90
|
-
if self._model_selector is None:
|
|
91
|
-
try:
|
|
92
|
-
from ..cli.model_selector import ModelSelector
|
|
93
|
-
|
|
94
|
-
self._model_selector = ModelSelector()
|
|
95
|
-
except ImportError:
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
# Get the partial model name
|
|
99
|
-
word_before_cursor = document.get_word_before_cursor(WORD=True)
|
|
100
|
-
partial = word_before_cursor.lower()
|
|
101
|
-
|
|
102
|
-
# Yield model short names and indices
|
|
103
|
-
seen = set()
|
|
104
|
-
for i, model in enumerate(self._model_selector.models):
|
|
105
|
-
# Complete by index
|
|
106
|
-
index_str = str(i)
|
|
107
|
-
if index_str.startswith(partial) and index_str not in seen:
|
|
108
|
-
seen.add(index_str)
|
|
109
|
-
yield Completion(
|
|
110
|
-
text=index_str,
|
|
111
|
-
start_position=-len(word_before_cursor),
|
|
112
|
-
display=f"{index_str} - {model.display_name}",
|
|
113
|
-
display_meta=model.provider.value[2],
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
# Complete by short name
|
|
117
|
-
if model.short_name.lower().startswith(partial) and model.short_name not in seen:
|
|
118
|
-
seen.add(model.short_name)
|
|
119
|
-
yield Completion(
|
|
120
|
-
text=model.short_name,
|
|
121
|
-
start_position=-len(word_before_cursor),
|
|
122
|
-
display=f"{model.short_name} - {model.display_name}",
|
|
123
|
-
display_meta=model.provider.value[2],
|
|
60
|
+
display_meta='command'
|
|
124
61
|
)
|
|
125
62
|
|
|
126
63
|
|
|
@@ -133,14 +70,14 @@ class FileReferenceCompleter(Completer):
|
|
|
133
70
|
"""Get completions for @file references."""
|
|
134
71
|
# Get the word before cursor
|
|
135
72
|
word_before_cursor = document.get_word_before_cursor(WORD=True)
|
|
136
|
-
|
|
73
|
+
|
|
137
74
|
# Check if we're in an @file reference
|
|
138
75
|
if not word_before_cursor.startswith("@"):
|
|
139
76
|
return
|
|
140
|
-
|
|
77
|
+
|
|
141
78
|
# Get the path part after @
|
|
142
79
|
path_part = word_before_cursor[1:] # Remove @
|
|
143
|
-
|
|
80
|
+
|
|
144
81
|
# Determine directory and prefix
|
|
145
82
|
if "/" in path_part:
|
|
146
83
|
# Path includes directory
|
|
@@ -150,18 +87,18 @@ class FileReferenceCompleter(Completer):
|
|
|
150
87
|
# Just filename, search in current directory
|
|
151
88
|
dir_path = "."
|
|
152
89
|
prefix = path_part
|
|
153
|
-
|
|
90
|
+
|
|
154
91
|
# Get matching files
|
|
155
92
|
try:
|
|
156
93
|
if os.path.exists(dir_path) and os.path.isdir(dir_path):
|
|
157
94
|
for item in sorted(os.listdir(dir_path)):
|
|
158
95
|
if item.startswith(prefix):
|
|
159
96
|
full_path = os.path.join(dir_path, item) if dir_path != "." else item
|
|
160
|
-
|
|
97
|
+
|
|
161
98
|
# Skip hidden files unless explicitly requested
|
|
162
99
|
if item.startswith(".") and not prefix.startswith("."):
|
|
163
100
|
continue
|
|
164
|
-
|
|
101
|
+
|
|
165
102
|
# Add / for directories
|
|
166
103
|
if os.path.isdir(full_path):
|
|
167
104
|
display = item + "/"
|
|
@@ -169,15 +106,15 @@ class FileReferenceCompleter(Completer):
|
|
|
169
106
|
else:
|
|
170
107
|
display = item
|
|
171
108
|
completion = full_path
|
|
172
|
-
|
|
109
|
+
|
|
173
110
|
# Calculate how much to replace
|
|
174
111
|
start_position = -len(path_part)
|
|
175
|
-
|
|
112
|
+
|
|
176
113
|
yield Completion(
|
|
177
114
|
text=completion,
|
|
178
115
|
start_position=start_position,
|
|
179
116
|
display=display,
|
|
180
|
-
display_meta="dir" if os.path.isdir(full_path) else "file"
|
|
117
|
+
display_meta="dir" if os.path.isdir(full_path) else "file"
|
|
181
118
|
)
|
|
182
119
|
except (OSError, PermissionError):
|
|
183
120
|
# Silently ignore inaccessible directories
|
|
@@ -186,9 +123,7 @@ class FileReferenceCompleter(Completer):
|
|
|
186
123
|
|
|
187
124
|
def create_completer(command_registry: Optional[CommandRegistry] = None) -> Completer:
|
|
188
125
|
"""Create a merged completer for both commands and file references."""
|
|
189
|
-
return merge_completers(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
]
|
|
194
|
-
)
|
|
126
|
+
return merge_completers([
|
|
127
|
+
CommandCompleter(command_registry),
|
|
128
|
+
FileReferenceCompleter(),
|
|
129
|
+
])
|
tunacode/ui/input.py
CHANGED
|
@@ -4,9 +4,10 @@ from typing import Optional
|
|
|
4
4
|
|
|
5
5
|
from prompt_toolkit.formatted_text import HTML
|
|
6
6
|
from prompt_toolkit.key_binding import KeyBindings
|
|
7
|
+
from prompt_toolkit.styles import Style
|
|
7
8
|
from prompt_toolkit.validation import Validator
|
|
8
9
|
|
|
9
|
-
from tunacode.constants import UI_PROMPT_PREFIX
|
|
10
|
+
from tunacode.constants import UI_COLORS, UI_PROMPT_PREFIX
|
|
10
11
|
from tunacode.core.state import StateManager
|
|
11
12
|
|
|
12
13
|
from .completers import create_completer
|
|
@@ -32,7 +33,6 @@ async def input(
|
|
|
32
33
|
lexer=None,
|
|
33
34
|
timeoutlen: float = 0.05,
|
|
34
35
|
state_manager: Optional[StateManager] = None,
|
|
35
|
-
default: str = "",
|
|
36
36
|
) -> str:
|
|
37
37
|
"""
|
|
38
38
|
Prompt for user input using simplified prompt management.
|
|
@@ -49,7 +49,6 @@ async def input(
|
|
|
49
49
|
lexer: Optional lexer for syntax highlighting
|
|
50
50
|
timeoutlen: Timeout length for input
|
|
51
51
|
state_manager: The state manager for session storage
|
|
52
|
-
default: Default value to pre-fill in the input
|
|
53
52
|
|
|
54
53
|
Returns:
|
|
55
54
|
User input string
|
|
@@ -64,7 +63,6 @@ async def input(
|
|
|
64
63
|
completer=completer,
|
|
65
64
|
lexer=lexer,
|
|
66
65
|
timeoutlen=timeoutlen,
|
|
67
|
-
default=default,
|
|
68
66
|
)
|
|
69
67
|
|
|
70
68
|
# Create prompt manager
|
|
@@ -74,26 +72,24 @@ async def input(
|
|
|
74
72
|
return await manager.get_input(session_key, pretext, config)
|
|
75
73
|
|
|
76
74
|
|
|
77
|
-
async def multiline_input(
|
|
78
|
-
state_manager: Optional[StateManager] = None, command_registry=None
|
|
79
|
-
) -> str:
|
|
75
|
+
async def multiline_input(state_manager: Optional[StateManager] = None, command_registry=None) -> str:
|
|
80
76
|
"""Get multiline input from the user with @file completion and highlighting."""
|
|
81
77
|
kb = create_key_bindings()
|
|
82
78
|
placeholder = formatted_text(
|
|
83
79
|
(
|
|
84
80
|
"<darkgrey>"
|
|
85
|
-
"<bold>Enter</bold> to submit
|
|
86
|
-
"<bold>Esc + Enter</bold> for new line
|
|
81
|
+
"<bold>Enter</bold> to submit • "
|
|
82
|
+
"<bold>Esc + Enter</bold> for new line • "
|
|
87
83
|
"<bold>/help</bold> for commands"
|
|
88
84
|
"</darkgrey>"
|
|
89
85
|
)
|
|
90
86
|
)
|
|
91
87
|
return await input(
|
|
92
|
-
"multiline",
|
|
93
|
-
key_bindings=kb,
|
|
94
|
-
multiline=True,
|
|
88
|
+
"multiline",
|
|
89
|
+
key_bindings=kb,
|
|
90
|
+
multiline=True,
|
|
95
91
|
placeholder=placeholder,
|
|
96
92
|
completer=create_completer(command_registry),
|
|
97
93
|
lexer=FileReferenceLexer(),
|
|
98
|
-
state_manager=state_manager
|
|
94
|
+
state_manager=state_manager
|
|
99
95
|
)
|
tunacode/ui/keybindings.py
CHANGED
|
@@ -7,6 +7,8 @@ def create_key_bindings() -> KeyBindings:
|
|
|
7
7
|
"""Create and configure key bindings for the UI."""
|
|
8
8
|
kb = KeyBindings()
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
|
|
10
12
|
@kb.add("enter")
|
|
11
13
|
def _submit(event):
|
|
12
14
|
"""Submit the current buffer."""
|
|
@@ -16,7 +18,7 @@ def create_key_bindings() -> KeyBindings:
|
|
|
16
18
|
def _newline(event):
|
|
17
19
|
"""Insert a newline character."""
|
|
18
20
|
event.current_buffer.insert_text("\n")
|
|
19
|
-
|
|
21
|
+
|
|
20
22
|
@kb.add("escape", "enter")
|
|
21
23
|
def _escape_enter(event):
|
|
22
24
|
"""Insert a newline when escape then enter is pressed."""
|
tunacode/ui/lexers.py
CHANGED
|
@@ -2,44 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
+
from prompt_toolkit.formatted_text import FormattedText
|
|
5
6
|
from prompt_toolkit.lexers import Lexer
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class FileReferenceLexer(Lexer):
|
|
9
10
|
"""Lexer that highlights @file references in light blue."""
|
|
10
|
-
|
|
11
|
+
|
|
11
12
|
# Pattern to match @file references
|
|
12
|
-
FILE_REF_PATTERN = re.compile(r
|
|
13
|
-
|
|
13
|
+
FILE_REF_PATTERN = re.compile(r'@([\w./_-]+)')
|
|
14
|
+
|
|
14
15
|
def lex_document(self, document):
|
|
15
16
|
"""Return a formatted text list for the given document."""
|
|
16
|
-
lines = document.text.split(
|
|
17
|
-
|
|
17
|
+
lines = document.text.split('\n')
|
|
18
|
+
|
|
18
19
|
def get_line_tokens(line_number):
|
|
19
20
|
"""Get tokens for a specific line."""
|
|
20
21
|
if line_number >= len(lines):
|
|
21
22
|
return []
|
|
22
|
-
|
|
23
|
+
|
|
23
24
|
line = lines[line_number]
|
|
24
25
|
tokens = []
|
|
25
26
|
last_end = 0
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
# Find all @file references in the line
|
|
28
29
|
for match in self.FILE_REF_PATTERN.finditer(line):
|
|
29
30
|
start, end = match.span()
|
|
30
|
-
|
|
31
|
+
|
|
31
32
|
# Add text before the match
|
|
32
33
|
if start > last_end:
|
|
33
|
-
tokens.append((
|
|
34
|
-
|
|
34
|
+
tokens.append(('', line[last_end:start]))
|
|
35
|
+
|
|
35
36
|
# Add the @file reference with styling
|
|
36
|
-
tokens.append((
|
|
37
|
+
tokens.append(('class:file-reference', match.group(0)))
|
|
37
38
|
last_end = end
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
# Add remaining text
|
|
40
41
|
if last_end < len(line):
|
|
41
|
-
tokens.append((
|
|
42
|
-
|
|
42
|
+
tokens.append(('', line[last_end:]))
|
|
43
|
+
|
|
43
44
|
return tokens
|
|
44
|
-
|
|
45
|
-
return get_line_tokens
|
|
45
|
+
|
|
46
|
+
return get_line_tokens
|
tunacode/ui/output.py
CHANGED
|
@@ -16,15 +16,9 @@ from .decorators import create_sync_wrapper
|
|
|
16
16
|
console = Console()
|
|
17
17
|
colors = DotDict(UI_COLORS)
|
|
18
18
|
|
|
19
|
-
BANNER =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"[bold #00d7ff]│[/bold #00d7ff] [bold white]T U N A C O D E[/bold white] "
|
|
23
|
-
"[dim #64748b]• Agentic AI Development Environment[/dim #64748b] "
|
|
24
|
-
"[bold #00d7ff]│[/bold #00d7ff]\n"
|
|
25
|
-
"[bold #00d7ff]└─────────────────────────────────────────────────────────────────┘"
|
|
26
|
-
"[/bold #00d7ff]"
|
|
27
|
-
)
|
|
19
|
+
BANNER = """[bold #00d7ff]╭─────────────────────────────────────────────────────────────────╮[/bold #00d7ff]
|
|
20
|
+
[bold #00d7ff]│[/bold #00d7ff] [bold white]T U N A C O D E[/bold white] [dim #64748b]• Agentic AI Development Environment[/dim #64748b] [bold #00d7ff]│[/bold #00d7ff]
|
|
21
|
+
[bold #00d7ff]╰─────────────────────────────────────────────────────────────────╯[/bold #00d7ff]"""
|
|
28
22
|
|
|
29
23
|
|
|
30
24
|
@create_sync_wrapper
|
|
@@ -34,8 +28,8 @@ async def print(message, **kwargs) -> None:
|
|
|
34
28
|
|
|
35
29
|
|
|
36
30
|
async def line() -> None:
|
|
37
|
-
"""Print
|
|
38
|
-
await
|
|
31
|
+
"""Print an empty line for spacing."""
|
|
32
|
+
await print("")
|
|
39
33
|
|
|
40
34
|
|
|
41
35
|
async def info(text: str) -> None:
|
|
@@ -53,9 +47,9 @@ async def warning(text: str) -> None:
|
|
|
53
47
|
await print(f"[{colors.warning}]⚠[/{colors.warning}] {text}")
|
|
54
48
|
|
|
55
49
|
|
|
56
|
-
async def muted(text: str
|
|
57
|
-
"""Print
|
|
58
|
-
await print(
|
|
50
|
+
async def muted(text: str) -> None:
|
|
51
|
+
"""Print muted text."""
|
|
52
|
+
await print(text, style=colors.muted)
|
|
59
53
|
|
|
60
54
|
|
|
61
55
|
async def usage(usage: str) -> None:
|
tunacode/ui/panels.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Optional, Union
|
|
4
4
|
|
|
5
|
+
from rich.box import ROUNDED
|
|
5
6
|
from rich.markdown import Markdown
|
|
6
7
|
from rich.padding import Padding
|
|
7
8
|
from rich.panel import Panel
|
|
@@ -38,11 +39,12 @@ async def panel(
|
|
|
38
39
|
"""Display a rich panel with modern styling."""
|
|
39
40
|
border_style = border_style or kwargs.get("style") or colors.border
|
|
40
41
|
panel_obj = Panel(
|
|
41
|
-
Padding(text, (0, 1, 0, 1)),
|
|
42
|
-
title=f"[bold]{title}[/bold]",
|
|
43
|
-
title_align="left",
|
|
42
|
+
Padding(text, (0, 1, 0, 1)),
|
|
43
|
+
title=f"[bold]{title}[/bold]",
|
|
44
|
+
title_align="left",
|
|
44
45
|
border_style=border_style,
|
|
45
46
|
padding=(0, 1),
|
|
47
|
+
box=ROUNDED # Use ROUNDED box style
|
|
46
48
|
)
|
|
47
49
|
await print(Padding(panel_obj, (top, right, bottom, left)), **kwargs)
|
|
48
50
|
|
|
@@ -83,8 +85,8 @@ async def models(state_manager: StateManager = None) -> None:
|
|
|
83
85
|
|
|
84
86
|
async def help(command_registry=None) -> None:
|
|
85
87
|
"""Display the available commands organized by category."""
|
|
86
|
-
table = Table(show_header=False, box=None, padding=(0,
|
|
87
|
-
table.add_column("Command", style=f"bold {colors.primary}", justify="right", min_width=
|
|
88
|
+
table = Table(show_header=False, box=None, padding=(0, 2, 0, 0))
|
|
89
|
+
table.add_column("Command", style=f"bold {colors.primary}", justify="right", min_width=18)
|
|
88
90
|
table.add_column("Description", style=colors.muted)
|
|
89
91
|
|
|
90
92
|
if command_registry:
|
tunacode/ui/prompt_manager.py
CHANGED
|
@@ -28,7 +28,6 @@ class PromptConfig:
|
|
|
28
28
|
completer: Optional[Completer] = None
|
|
29
29
|
lexer: Optional[Lexer] = None
|
|
30
30
|
timeoutlen: float = 0.05
|
|
31
|
-
default: str = ""
|
|
32
31
|
|
|
33
32
|
|
|
34
33
|
class PromptManager:
|
|
@@ -43,14 +42,12 @@ class PromptManager:
|
|
|
43
42
|
self.state_manager = state_manager
|
|
44
43
|
self._temp_sessions = {} # For when no state manager is available
|
|
45
44
|
self._style = self._create_style()
|
|
46
|
-
|
|
45
|
+
|
|
47
46
|
def _create_style(self) -> Style:
|
|
48
47
|
"""Create the style for the prompt with file reference highlighting."""
|
|
49
|
-
return Style.from_dict(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
)
|
|
48
|
+
return Style.from_dict({
|
|
49
|
+
'file-reference': UI_COLORS.get('file_ref', 'light_blue'),
|
|
50
|
+
})
|
|
54
51
|
|
|
55
52
|
def get_session(self, session_key: str, config: PromptConfig) -> PromptSession:
|
|
56
53
|
"""Get or create a prompt session.
|
|
@@ -108,7 +105,6 @@ class PromptManager:
|
|
|
108
105
|
is_password=config.is_password,
|
|
109
106
|
validator=config.validator,
|
|
110
107
|
multiline=config.multiline,
|
|
111
|
-
default=config.default,
|
|
112
108
|
)
|
|
113
109
|
|
|
114
110
|
# Clean up response
|
tunacode/ui/tool_ui.py
CHANGED
|
@@ -154,9 +154,9 @@ class ToolUI:
|
|
|
154
154
|
if request.filepath:
|
|
155
155
|
ui.console.print(f"File: {request.filepath}", style=self.colors.muted)
|
|
156
156
|
|
|
157
|
-
ui.console.print(" 1
|
|
158
|
-
ui.console.print(" 2
|
|
159
|
-
ui.console.print(f" 3
|
|
157
|
+
ui.console.print(" [1] Yes (default)")
|
|
158
|
+
ui.console.print(" [2] Yes, and don't ask again for commands like this")
|
|
159
|
+
ui.console.print(f" [3] No, and tell {APP_NAME} what to do differently")
|
|
160
160
|
resp = input(" Choose an option [1/2/3]: ").strip() or "1"
|
|
161
161
|
|
|
162
162
|
# Add spacing after user choice for better readability
|
tunacode/utils/system.py
CHANGED
|
@@ -277,46 +277,6 @@ def check_for_updates():
|
|
|
277
277
|
return False, current_version
|
|
278
278
|
|
|
279
279
|
|
|
280
|
-
async def update_tunacode():
|
|
281
|
-
"""
|
|
282
|
-
Update TunaCode to the latest version using pip.
|
|
283
|
-
"""
|
|
284
|
-
from ..ui import console as ui
|
|
285
|
-
|
|
286
|
-
await ui.info("🔄 Checking for updates...")
|
|
287
|
-
|
|
288
|
-
has_update, latest_version = check_for_updates()
|
|
289
|
-
|
|
290
|
-
if not has_update:
|
|
291
|
-
await ui.success("✅ TunaCode is already up to date!")
|
|
292
|
-
return
|
|
293
|
-
|
|
294
|
-
app_settings = ApplicationSettings()
|
|
295
|
-
current_version = app_settings.version
|
|
296
|
-
|
|
297
|
-
await ui.info(f"📦 Updating from v{current_version} to v{latest_version}...")
|
|
298
|
-
|
|
299
|
-
try:
|
|
300
|
-
# Run pip install --upgrade tunacode-cli
|
|
301
|
-
result = subprocess.run(
|
|
302
|
-
["pip", "install", "--upgrade", "tunacode-cli"],
|
|
303
|
-
capture_output=True,
|
|
304
|
-
text=True,
|
|
305
|
-
check=True
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
if result.returncode == 0:
|
|
309
|
-
await ui.success(f"🎉 Successfully updated to v{latest_version}!")
|
|
310
|
-
await ui.info("Please restart TunaCode to use the new version.")
|
|
311
|
-
else:
|
|
312
|
-
await ui.error(f"❌ Update failed: {result.stderr}")
|
|
313
|
-
|
|
314
|
-
except subprocess.CalledProcessError as e:
|
|
315
|
-
await ui.error(f"❌ Update failed: {e.stderr}")
|
|
316
|
-
except Exception as e:
|
|
317
|
-
await ui.error(f"❌ Update failed: {str(e)}")
|
|
318
|
-
|
|
319
|
-
|
|
320
280
|
def list_cwd(max_depth=3):
|
|
321
281
|
"""
|
|
322
282
|
Lists files in the current working directory up to a specified depth,
|