gemcode 0.3.51__tar.gz → 0.3.53__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.51/src/gemcode.egg-info → gemcode-0.3.53}/PKG-INFO +1 -1
- {gemcode-0.3.51 → gemcode-0.3.53}/pyproject.toml +1 -1
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/session_runtime.py +31 -4
- gemcode-0.3.53/src/gemcode/tui/welcome_banner.py +137 -0
- {gemcode-0.3.51 → gemcode-0.3.53/src/gemcode.egg-info}/PKG-INFO +1 -1
- gemcode-0.3.51/src/gemcode/tui/welcome_banner.py +0 -127
- {gemcode-0.3.51 → gemcode-0.3.53}/LICENSE +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/MANIFEST.in +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/README.md +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/setup.cfg +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/agent.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/cli.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/config.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/hooks.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/intent_classifier.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/invoke.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/kairos_daemon.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/openapi_loader.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/pricing.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/refine.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/repl_commands.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/repl_slash.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/review_agent.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/session_store.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/__init__.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/notes.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/subtask.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tui/input_handler.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tui/scrollback.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/version.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/web/claude_sse_adapter.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode.egg-info/SOURCES.txt +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_claude_web_adapter_sse.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_credentials.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_kairos_scheduler.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_paths.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_permissions.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_tools.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.51 → gemcode-0.3.53}/tests/test_workspace_hints.py +0 -0
|
@@ -77,6 +77,18 @@ def _make_safe_computer_toolset(computer):
|
|
|
77
77
|
file=sys.stderr,
|
|
78
78
|
)
|
|
79
79
|
|
|
80
|
+
# ── BaseToolset required attribute ──────────────────────────────────────
|
|
81
|
+
@property
|
|
82
|
+
def tool_name_prefix(self) -> str:
|
|
83
|
+
if self._inner is not None:
|
|
84
|
+
try:
|
|
85
|
+
return self._inner.tool_name_prefix
|
|
86
|
+
except Exception:
|
|
87
|
+
pass
|
|
88
|
+
return ""
|
|
89
|
+
|
|
90
|
+
# ── Core protocol methods ────────────────────────────────────────────────
|
|
91
|
+
|
|
80
92
|
async def process_llm_request(self, *, tool_context, llm_request) -> None:
|
|
81
93
|
if self._broken or self._inner is None:
|
|
82
94
|
return
|
|
@@ -85,8 +97,9 @@ def _make_safe_computer_toolset(computer):
|
|
|
85
97
|
tool_context=tool_context, llm_request=llm_request
|
|
86
98
|
)
|
|
87
99
|
except Exception as exc:
|
|
88
|
-
self._broken
|
|
89
|
-
|
|
100
|
+
if not self._broken:
|
|
101
|
+
self._broken = True
|
|
102
|
+
self._warn_once(exc)
|
|
90
103
|
|
|
91
104
|
async def get_tools(self, readonly_context=None):
|
|
92
105
|
if self._broken or self._inner is None:
|
|
@@ -94,8 +107,9 @@ def _make_safe_computer_toolset(computer):
|
|
|
94
107
|
try:
|
|
95
108
|
return await self._inner.get_tools(readonly_context)
|
|
96
109
|
except Exception as exc:
|
|
97
|
-
self._broken
|
|
98
|
-
|
|
110
|
+
if not self._broken:
|
|
111
|
+
self._broken = True
|
|
112
|
+
self._warn_once(exc)
|
|
99
113
|
return []
|
|
100
114
|
|
|
101
115
|
async def close(self) -> None:
|
|
@@ -105,6 +119,19 @@ def _make_safe_computer_toolset(computer):
|
|
|
105
119
|
except Exception:
|
|
106
120
|
pass
|
|
107
121
|
|
|
122
|
+
def __getattr__(self, name: str):
|
|
123
|
+
"""Proxy any other BaseToolset attributes ADK needs to the inner toolset."""
|
|
124
|
+
# Guard against infinite recursion for our own private attrs.
|
|
125
|
+
if name.startswith("_"):
|
|
126
|
+
raise AttributeError(name)
|
|
127
|
+
inner = object.__getattribute__(self, "_inner")
|
|
128
|
+
if inner is not None:
|
|
129
|
+
try:
|
|
130
|
+
return getattr(inner, name)
|
|
131
|
+
except AttributeError:
|
|
132
|
+
pass
|
|
133
|
+
raise AttributeError(f"'_SafeComputerUseToolset' has no attribute '{name}'")
|
|
134
|
+
|
|
108
135
|
return _SafeComputerUseToolset()
|
|
109
136
|
|
|
110
137
|
|
|
@@ -0,0 +1,137 @@
|
|
|
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
|
+
provider, model, endpoint = _provider_model_endpoint(cfg)
|
|
76
|
+
# Banner style: "solid" removes the boxed provider block and blank spacer
|
|
77
|
+
# lines for a single rigid header (OpenClaude-like).
|
|
78
|
+
style = (os.environ.get("GEMCODE_TUI_BANNER_STYLE", "solid") or "solid").strip().lower()
|
|
79
|
+
|
|
80
|
+
bw = max(_banner_width(), min(80, max(48, term_width)))
|
|
81
|
+
lines: list[str] = []
|
|
82
|
+
for row in _GEM_LINES:
|
|
83
|
+
lines.append(_center(row, bw))
|
|
84
|
+
for row in _CODE_LINES:
|
|
85
|
+
lines.append(_center(row, bw))
|
|
86
|
+
lines.append(_center(_TAGLINE, bw))
|
|
87
|
+
|
|
88
|
+
if style not in ("solid", "rigid", "compact"):
|
|
89
|
+
# Backward-compat: unknown values fall back to the old boxed layout.
|
|
90
|
+
style = "boxed"
|
|
91
|
+
|
|
92
|
+
if style == "boxed":
|
|
93
|
+
inner = min(60, max(48, term_width - 4))
|
|
94
|
+
top = "╔" + ("═" * inner) + "╗"
|
|
95
|
+
mid = "╠" + ("═" * inner) + "╣"
|
|
96
|
+
bot = "╚" + ("═" * inner) + "╝"
|
|
97
|
+
|
|
98
|
+
row_p = _kv_line(inner, "Provider", provider)
|
|
99
|
+
row_m = _kv_line(inner, "Model", model)
|
|
100
|
+
row_e = _kv_line(inner, "Endpoint", endpoint)
|
|
101
|
+
|
|
102
|
+
mode = "vertex" if vertex_env_active() else "cloud"
|
|
103
|
+
status_raw = f" ● {mode} Ready — type /help to begin"
|
|
104
|
+
if len(status_raw) > inner:
|
|
105
|
+
status_raw = status_raw[: max(0, inner - 3)] + "..."
|
|
106
|
+
row_s = status_raw.ljust(inner)[:inner]
|
|
107
|
+
|
|
108
|
+
box_w = inner + 2
|
|
109
|
+
pad_b = max(0, (bw - box_w) // 2)
|
|
110
|
+
bp = " " * pad_b
|
|
111
|
+
|
|
112
|
+
def box_row(body: str) -> str:
|
|
113
|
+
b = body[:inner].ljust(inner)
|
|
114
|
+
return bp + "│" + b + "│"
|
|
115
|
+
|
|
116
|
+
lines.append(bp + top)
|
|
117
|
+
lines.append(box_row(row_p))
|
|
118
|
+
lines.append(box_row(row_m))
|
|
119
|
+
lines.append(box_row(row_e))
|
|
120
|
+
lines.append(bp + mid)
|
|
121
|
+
lines.append(box_row(row_s))
|
|
122
|
+
lines.append(bp + bot)
|
|
123
|
+
else:
|
|
124
|
+
# Solid header: one rigid info line, no extra blocks.
|
|
125
|
+
mode = "vertex" if vertex_env_active() else "cloud"
|
|
126
|
+
info = f"{provider} · {model} · {endpoint} · {mode} ready — type /help"
|
|
127
|
+
lines.append(_center(info, bw))
|
|
128
|
+
|
|
129
|
+
ver = os.environ.get("GEMCODE_VERSION", get_version())
|
|
130
|
+
lines.append(_center(f"gemcode v{ver}", bw))
|
|
131
|
+
|
|
132
|
+
root = getattr(cfg, "project_root", None)
|
|
133
|
+
if isinstance(root, Path):
|
|
134
|
+
nt = narrow_workspace_tip(root)
|
|
135
|
+
if nt:
|
|
136
|
+
lines.append(_center(nt, bw))
|
|
137
|
+
return "\n".join(lines)
|
|
@@ -1,127 +0,0 @@
|
|
|
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)
|
|
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
|
|
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
|