gemcode 0.3.12__tar.gz → 0.3.14__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.12/src/gemcode.egg-info → gemcode-0.3.14}/PKG-INFO +2 -4
- {gemcode-0.3.12 → gemcode-0.3.14}/pyproject.toml +2 -2
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/cli.py +11 -28
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tui/scrollback.py +10 -78
- gemcode-0.3.14/src/gemcode/tui/welcome_banner.py +127 -0
- gemcode-0.3.14/src/gemcode/tui/welcome_rich.py +40 -0
- {gemcode-0.3.12 → gemcode-0.3.14/src/gemcode.egg-info}/PKG-INFO +2 -4
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode.egg-info/SOURCES.txt +2 -1
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode.egg-info/requires.txt +1 -4
- gemcode-0.3.12/src/gemcode/tui/app.py +0 -968
- {gemcode-0.3.12 → gemcode-0.3.14}/LICENSE +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/MANIFEST.in +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/README.md +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/setup.cfg +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/agent.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/config.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/invoke.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/kairos_daemon.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/repl_commands.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/repl_slash.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tools/__init__.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/tui/startup_screen.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/version.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/web/claude_sse_adapter.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_claude_web_adapter_sse.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_credentials.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_kairos_scheduler.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_paths.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_permissions.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_tools.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.12 → gemcode-0.3.14}/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.14
|
|
4
4
|
Summary: Local-first coding agent on Google Gemini + ADK
|
|
5
5
|
Author: GemCode Contributors
|
|
6
6
|
License: Apache License
|
|
@@ -178,14 +178,12 @@ License-File: LICENSE
|
|
|
178
178
|
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
|
+
Requires-Dist: rich>=13.0.0
|
|
181
182
|
Provides-Extra: dev
|
|
182
183
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
183
184
|
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
|
184
185
|
Provides-Extra: mcp
|
|
185
186
|
Requires-Dist: mcp>=1.0.0; extra == "mcp"
|
|
186
|
-
Provides-Extra: tui
|
|
187
|
-
Requires-Dist: prompt_toolkit>=3.0.0; extra == "tui"
|
|
188
|
-
Requires-Dist: rich>=13.0.0; extra == "tui"
|
|
189
187
|
Dynamic: license-file
|
|
190
188
|
|
|
191
189
|
# GemCode
|
|
@@ -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.14"
|
|
8
8
|
description = "Local-first coding agent on Google Gemini + ADK"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -30,6 +30,7 @@ dependencies = [
|
|
|
30
30
|
"google-adk>=1.0.0",
|
|
31
31
|
"google-genai>=1.0.0",
|
|
32
32
|
"python-dotenv>=1.0.0",
|
|
33
|
+
"rich>=13.0.0",
|
|
33
34
|
]
|
|
34
35
|
|
|
35
36
|
[project.urls]
|
|
@@ -40,7 +41,6 @@ Issues = "https://github.com/spiderdev27/GemCode/issues"
|
|
|
40
41
|
[project.optional-dependencies]
|
|
41
42
|
dev = ["pytest>=8.0.0", "pytest-asyncio>=0.24.0"]
|
|
42
43
|
mcp = ["mcp>=1.0.0"]
|
|
43
|
-
tui = ["prompt_toolkit>=3.0.0", "rich>=13.0.0"]
|
|
44
44
|
|
|
45
45
|
[project.scripts]
|
|
46
46
|
gemcode = "gemcode.cli:main"
|
|
@@ -229,11 +229,10 @@ async def _run_repl(cfg: GemCodeConfig, session_id: str, *, use_mcp: bool) -> No
|
|
|
229
229
|
except EOFError:
|
|
230
230
|
pass
|
|
231
231
|
|
|
232
|
-
# Optional
|
|
232
|
+
# Optional terminal UI: single scrollback-style REPL (terminal history, no alt-screen app).
|
|
233
233
|
tui_enabled = os.environ.get("GEMCODE_TUI", "1").lower() in ("1", "true", "yes", "on")
|
|
234
234
|
if tui_enabled:
|
|
235
235
|
term = (os.environ.get("TERM") or "").strip().lower()
|
|
236
|
-
# Guardrails: Prompt Toolkit needs a real interactive terminal.
|
|
237
236
|
if not sys.stdin.isatty() or not sys.stdout.isatty() or term in ("", "dumb", "unknown"):
|
|
238
237
|
print(
|
|
239
238
|
f"[tui] disabled (stdin/stdout isatty={sys.stdin.isatty()}/{sys.stdout.isatty()}, TERM={term or '<unset>'}); using plain REPL",
|
|
@@ -241,35 +240,19 @@ async def _run_repl(cfg: GemCodeConfig, session_id: str, *, use_mcp: bool) -> No
|
|
|
241
240
|
)
|
|
242
241
|
else:
|
|
243
242
|
try:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
runner=runner,
|
|
253
|
-
session_id=session_id,
|
|
254
|
-
extra_tools=extra or None,
|
|
255
|
-
)
|
|
256
|
-
else:
|
|
257
|
-
from gemcode.tui.app import run_gemcode_tui
|
|
258
|
-
|
|
259
|
-
await run_gemcode_tui(
|
|
260
|
-
cfg=cfg,
|
|
261
|
-
runner=runner,
|
|
262
|
-
session_id=session_id,
|
|
263
|
-
extra_tools=extra or None,
|
|
264
|
-
)
|
|
243
|
+
from gemcode.tui.scrollback import run_gemcode_scrollback_tui
|
|
244
|
+
|
|
245
|
+
await run_gemcode_scrollback_tui(
|
|
246
|
+
cfg=cfg,
|
|
247
|
+
runner=runner,
|
|
248
|
+
session_id=session_id,
|
|
249
|
+
extra_tools=extra or None,
|
|
250
|
+
)
|
|
265
251
|
return
|
|
266
252
|
except Exception as e:
|
|
267
|
-
# Dependency missing or terminal doesn't support full-screen.
|
|
268
|
-
# Print one line so users know how to fix it.
|
|
269
253
|
print(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
file=sys.stderr,
|
|
254
|
+
f"[tui] failed to start: {type(e).__name__}: {e} (falling back to plain REPL).",
|
|
255
|
+
file=sys.stderr,
|
|
273
256
|
)
|
|
274
257
|
|
|
275
258
|
print(
|
|
@@ -8,13 +8,13 @@ from dataclasses import dataclass
|
|
|
8
8
|
|
|
9
9
|
from google.adk.agents.run_config import RunConfig
|
|
10
10
|
from google.genai import types
|
|
11
|
+
from rich.console import Console
|
|
11
12
|
|
|
12
13
|
from gemcode.capability_routing import apply_capability_routing
|
|
13
14
|
from gemcode.config import load_cli_environment
|
|
14
15
|
from gemcode.model_routing import pick_effective_model
|
|
15
16
|
from gemcode.repl_slash import process_repl_slash
|
|
16
|
-
from gemcode.
|
|
17
|
-
from gemcode.workspace_hints import narrow_workspace_tip
|
|
17
|
+
from gemcode.tui.welcome_rich import print_shortcuts_hint, print_welcome_dashboard
|
|
18
18
|
|
|
19
19
|
_ADK_REQUEST_CONFIRMATION = "adk_request_confirmation"
|
|
20
20
|
|
|
@@ -154,64 +154,6 @@ def _hr(ch: str = "─") -> str:
|
|
|
154
154
|
return ch * _term_width()
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
def _dashboard(cfg) -> str:
|
|
158
|
-
w = _term_width()
|
|
159
|
-
title = f" GemCode v{os.environ.get('GEMCODE_VERSION', get_version())} "
|
|
160
|
-
left_w = (w - 4) * 2 // 3
|
|
161
|
-
right_w = (w - 4) - left_w
|
|
162
|
-
|
|
163
|
-
def pad(s: str, ww: int) -> str:
|
|
164
|
-
s = s.replace("\n", " ")
|
|
165
|
-
if len(s) > ww:
|
|
166
|
-
return s[: ww - 1] + "…"
|
|
167
|
-
return s + (" " * (ww - len(s)))
|
|
168
|
-
|
|
169
|
-
user = (os.environ.get("USER") or os.environ.get("LOGNAME") or "there").strip()
|
|
170
|
-
model = getattr(cfg, "model", "") or ""
|
|
171
|
-
root = str(getattr(cfg, "project_root", "") or "")
|
|
172
|
-
|
|
173
|
-
box_top = "╭" + ("─" * (w - 2)) + "╮"
|
|
174
|
-
box_bot = "╰" + ("─" * (w - 2)) + "╯"
|
|
175
|
-
lines: list[str] = [box_top]
|
|
176
|
-
lines.append("│" + pad(title, w - 2) + "│")
|
|
177
|
-
lines.append("│" + (" " * (w - 2)) + "│")
|
|
178
|
-
left = [
|
|
179
|
-
"",
|
|
180
|
-
f"Welcome back {user}!",
|
|
181
|
-
"",
|
|
182
|
-
" ▄▄▄▄▄▄▄",
|
|
183
|
-
" ▐█████████▌",
|
|
184
|
-
" ▐█████████▌",
|
|
185
|
-
" ▀▀▀▀▀▀▀",
|
|
186
|
-
"",
|
|
187
|
-
f"{model or 'Gemini 2.0 Flash'} · Local session",
|
|
188
|
-
root,
|
|
189
|
-
]
|
|
190
|
-
right = [
|
|
191
|
-
"Tips for getting started",
|
|
192
|
-
"First run creates .gemcode/ (trust + API key)",
|
|
193
|
-
"Use /help to see available commands",
|
|
194
|
-
"",
|
|
195
|
-
"Recent activity",
|
|
196
|
-
"No recent activity",
|
|
197
|
-
]
|
|
198
|
-
h = max(len(left), len(right))
|
|
199
|
-
left += [""] * (h - len(left))
|
|
200
|
-
right += [""] * (h - len(right))
|
|
201
|
-
for i in range(h):
|
|
202
|
-
lines.append(
|
|
203
|
-
"│ " + pad(left[i], left_w) + " │ " + pad(right[i], right_w) + " │"
|
|
204
|
-
)
|
|
205
|
-
nt = narrow_workspace_tip(getattr(cfg, "project_root"))
|
|
206
|
-
if nt:
|
|
207
|
-
lines.append("│" + pad(f" {nt}", w - 2) + "│")
|
|
208
|
-
lines.append(box_bot)
|
|
209
|
-
lines.append("")
|
|
210
|
-
lines.append(" ↑ GemCode now supports larger contexts · faster streaming · better tools")
|
|
211
|
-
lines.append("")
|
|
212
|
-
return "\n".join(lines)
|
|
213
|
-
|
|
214
|
-
|
|
215
157
|
async def run_gemcode_scrollback_tui(
|
|
216
158
|
*, cfg, runner, session_id: str, extra_tools=None
|
|
217
159
|
) -> None:
|
|
@@ -234,25 +176,15 @@ async def run_gemcode_scrollback_tui(
|
|
|
234
176
|
)
|
|
235
177
|
)
|
|
236
178
|
|
|
179
|
+
console = Console(
|
|
180
|
+
width=_term_width(),
|
|
181
|
+
force_terminal=bool(sys.stdout.isatty()),
|
|
182
|
+
no_color=not ansi.enabled,
|
|
183
|
+
highlight=False,
|
|
184
|
+
)
|
|
237
185
|
if os.environ.get("GEMCODE_TUI_SHOW_DASHBOARD", "1").lower() in ("1", "true", "yes", "on"):
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
# Color title + the ASCII mark.
|
|
241
|
-
lines = dash.splitlines()
|
|
242
|
-
if len(lines) >= 2:
|
|
243
|
-
lines[1] = (
|
|
244
|
-
lines[1]
|
|
245
|
-
.replace("GemCode", f"{ansi.blue}{ansi.bold}GemCode{ansi.reset}")
|
|
246
|
-
.replace("v", f"{ansi.dim}v{ansi.reset}")
|
|
247
|
-
)
|
|
248
|
-
for i, ln in enumerate(lines):
|
|
249
|
-
if "▐▛███▜▌" in ln or "▝▜█████▛▘" in ln or "▘▘ ▝▝" in ln:
|
|
250
|
-
lines[i] = f"{ansi.blue2}{ln}{ansi.reset}"
|
|
251
|
-
dash = "\n".join(lines)
|
|
252
|
-
print(dash)
|
|
253
|
-
|
|
254
|
-
print(f"{ansi.dim} ? for shortcuts{ansi.reset}")
|
|
255
|
-
print(f"{ansi.dim} (Tip: set GEMCODE_TUI_STYLE=full for spinner/status bar){ansi.reset}")
|
|
186
|
+
print_welcome_dashboard(cfg, console=console)
|
|
187
|
+
print_shortcuts_hint(console=console)
|
|
256
188
|
print("")
|
|
257
189
|
|
|
258
190
|
char_delay_ms = int(os.environ.get("GEMCODE_TUI_CHAR_DELAY_MS", "0") or "0")
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""ASCII welcome banner + provider box (scrollback + full-screen TUI)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from gemcode.version import get_version
|
|
9
|
+
from gemcode.vertex import vertex_env_active
|
|
10
|
+
from gemcode.workspace_hints import narrow_workspace_tip
|
|
11
|
+
|
|
12
|
+
_GEM_LINES = (
|
|
13
|
+
r" ██████╗ ███████╗███╗ ███╗",
|
|
14
|
+
r" ██╔════╝ ██╔════╝████╗ ████║",
|
|
15
|
+
r" ██║ ███╗█████╗ ██╔████╔██║",
|
|
16
|
+
r" ██║ ██║██╔══╝ ██║╚██╔╝██║",
|
|
17
|
+
r" ╚██████╔╝███████╗██║ ╚═╝ ██║",
|
|
18
|
+
r" ╚═════╝ ╚══════╝╚═╝ ╚═╝",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
_CODE_LINES = (
|
|
22
|
+
r" ██████╗ ██████╗ ██████╗ ███████╗",
|
|
23
|
+
r" ██╔════╝██╔═══██╗██╔══██╗██╔════╝",
|
|
24
|
+
r" ██║ ██║ ██║██║ ██║█████╗ ",
|
|
25
|
+
r" ██║ ██║ ██║██║ ██║██╔══╝ ",
|
|
26
|
+
r" ╚██████╗╚██████╔╝██████╔╝███████╗",
|
|
27
|
+
r" ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
_TAGLINE = "✦ Gemini-powered coding agent. Fast. Capable. Local. ✦"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _banner_width() -> int:
|
|
34
|
+
return max(len(x) for x in _GEM_LINES + _CODE_LINES)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _center(line: str, width: int) -> str:
|
|
38
|
+
if len(line) >= width:
|
|
39
|
+
return line
|
|
40
|
+
pad = width - len(line)
|
|
41
|
+
left = pad // 2
|
|
42
|
+
return " " * left + line + " " * (pad - left)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _provider_model_endpoint(cfg) -> tuple[str, str, str]:
|
|
46
|
+
model = (getattr(cfg, "model", None) or "").strip() or "gemini-2.5-flash"
|
|
47
|
+
if vertex_env_active():
|
|
48
|
+
provider = "Vertex AI"
|
|
49
|
+
loc = (os.environ.get("GOOGLE_CLOUD_LOCATION") or "us-central1").strip()
|
|
50
|
+
proj = (os.environ.get("GOOGLE_CLOUD_PROJECT") or "").strip()
|
|
51
|
+
end = f"vertex://{loc}"
|
|
52
|
+
if proj:
|
|
53
|
+
end = f"{end} · {proj}"
|
|
54
|
+
else:
|
|
55
|
+
provider = "Google Gemini"
|
|
56
|
+
end = "https://generativelanguage.googleapis.com/"
|
|
57
|
+
return provider, model, end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _kv_line(inner: int, label: str, value: str) -> str:
|
|
61
|
+
"""One row inside the box: leading space + label column + value (total width ``inner``)."""
|
|
62
|
+
lead = " "
|
|
63
|
+
prefix = f"{label:<10}"
|
|
64
|
+
room = inner - len(lead) - len(prefix)
|
|
65
|
+
if room < 4:
|
|
66
|
+
raw = (lead + prefix + value)[:inner]
|
|
67
|
+
return raw.ljust(inner)
|
|
68
|
+
v = value
|
|
69
|
+
if len(v) > room:
|
|
70
|
+
v = v[: max(0, room - 3)] + "..."
|
|
71
|
+
return (lead + prefix + v).ljust(inner)[:inner]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def format_welcome_banner(cfg, *, term_width: int = 100) -> str:
|
|
75
|
+
inner = min(60, max(48, term_width - 4))
|
|
76
|
+
top = "╔" + ("═" * inner) + "╗"
|
|
77
|
+
mid = "╠" + ("═" * inner) + "╣"
|
|
78
|
+
bot = "╚" + ("═" * inner) + "╝"
|
|
79
|
+
|
|
80
|
+
provider, model, endpoint = _provider_model_endpoint(cfg)
|
|
81
|
+
row_p = _kv_line(inner, "Provider", provider)
|
|
82
|
+
row_m = _kv_line(inner, "Model", model)
|
|
83
|
+
row_e = _kv_line(inner, "Endpoint", endpoint)
|
|
84
|
+
|
|
85
|
+
mode = "vertex" if vertex_env_active() else "cloud"
|
|
86
|
+
status_raw = f" ● {mode} Ready — type /help to begin"
|
|
87
|
+
if len(status_raw) > inner:
|
|
88
|
+
status_raw = status_raw[: max(0, inner - 3)] + "..."
|
|
89
|
+
row_s = status_raw.ljust(inner)[:inner]
|
|
90
|
+
|
|
91
|
+
bw = max(_banner_width(), inner + 2)
|
|
92
|
+
lines: list[str] = []
|
|
93
|
+
for row in _GEM_LINES:
|
|
94
|
+
lines.append(_center(row, bw))
|
|
95
|
+
lines.append("")
|
|
96
|
+
for row in _CODE_LINES:
|
|
97
|
+
lines.append(_center(row, bw))
|
|
98
|
+
lines.append("")
|
|
99
|
+
lines.append(_center(f" {_TAGLINE} ", bw))
|
|
100
|
+
lines.append("")
|
|
101
|
+
|
|
102
|
+
box_w = inner + 2
|
|
103
|
+
pad_b = max(0, (bw - box_w) // 2)
|
|
104
|
+
bp = " " * pad_b
|
|
105
|
+
|
|
106
|
+
def box_row(body: str) -> str:
|
|
107
|
+
b = body[:inner].ljust(inner)
|
|
108
|
+
return bp + "│" + b + "│"
|
|
109
|
+
|
|
110
|
+
lines.append(bp + top)
|
|
111
|
+
lines.append(box_row(row_p))
|
|
112
|
+
lines.append(box_row(row_m))
|
|
113
|
+
lines.append(box_row(row_e))
|
|
114
|
+
lines.append(bp + mid)
|
|
115
|
+
lines.append(box_row(row_s))
|
|
116
|
+
lines.append(bp + bot)
|
|
117
|
+
|
|
118
|
+
ver = os.environ.get("GEMCODE_VERSION", get_version())
|
|
119
|
+
lines.append(_center(f" gemcode v{ver} ", bw))
|
|
120
|
+
|
|
121
|
+
root = getattr(cfg, "project_root", None)
|
|
122
|
+
if isinstance(root, Path):
|
|
123
|
+
nt = narrow_workspace_tip(root)
|
|
124
|
+
if nt:
|
|
125
|
+
lines.append(_center(nt, bw))
|
|
126
|
+
lines.append("")
|
|
127
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Rich renderables for the scrollback welcome screen."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from gemcode.tui.welcome_banner import format_welcome_banner
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def print_welcome_dashboard(cfg, *, console: Console) -> None:
|
|
14
|
+
from rich.console import Group
|
|
15
|
+
from rich.text import Text
|
|
16
|
+
|
|
17
|
+
tw = max(60, console.width or 80)
|
|
18
|
+
raw = format_welcome_banner(cfg, term_width=tw)
|
|
19
|
+
rows: list[Text] = []
|
|
20
|
+
for ln in raw.splitlines():
|
|
21
|
+
low = ln.lower()
|
|
22
|
+
if "█" in ln:
|
|
23
|
+
rows.append(Text(ln, style="bold #3BA8E8"))
|
|
24
|
+
elif any(c in ln for c in "╔╗║╚╠╣═│") and "█" not in ln:
|
|
25
|
+
rows.append(Text(ln, style="bright_blue"))
|
|
26
|
+
elif "gemcode v" in low:
|
|
27
|
+
rows.append(Text(ln, style="bold dim"))
|
|
28
|
+
elif "✦" in ln:
|
|
29
|
+
rows.append(Text(ln, style="dim italic"))
|
|
30
|
+
elif ln.strip().startswith("Tip:"):
|
|
31
|
+
rows.append(Text(ln, style="dim"))
|
|
32
|
+
else:
|
|
33
|
+
rows.append(Text(ln))
|
|
34
|
+
console.print(Group(*rows))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def print_shortcuts_hint(*, console: Console) -> None:
|
|
38
|
+
from rich.text import Text
|
|
39
|
+
|
|
40
|
+
console.print(Text(" ? for shortcuts", style="dim"))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gemcode
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.14
|
|
4
4
|
Summary: Local-first coding agent on Google Gemini + ADK
|
|
5
5
|
Author: GemCode Contributors
|
|
6
6
|
License: Apache License
|
|
@@ -178,14 +178,12 @@ License-File: LICENSE
|
|
|
178
178
|
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
|
+
Requires-Dist: rich>=13.0.0
|
|
181
182
|
Provides-Extra: dev
|
|
182
183
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
183
184
|
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
|
184
185
|
Provides-Extra: mcp
|
|
185
186
|
Requires-Dist: mcp>=1.0.0; extra == "mcp"
|
|
186
|
-
Provides-Extra: tui
|
|
187
|
-
Requires-Dist: prompt_toolkit>=3.0.0; extra == "tui"
|
|
188
|
-
Requires-Dist: rich>=13.0.0; extra == "tui"
|
|
189
187
|
Dynamic: license-file
|
|
190
188
|
|
|
191
189
|
# GemCode
|
|
@@ -69,10 +69,11 @@ src/gemcode/tools/search.py
|
|
|
69
69
|
src/gemcode/tools/shell.py
|
|
70
70
|
src/gemcode/tools/shell_gate.py
|
|
71
71
|
src/gemcode/tools/todo.py
|
|
72
|
-
src/gemcode/tui/app.py
|
|
73
72
|
src/gemcode/tui/scrollback.py
|
|
74
73
|
src/gemcode/tui/spinner.py
|
|
75
74
|
src/gemcode/tui/startup_screen.py
|
|
75
|
+
src/gemcode/tui/welcome_banner.py
|
|
76
|
+
src/gemcode/tui/welcome_rich.py
|
|
76
77
|
src/gemcode/web/__init__.py
|
|
77
78
|
src/gemcode/web/claude_sse_adapter.py
|
|
78
79
|
src/gemcode/web/terminal_repl.py
|