gemcode 0.3.16__tar.gz → 0.3.17__tar.gz
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.
- {gemcode-0.3.16/src/gemcode.egg-info → gemcode-0.3.17}/PKG-INFO +2 -1
- {gemcode-0.3.16 → gemcode-0.3.17}/pyproject.toml +2 -1
- gemcode-0.3.17/src/gemcode/tui/input_handler.py +182 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tui/scrollback.py +20 -4
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tui/welcome_rich.py +6 -1
- {gemcode-0.3.16 → gemcode-0.3.17/src/gemcode.egg-info}/PKG-INFO +2 -1
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode.egg-info/SOURCES.txt +1 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode.egg-info/requires.txt +1 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/LICENSE +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/MANIFEST.in +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/README.md +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/setup.cfg +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/agent.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/cli.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/config.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/invoke.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/kairos_daemon.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/repl_commands.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/repl_slash.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tools/__init__.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/version.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/web/claude_sse_adapter.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_claude_web_adapter_sse.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_credentials.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_kairos_scheduler.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_paths.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_permissions.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_tools.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.16 → gemcode-0.3.17}/tests/test_workspace_hints.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gemcode
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17
|
|
4
4
|
Summary: Local-first coding agent on Google Gemini + ADK
|
|
5
5
|
Author: GemCode Contributors
|
|
6
6
|
License: Apache License
|
|
@@ -179,6 +179,7 @@ Requires-Dist: google-adk>=1.0.0
|
|
|
179
179
|
Requires-Dist: google-genai>=1.0.0
|
|
180
180
|
Requires-Dist: python-dotenv>=1.0.0
|
|
181
181
|
Requires-Dist: rich>=13.0.0
|
|
182
|
+
Requires-Dist: prompt_toolkit>=3.0.0
|
|
182
183
|
Provides-Extra: dev
|
|
183
184
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
184
185
|
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gemcode"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.17"
|
|
8
8
|
description = "Local-first coding agent on Google Gemini + ADK"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -31,6 +31,7 @@ dependencies = [
|
|
|
31
31
|
"google-genai>=1.0.0",
|
|
32
32
|
"python-dotenv>=1.0.0",
|
|
33
33
|
"rich>=13.0.0",
|
|
34
|
+
"prompt_toolkit>=3.0.0",
|
|
34
35
|
]
|
|
35
36
|
|
|
36
37
|
[project.urls]
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interactive input handler for the GemCode REPL.
|
|
3
|
+
|
|
4
|
+
Provides (when prompt_toolkit is available):
|
|
5
|
+
- Slash command autocomplete — type / and see all commands with descriptions;
|
|
6
|
+
use ↑ / ↓ arrows to navigate the list, Tab or Enter to accept, Esc to dismiss.
|
|
7
|
+
- Conversation history — ↑ / ↓ arrows cycle through previous messages (when
|
|
8
|
+
the completion popup is not open).
|
|
9
|
+
- Status bar — shows the active model and session ID while typing.
|
|
10
|
+
- Styled ❯ prompt in GemCode blue.
|
|
11
|
+
|
|
12
|
+
Falls back gracefully to plain input() when prompt_toolkit is unavailable or
|
|
13
|
+
stdout is not a TTY (e.g. piped input, CI).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
from typing import Callable
|
|
20
|
+
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
# Availability guard — import everything inside try so we never hard-crash
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
_PT_AVAILABLE = False
|
|
25
|
+
try:
|
|
26
|
+
from prompt_toolkit import PromptSession
|
|
27
|
+
from prompt_toolkit.completion import Completer, Completion
|
|
28
|
+
from prompt_toolkit.document import Document # noqa: F401 (type hint only)
|
|
29
|
+
from prompt_toolkit.formatted_text import HTML
|
|
30
|
+
from prompt_toolkit.history import InMemoryHistory
|
|
31
|
+
from prompt_toolkit.key_binding import KeyBindings
|
|
32
|
+
from prompt_toolkit.styles import Style
|
|
33
|
+
|
|
34
|
+
_PT_AVAILABLE = True
|
|
35
|
+
except ImportError:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
# Slash command registry
|
|
40
|
+
# (name without /, description shown in the autocomplete popup)
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
SLASH_COMMANDS: list[tuple[str, str]] = [
|
|
43
|
+
("help", "List all available commands"),
|
|
44
|
+
("clear", "Start a fresh session — clears conversation history"),
|
|
45
|
+
("model", "View or switch model · /model use <id> · /model list"),
|
|
46
|
+
("status", "Show session ID, model, and current settings"),
|
|
47
|
+
("context", "Show context window usage and token counts"),
|
|
48
|
+
("tools", "List all tools and their permission categories"),
|
|
49
|
+
("compact", "Summarise conversation history to reclaim context space"),
|
|
50
|
+
("config", "Show active configuration and environment variables"),
|
|
51
|
+
("permissions", "Show current permission mode (default / strict / yes)"),
|
|
52
|
+
("audit", "Show recent audit log · /audit [N lines]"),
|
|
53
|
+
("memory", "Show memory / storage settings"),
|
|
54
|
+
("doctor", "Run diagnostics and validate the environment"),
|
|
55
|
+
("hooks", "Show hooks configuration"),
|
|
56
|
+
("version", "Show installed GemCode version"),
|
|
57
|
+
("exit", "Exit GemCode"),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
# Completer
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
if _PT_AVAILABLE:
|
|
65
|
+
class _SlashCommandCompleter(Completer):
|
|
66
|
+
"""
|
|
67
|
+
Activates when the input starts with '/'.
|
|
68
|
+
Supports prefix matching and substring matching so typing '/mo' shows
|
|
69
|
+
/model, '/compact', etc.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def get_completions(self, document, complete_event):
|
|
73
|
+
text = document.text_before_cursor
|
|
74
|
+
if not text.startswith("/"):
|
|
75
|
+
return
|
|
76
|
+
query = text[1:].lower()
|
|
77
|
+
for name, desc in SLASH_COMMANDS:
|
|
78
|
+
if not query or name.startswith(query) or query in name:
|
|
79
|
+
yield Completion(
|
|
80
|
+
"/" + name,
|
|
81
|
+
start_position=-len(text),
|
|
82
|
+
display=HTML(f"<ansiblue>/<b>{name}</b></ansiblue>"),
|
|
83
|
+
display_meta=desc,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
# Public class
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
class GemCodeInputHandler:
|
|
91
|
+
"""
|
|
92
|
+
Wraps prompt_toolkit PromptSession with GemCode-specific styling and slash
|
|
93
|
+
command autocomplete. Use ``await handler.prompt_async()`` in the async
|
|
94
|
+
REPL loop.
|
|
95
|
+
|
|
96
|
+
When prompt_toolkit is not installed (or stdout is not a TTY) every call
|
|
97
|
+
transparently delegates to ``input()``.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
def __init__(
|
|
101
|
+
self,
|
|
102
|
+
*,
|
|
103
|
+
ansi_enabled: bool = True,
|
|
104
|
+
get_model: Callable[[], str] | None = None,
|
|
105
|
+
get_session_id: Callable[[], str] | None = None,
|
|
106
|
+
) -> None:
|
|
107
|
+
self._ansi = ansi_enabled
|
|
108
|
+
self._get_model = get_model or (lambda: "gemini")
|
|
109
|
+
self._get_session_id = get_session_id or (lambda: "")
|
|
110
|
+
self._session: "PromptSession | None" = None
|
|
111
|
+
|
|
112
|
+
if _PT_AVAILABLE and sys.stdin.isatty() and sys.stdout.isatty():
|
|
113
|
+
self._build_session()
|
|
114
|
+
|
|
115
|
+
def _build_session(self) -> None:
|
|
116
|
+
style = Style.from_dict(
|
|
117
|
+
{
|
|
118
|
+
# The ❯ prompt glyph
|
|
119
|
+
"prompt": "#5fafd7 bold",
|
|
120
|
+
# Completion popup rows
|
|
121
|
+
"completion-menu.completion": "bg:#0d2035 fg:#87cefa",
|
|
122
|
+
"completion-menu.completion.current": "bg:#0087d7 fg:#ffffff bold",
|
|
123
|
+
# Meta (description) column
|
|
124
|
+
"completion-menu.meta.completion": "bg:#0d2035 fg:#5a7a9a",
|
|
125
|
+
"completion-menu.meta.completion.current": "bg:#0065a0 fg:#cce8ff",
|
|
126
|
+
"completion-menu.border": "#0087d7",
|
|
127
|
+
# Bottom toolbar
|
|
128
|
+
"bottom-toolbar": "bg:#0d1f2d fg:#4a7a9a",
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
get_model = self._get_model
|
|
133
|
+
get_session_id = self._get_session_id
|
|
134
|
+
|
|
135
|
+
def bottom_toolbar() -> HTML:
|
|
136
|
+
model = get_model() or "gemini"
|
|
137
|
+
sid = get_session_id()
|
|
138
|
+
sid_short = sid[:8] if len(sid) >= 8 else sid
|
|
139
|
+
return HTML(
|
|
140
|
+
f'<style bg="#0d1f2d" fg="#5fafd7"><b> ◆ {model}</b></style>'
|
|
141
|
+
f'<style bg="#0d1f2d" fg="#3d6080"> · session {sid_short}'
|
|
142
|
+
f' · / for commands</style>'
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
kb = KeyBindings()
|
|
146
|
+
|
|
147
|
+
@kb.add("c-c")
|
|
148
|
+
def _ctrl_c(event):
|
|
149
|
+
"""Ctrl+C clears current line (like a real shell)."""
|
|
150
|
+
event.app.current_buffer.reset()
|
|
151
|
+
|
|
152
|
+
self._session = PromptSession(
|
|
153
|
+
history=InMemoryHistory(),
|
|
154
|
+
completer=_SlashCommandCompleter(),
|
|
155
|
+
complete_while_typing=True,
|
|
156
|
+
style=style,
|
|
157
|
+
bottom_toolbar=bottom_toolbar,
|
|
158
|
+
key_bindings=kb,
|
|
159
|
+
mouse_support=False,
|
|
160
|
+
complete_in_thread=True,
|
|
161
|
+
reserve_space_for_menu=10,
|
|
162
|
+
# Show completion descriptions to the right (like VS Code)
|
|
163
|
+
complete_style="COLUMN",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
def is_interactive(self) -> bool:
|
|
167
|
+
return self._session is not None
|
|
168
|
+
|
|
169
|
+
async def prompt_async(self) -> str:
|
|
170
|
+
"""
|
|
171
|
+
Async prompt — returns stripped user input.
|
|
172
|
+
Raises EOFError on Ctrl+D (caller should handle gracefully).
|
|
173
|
+
"""
|
|
174
|
+
if self._session is not None:
|
|
175
|
+
from prompt_toolkit.formatted_text import HTML as _HTML
|
|
176
|
+
|
|
177
|
+
result = await self._session.prompt_async(
|
|
178
|
+
_HTML('<style fg="#5fafd7"><b>❯</b></style> '),
|
|
179
|
+
)
|
|
180
|
+
return (result or "").strip()
|
|
181
|
+
else:
|
|
182
|
+
return input("❯ ").strip()
|
|
@@ -14,6 +14,7 @@ from gemcode.capability_routing import apply_capability_routing
|
|
|
14
14
|
from gemcode.config import load_cli_environment
|
|
15
15
|
from gemcode.model_routing import pick_effective_model
|
|
16
16
|
from gemcode.repl_slash import process_repl_slash
|
|
17
|
+
from gemcode.tui.input_handler import GemCodeInputHandler
|
|
17
18
|
from gemcode.tui.welcome_rich import print_shortcuts_hint, print_welcome_dashboard
|
|
18
19
|
|
|
19
20
|
_ADK_REQUEST_CONFIRMATION = "adk_request_confirmation"
|
|
@@ -185,6 +186,17 @@ async def run_gemcode_scrollback_tui(
|
|
|
185
186
|
|
|
186
187
|
char_delay_ms = int(os.environ.get("GEMCODE_TUI_CHAR_DELAY_MS", "0") or "0")
|
|
187
188
|
|
|
189
|
+
# Build the interactive input handler (prompt_toolkit when available, plain
|
|
190
|
+
# input() otherwise). Callables let the handler always read the *current*
|
|
191
|
+
# model and session id even after /model or /clear commands.
|
|
192
|
+
_current_session_id_holder = [session_id]
|
|
193
|
+
|
|
194
|
+
input_handler = GemCodeInputHandler(
|
|
195
|
+
ansi_enabled=ansi.enabled,
|
|
196
|
+
get_model=lambda: getattr(cfg, "model", "gemini") or "gemini",
|
|
197
|
+
get_session_id=lambda: _current_session_id_holder[0],
|
|
198
|
+
)
|
|
199
|
+
|
|
188
200
|
async def typewrite(text: str) -> None:
|
|
189
201
|
if not text:
|
|
190
202
|
return
|
|
@@ -252,7 +264,7 @@ async def run_gemcode_scrollback_tui(
|
|
|
252
264
|
|
|
253
265
|
while True:
|
|
254
266
|
try:
|
|
255
|
-
prompt =
|
|
267
|
+
prompt = await input_handler.prompt_async()
|
|
256
268
|
except EOFError:
|
|
257
269
|
print("")
|
|
258
270
|
return
|
|
@@ -276,6 +288,7 @@ async def run_gemcode_scrollback_tui(
|
|
|
276
288
|
return
|
|
277
289
|
if slash.new_session_id is not None:
|
|
278
290
|
current_session_id = slash.new_session_id
|
|
291
|
+
_current_session_id_holder[0] = current_session_id
|
|
279
292
|
if slash.skip_model_turn:
|
|
280
293
|
# Runner binds the model at creation time (LlmAgent(model=...)),
|
|
281
294
|
# so rebuild it when the user overrides the model mid-session.
|
|
@@ -400,9 +413,12 @@ async def run_gemcode_scrollback_tui(
|
|
|
400
413
|
print(
|
|
401
414
|
f" ⎿ {ansi.blue}{ansi.bold}Permission needed{ansi.reset} for {ansi.bold}{tool_name}{ansi.reset}."
|
|
402
415
|
)
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
416
|
+
try:
|
|
417
|
+
ans = input(
|
|
418
|
+
f" ⎿ Allow? ({ansi.blue_ok}y{ansi.reset}/{ansi.dim}N{ansi.reset}) "
|
|
419
|
+
).strip().lower()
|
|
420
|
+
except EOFError:
|
|
421
|
+
ans = ""
|
|
406
422
|
ok = ans in ("y", "yes")
|
|
407
423
|
|
|
408
424
|
parts.append(
|
|
@@ -132,4 +132,9 @@ def print_welcome_dashboard(cfg, *, console: Console) -> None:
|
|
|
132
132
|
def print_shortcuts_hint(*, console: Console) -> None:
|
|
133
133
|
from rich.text import Text
|
|
134
134
|
|
|
135
|
-
console.print(
|
|
135
|
+
console.print(
|
|
136
|
+
Text(
|
|
137
|
+
" Type a message to begin · / for commands · ↑↓ history",
|
|
138
|
+
style=f"dim {_LABEL_GREY}",
|
|
139
|
+
)
|
|
140
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gemcode
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17
|
|
4
4
|
Summary: Local-first coding agent on Google Gemini + ADK
|
|
5
5
|
Author: GemCode Contributors
|
|
6
6
|
License: Apache License
|
|
@@ -179,6 +179,7 @@ Requires-Dist: google-adk>=1.0.0
|
|
|
179
179
|
Requires-Dist: google-genai>=1.0.0
|
|
180
180
|
Requires-Dist: python-dotenv>=1.0.0
|
|
181
181
|
Requires-Dist: rich>=13.0.0
|
|
182
|
+
Requires-Dist: prompt_toolkit>=3.0.0
|
|
182
183
|
Provides-Extra: dev
|
|
183
184
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
184
185
|
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|