klaude-code 2.2.0__py3-none-any.whl → 2.4.0__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.
- klaude_code/app/runtime.py +2 -15
- klaude_code/cli/list_model.py +30 -13
- klaude_code/cli/main.py +26 -10
- klaude_code/config/assets/builtin_config.yaml +177 -310
- klaude_code/config/config.py +158 -21
- klaude_code/config/{select_model.py → model_matcher.py} +41 -16
- klaude_code/config/sub_agent_model_helper.py +217 -0
- klaude_code/config/thinking.py +2 -2
- klaude_code/const.py +1 -1
- klaude_code/core/agent_profile.py +43 -5
- klaude_code/core/executor.py +129 -47
- klaude_code/core/manager/llm_clients_builder.py +17 -11
- klaude_code/core/prompts/prompt-nano-banana.md +1 -1
- klaude_code/core/tool/file/diff_builder.py +25 -18
- klaude_code/core/tool/sub_agent_tool.py +2 -1
- klaude_code/llm/anthropic/client.py +12 -9
- klaude_code/llm/anthropic/input.py +54 -29
- klaude_code/llm/client.py +1 -1
- klaude_code/llm/codex/client.py +2 -2
- klaude_code/llm/google/client.py +7 -7
- klaude_code/llm/google/input.py +23 -2
- klaude_code/llm/input_common.py +2 -2
- klaude_code/llm/openai_compatible/client.py +3 -3
- klaude_code/llm/openai_compatible/input.py +22 -13
- klaude_code/llm/openai_compatible/stream.py +1 -1
- klaude_code/llm/openrouter/client.py +4 -4
- klaude_code/llm/openrouter/input.py +35 -25
- klaude_code/llm/responses/client.py +5 -5
- klaude_code/llm/responses/input.py +96 -57
- klaude_code/protocol/commands.py +1 -2
- klaude_code/protocol/events/__init__.py +7 -1
- klaude_code/protocol/events/chat.py +10 -0
- klaude_code/protocol/events/system.py +4 -0
- klaude_code/protocol/llm_param.py +1 -1
- klaude_code/protocol/model.py +0 -26
- klaude_code/protocol/op.py +17 -5
- klaude_code/protocol/op_handler.py +5 -0
- klaude_code/protocol/sub_agent/AGENTS.md +28 -0
- klaude_code/protocol/sub_agent/__init__.py +10 -14
- klaude_code/protocol/sub_agent/image_gen.py +2 -1
- klaude_code/session/codec.py +2 -6
- klaude_code/session/session.py +13 -3
- klaude_code/skill/assets/create-plan/SKILL.md +3 -5
- klaude_code/tui/command/__init__.py +3 -6
- klaude_code/tui/command/clear_cmd.py +0 -1
- klaude_code/tui/command/command_abc.py +6 -4
- klaude_code/tui/command/copy_cmd.py +10 -10
- klaude_code/tui/command/debug_cmd.py +11 -10
- klaude_code/tui/command/export_online_cmd.py +18 -23
- klaude_code/tui/command/fork_session_cmd.py +39 -43
- klaude_code/tui/command/model_cmd.py +10 -49
- klaude_code/tui/command/model_picker.py +142 -0
- klaude_code/tui/command/refresh_cmd.py +0 -1
- klaude_code/tui/command/registry.py +15 -21
- klaude_code/tui/command/resume_cmd.py +10 -16
- klaude_code/tui/command/status_cmd.py +8 -12
- klaude_code/tui/command/sub_agent_model_cmd.py +185 -0
- klaude_code/tui/command/terminal_setup_cmd.py +8 -11
- klaude_code/tui/command/thinking_cmd.py +4 -6
- klaude_code/tui/commands.py +5 -0
- klaude_code/tui/components/bash_syntax.py +1 -1
- klaude_code/tui/components/command_output.py +96 -0
- klaude_code/tui/components/common.py +1 -1
- klaude_code/tui/components/developer.py +3 -115
- klaude_code/tui/components/metadata.py +1 -63
- klaude_code/tui/components/rich/cjk_wrap.py +3 -2
- klaude_code/tui/components/rich/status.py +49 -3
- klaude_code/tui/components/rich/theme.py +2 -0
- klaude_code/tui/components/sub_agent.py +25 -46
- klaude_code/tui/components/welcome.py +99 -0
- klaude_code/tui/input/prompt_toolkit.py +19 -8
- klaude_code/tui/machine.py +5 -0
- klaude_code/tui/renderer.py +7 -8
- klaude_code/tui/runner.py +0 -6
- klaude_code/tui/terminal/selector.py +8 -6
- {klaude_code-2.2.0.dist-info → klaude_code-2.4.0.dist-info}/METADATA +21 -74
- {klaude_code-2.2.0.dist-info → klaude_code-2.4.0.dist-info}/RECORD +79 -76
- klaude_code/tui/command/help_cmd.py +0 -51
- klaude_code/tui/command/model_select.py +0 -84
- klaude_code/tui/command/release_notes_cmd.py +0 -85
- {klaude_code-2.2.0.dist-info → klaude_code-2.4.0.dist-info}/WHEEL +0 -0
- {klaude_code-2.2.0.dist-info → klaude_code-2.4.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,24 +1,12 @@
|
|
|
1
|
-
from importlib.metadata import PackageNotFoundError, version
|
|
2
|
-
|
|
3
1
|
from rich.console import Group, RenderableType
|
|
4
2
|
from rich.padding import Padding
|
|
5
3
|
from rich.text import Text
|
|
6
4
|
|
|
7
5
|
from klaude_code.const import DEFAULT_MAX_TOKENS
|
|
8
|
-
from klaude_code.log import is_debug_enabled
|
|
9
6
|
from klaude_code.protocol import events, model
|
|
10
7
|
from klaude_code.tui.components.common import create_grid
|
|
11
|
-
from klaude_code.tui.components.rich.quote import Quote
|
|
12
8
|
from klaude_code.tui.components.rich.theme import ThemeKey
|
|
13
|
-
from klaude_code.ui.common import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def _get_version() -> str:
|
|
17
|
-
"""Get the current version of klaude-code."""
|
|
18
|
-
try:
|
|
19
|
-
return version("klaude-code")
|
|
20
|
-
except PackageNotFoundError:
|
|
21
|
-
return "unknown"
|
|
9
|
+
from klaude_code.ui.common import format_number
|
|
22
10
|
|
|
23
11
|
|
|
24
12
|
def _render_task_metadata_block(
|
|
@@ -195,53 +183,3 @@ def render_task_metadata(e: events.TaskMetadataEvent) -> RenderableType:
|
|
|
195
183
|
renderables.append(Padding(grid, (0, 0, 0, 2)))
|
|
196
184
|
|
|
197
185
|
return Group(*renderables)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def render_welcome(e: events.WelcomeEvent) -> RenderableType:
|
|
201
|
-
"""Render the welcome panel with model info and settings.
|
|
202
|
-
|
|
203
|
-
Args:
|
|
204
|
-
e: The welcome event.
|
|
205
|
-
"""
|
|
206
|
-
debug_mode = is_debug_enabled()
|
|
207
|
-
|
|
208
|
-
panel_content = Text()
|
|
209
|
-
|
|
210
|
-
if e.show_klaude_code_info:
|
|
211
|
-
# First line: Klaude Code version
|
|
212
|
-
klaude_code_style = ThemeKey.WELCOME_DEBUG_TITLE if debug_mode else ThemeKey.WELCOME_HIGHLIGHT_BOLD
|
|
213
|
-
panel_content.append_text(Text("Klaude Code", style=klaude_code_style))
|
|
214
|
-
panel_content.append_text(Text(f" v{_get_version()}", style=ThemeKey.WELCOME_INFO))
|
|
215
|
-
panel_content.append_text(Text("\n"))
|
|
216
|
-
|
|
217
|
-
# Model line: model @ provider · params...
|
|
218
|
-
panel_content.append_text(
|
|
219
|
-
Text.assemble(
|
|
220
|
-
(str(e.llm_config.model), ThemeKey.WELCOME_HIGHLIGHT),
|
|
221
|
-
(" @ ", ThemeKey.WELCOME_INFO),
|
|
222
|
-
(e.llm_config.provider_name, ThemeKey.WELCOME_INFO),
|
|
223
|
-
)
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
# Use format_model_params for consistent formatting
|
|
227
|
-
param_strings = format_model_params(e.llm_config)
|
|
228
|
-
|
|
229
|
-
# Render config items with tree-style prefixes
|
|
230
|
-
for i, param_str in enumerate(param_strings):
|
|
231
|
-
is_last = i == len(param_strings) - 1
|
|
232
|
-
prefix = "└─ " if is_last else "├─ "
|
|
233
|
-
panel_content.append_text(
|
|
234
|
-
Text.assemble(
|
|
235
|
-
("\n", ThemeKey.WELCOME_INFO),
|
|
236
|
-
(prefix, ThemeKey.LINES),
|
|
237
|
-
(param_str, ThemeKey.WELCOME_INFO),
|
|
238
|
-
)
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
border_style = ThemeKey.WELCOME_DEBUG_BORDER if debug_mode else ThemeKey.LINES
|
|
242
|
-
|
|
243
|
-
if e.show_klaude_code_info:
|
|
244
|
-
groups = ["", Quote(panel_content, style=border_style, prefix="▌ "), ""]
|
|
245
|
-
else:
|
|
246
|
-
groups = [Quote(panel_content, style=border_style, prefix="▌ "), ""]
|
|
247
|
-
return Group(*groups)
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import unicodedata
|
|
6
6
|
from collections.abc import Callable
|
|
7
|
+
from typing import Any, cast
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def _is_cjk_char(ch: str) -> bool:
|
|
@@ -222,7 +223,7 @@ def install_rich_cjk_wrap_patch() -> bool:
|
|
|
222
223
|
|
|
223
224
|
return break_positions
|
|
224
225
|
|
|
225
|
-
_wrap.divide_line = divide_line_patched # pyright: ignore[reportPrivateImportUsage]
|
|
226
|
-
_text.divide_line = divide_line_patched # pyright: ignore[reportPrivateImportUsage]
|
|
226
|
+
cast(Any, _wrap).divide_line = divide_line_patched # pyright: ignore[reportPrivateImportUsage]
|
|
227
|
+
cast(Any, _text).divide_line = divide_line_patched # pyright: ignore[reportPrivateImportUsage]
|
|
227
228
|
_rich_cjk_wrap_patch_installed = True
|
|
228
229
|
return True
|
|
@@ -263,6 +263,53 @@ def _breathing_style(console: Console, base_style: Style, intensity: float) -> S
|
|
|
263
263
|
return base_style + Style(color=breathing_color)
|
|
264
264
|
|
|
265
265
|
|
|
266
|
+
def truncate_left(text: Text, max_cells: int, *, console: Console, ellipsis: str = "…") -> Text:
|
|
267
|
+
"""Left-truncate Text to fit within max_cells.
|
|
268
|
+
|
|
269
|
+
Keeps the rightmost part of the text and prepends an ellipsis when truncation occurs.
|
|
270
|
+
Uses cell width so wide characters are handled reasonably.
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
max_cells = max(0, int(max_cells))
|
|
274
|
+
if max_cells == 0:
|
|
275
|
+
return Text("")
|
|
276
|
+
|
|
277
|
+
if cell_len(text.plain) <= max_cells:
|
|
278
|
+
return text
|
|
279
|
+
|
|
280
|
+
ellipsis_cells = cell_len(ellipsis)
|
|
281
|
+
if max_cells <= ellipsis_cells:
|
|
282
|
+
# Not enough space to show any meaningful suffix.
|
|
283
|
+
clipped = Text(ellipsis, style=text.style)
|
|
284
|
+
clipped.truncate(max_cells, overflow="crop", pad=False)
|
|
285
|
+
return clipped
|
|
286
|
+
|
|
287
|
+
suffix_budget = max_cells - ellipsis_cells
|
|
288
|
+
plain = text.plain
|
|
289
|
+
|
|
290
|
+
suffix_cells = 0
|
|
291
|
+
start_index = len(plain)
|
|
292
|
+
for i in range(len(plain) - 1, -1, -1):
|
|
293
|
+
ch_cells = cell_len(plain[i])
|
|
294
|
+
if suffix_cells + ch_cells > suffix_budget:
|
|
295
|
+
break
|
|
296
|
+
suffix_cells += ch_cells
|
|
297
|
+
start_index = i
|
|
298
|
+
if suffix_cells == suffix_budget:
|
|
299
|
+
break
|
|
300
|
+
|
|
301
|
+
if start_index >= len(plain):
|
|
302
|
+
return Text(ellipsis, style=text.style)
|
|
303
|
+
|
|
304
|
+
suffix = text[start_index:]
|
|
305
|
+
try:
|
|
306
|
+
ellipsis_style = suffix.get_style_at_offset(console, 0)
|
|
307
|
+
except Exception:
|
|
308
|
+
ellipsis_style = suffix.style or text.style
|
|
309
|
+
|
|
310
|
+
return Text.assemble(Text(ellipsis, style=ellipsis_style), suffix)
|
|
311
|
+
|
|
312
|
+
|
|
266
313
|
class ShimmerStatusText:
|
|
267
314
|
"""Renderable status line with shimmer effect on the main text and hint.
|
|
268
315
|
|
|
@@ -322,12 +369,11 @@ class _StatusLeftText:
|
|
|
322
369
|
# If the hint itself can't fit, fall back to truncating the combined text.
|
|
323
370
|
if max_width <= hint_cells:
|
|
324
371
|
combined = Text.assemble(main_text, hint_text)
|
|
325
|
-
combined
|
|
326
|
-
yield combined
|
|
372
|
+
yield truncate_left(combined, max(1, max_width), console=console)
|
|
327
373
|
return
|
|
328
374
|
|
|
329
375
|
main_budget = max_width - hint_cells
|
|
330
|
-
main_text
|
|
376
|
+
main_text = truncate_left(main_text, max(1, main_budget), console=console)
|
|
331
377
|
yield Text.assemble(main_text, hint_text)
|
|
332
378
|
|
|
333
379
|
|
|
@@ -191,6 +191,7 @@ class ThemeKey(str, Enum):
|
|
|
191
191
|
WELCOME_HIGHLIGHT_BOLD = "welcome.highlight.bold"
|
|
192
192
|
WELCOME_HIGHLIGHT = "welcome.highlight"
|
|
193
193
|
WELCOME_INFO = "welcome.info"
|
|
194
|
+
WELCOME_INFO_BOLD = "welcome.info.bold"
|
|
194
195
|
# WELCOME DEBUG
|
|
195
196
|
WELCOME_DEBUG_TITLE = "welcome.debug.title"
|
|
196
197
|
WELCOME_DEBUG_BORDER = "welcome.debug.border"
|
|
@@ -307,6 +308,7 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
307
308
|
ThemeKey.WELCOME_HIGHLIGHT_BOLD.value: "bold",
|
|
308
309
|
ThemeKey.WELCOME_HIGHLIGHT.value: palette.blue,
|
|
309
310
|
ThemeKey.WELCOME_INFO.value: palette.grey1,
|
|
311
|
+
ThemeKey.WELCOME_INFO_BOLD.value: "bold " + palette.grey1,
|
|
310
312
|
# WELCOME DEBUG
|
|
311
313
|
ThemeKey.WELCOME_DEBUG_TITLE.value: "bold " + palette.red,
|
|
312
314
|
ThemeKey.WELCOME_DEBUG_BORDER.value: palette.red,
|
|
@@ -3,7 +3,7 @@ from typing import Any, cast
|
|
|
3
3
|
|
|
4
4
|
from rich.console import Group, RenderableType
|
|
5
5
|
from rich.json import JSON
|
|
6
|
-
from rich.style import Style
|
|
6
|
+
from rich.style import Style
|
|
7
7
|
from rich.text import Text
|
|
8
8
|
|
|
9
9
|
from klaude_code.const import SUB_AGENT_RESULT_MAX_LINES
|
|
@@ -79,65 +79,44 @@ def _extract_agent_id_footer(text: str) -> tuple[str, str | None]:
|
|
|
79
79
|
def render_sub_agent_result(
|
|
80
80
|
result: str,
|
|
81
81
|
*,
|
|
82
|
-
code_theme: str,
|
|
83
|
-
style: StyleType | None = None,
|
|
84
82
|
has_structured_output: bool = False,
|
|
85
83
|
description: str | None = None,
|
|
86
84
|
) -> RenderableType:
|
|
87
85
|
stripped_result = result.strip()
|
|
88
|
-
|
|
89
|
-
# Extract agentId footer for separate styling
|
|
90
86
|
main_content, agent_id_footer = _extract_agent_id_footer(stripped_result)
|
|
91
87
|
stripped_result = main_content.strip()
|
|
92
88
|
|
|
93
|
-
|
|
89
|
+
elements: list[RenderableType] = []
|
|
90
|
+
if description:
|
|
91
|
+
elements.append(Text(f"---\n{description}", style=ThemeKey.TOOL_RESULT))
|
|
92
|
+
|
|
93
|
+
# Try structured JSON output first
|
|
94
|
+
use_text_rendering = True
|
|
94
95
|
if has_structured_output:
|
|
95
96
|
try:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
style=ThemeKey.TOOL_RESULT,
|
|
100
|
-
),
|
|
101
|
-
JSON(stripped_result),
|
|
102
|
-
]
|
|
103
|
-
if description:
|
|
104
|
-
group_elements.insert(0, Text(f"\n{description}", style=style or ""))
|
|
105
|
-
if agent_id_footer:
|
|
106
|
-
group_elements.append(Text(agent_id_footer, style=ThemeKey.SUB_AGENT_FOOTER))
|
|
107
|
-
return Group(*group_elements)
|
|
97
|
+
elements.append(Text("use /export to view full output", style=ThemeKey.TOOL_RESULT_TRUNCATED))
|
|
98
|
+
elements.append(JSON(stripped_result))
|
|
99
|
+
use_text_rendering = False
|
|
108
100
|
except json.JSONDecodeError:
|
|
109
|
-
# Fall back to markdown if not valid JSON
|
|
110
101
|
pass
|
|
111
102
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Text(
|
|
125
|
-
f"( … more {hidden_count} lines)",
|
|
126
|
-
style=ThemeKey.TOOL_RESULT_TRUNCATED,
|
|
127
|
-
)
|
|
128
|
-
)
|
|
129
|
-
truncated_elements.append(Text(tail_text, style=style or ""))
|
|
130
|
-
if agent_id_footer:
|
|
131
|
-
truncated_elements.append(Text(agent_id_footer, style=ThemeKey.SUB_AGENT_FOOTER))
|
|
132
|
-
return Group(*truncated_elements)
|
|
103
|
+
# Text rendering (either fallback or non-structured)
|
|
104
|
+
if use_text_rendering:
|
|
105
|
+
if not stripped_result:
|
|
106
|
+
return Text()
|
|
107
|
+
|
|
108
|
+
lines = stripped_result.splitlines()
|
|
109
|
+
if len(lines) > SUB_AGENT_RESULT_MAX_LINES:
|
|
110
|
+
hidden_count = len(lines) - SUB_AGENT_RESULT_MAX_LINES
|
|
111
|
+
elements.append(Text(f"( ... more {hidden_count} lines)", style=ThemeKey.TOOL_RESULT_TRUNCATED))
|
|
112
|
+
elements.append(Text("\n".join(lines[-SUB_AGENT_RESULT_MAX_LINES:]), style=ThemeKey.TOOL_RESULT))
|
|
113
|
+
else:
|
|
114
|
+
elements.append(Text(stripped_result, style=ThemeKey.TOOL_RESULT))
|
|
133
115
|
|
|
134
|
-
# No truncation needed - add description heading if provided
|
|
135
|
-
if description:
|
|
136
|
-
stripped_result = f"\n# {description}\n\n{stripped_result}"
|
|
137
|
-
normal_elements: list[RenderableType] = [Text(stripped_result)]
|
|
138
116
|
if agent_id_footer:
|
|
139
|
-
|
|
140
|
-
|
|
117
|
+
elements.append(Text(agent_id_footer, style=ThemeKey.SUB_AGENT_FOOTER))
|
|
118
|
+
|
|
119
|
+
return Group(*elements)
|
|
141
120
|
|
|
142
121
|
|
|
143
122
|
def build_sub_agent_state_from_tool_call(e: events.ToolCallEvent) -> model.SubAgentState | None:
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
2
|
+
|
|
3
|
+
from rich.console import Group, RenderableType
|
|
4
|
+
from rich.text import Text
|
|
5
|
+
|
|
6
|
+
from klaude_code.log import is_debug_enabled
|
|
7
|
+
from klaude_code.protocol import events
|
|
8
|
+
from klaude_code.tui.components.rich.quote import Quote
|
|
9
|
+
from klaude_code.tui.components.rich.theme import ThemeKey
|
|
10
|
+
from klaude_code.ui.common import format_model_params
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _get_version() -> str:
|
|
14
|
+
"""Get the current version of klaude-code."""
|
|
15
|
+
try:
|
|
16
|
+
return version("klaude-code")
|
|
17
|
+
except PackageNotFoundError:
|
|
18
|
+
return "unknown"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def render_welcome(e: events.WelcomeEvent) -> RenderableType:
|
|
22
|
+
"""Render the welcome panel with model info and settings.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
e: The welcome event.
|
|
26
|
+
"""
|
|
27
|
+
debug_mode = is_debug_enabled()
|
|
28
|
+
|
|
29
|
+
panel_content = Text()
|
|
30
|
+
|
|
31
|
+
if e.show_klaude_code_info:
|
|
32
|
+
# First line: Klaude Code version
|
|
33
|
+
klaude_code_style = ThemeKey.WELCOME_DEBUG_TITLE if debug_mode else ThemeKey.WELCOME_HIGHLIGHT_BOLD
|
|
34
|
+
panel_content.append_text(Text("Klaude Code", style=klaude_code_style))
|
|
35
|
+
panel_content.append_text(Text(f" v{_get_version()}", style=ThemeKey.WELCOME_INFO))
|
|
36
|
+
panel_content.append_text(Text("\n"))
|
|
37
|
+
|
|
38
|
+
# Model line: model @ provider · params...
|
|
39
|
+
panel_content.append_text(
|
|
40
|
+
Text.assemble(
|
|
41
|
+
(str(e.llm_config.model_id), ThemeKey.WELCOME_HIGHLIGHT),
|
|
42
|
+
(" @ ", ThemeKey.WELCOME_INFO),
|
|
43
|
+
(e.llm_config.provider_name, ThemeKey.WELCOME_INFO),
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Use format_model_params for consistent formatting
|
|
48
|
+
param_strings = format_model_params(e.llm_config)
|
|
49
|
+
|
|
50
|
+
# Check if we have sub-agent models to show
|
|
51
|
+
has_sub_agents = e.show_sub_agent_models and e.sub_agent_models
|
|
52
|
+
|
|
53
|
+
# Render config items with tree-style prefixes
|
|
54
|
+
for i, param_str in enumerate(param_strings):
|
|
55
|
+
is_last = i == len(param_strings) - 1 and not has_sub_agents
|
|
56
|
+
prefix = "└─ " if is_last else "├─ "
|
|
57
|
+
panel_content.append_text(
|
|
58
|
+
Text.assemble(
|
|
59
|
+
("\n", ThemeKey.WELCOME_INFO),
|
|
60
|
+
(prefix, ThemeKey.LINES),
|
|
61
|
+
(param_str, ThemeKey.WELCOME_INFO),
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Render sub-agent models
|
|
66
|
+
if has_sub_agents:
|
|
67
|
+
# Add sub-agents header with tree prefix
|
|
68
|
+
panel_content.append_text(
|
|
69
|
+
Text.assemble(
|
|
70
|
+
("\n", ThemeKey.WELCOME_INFO),
|
|
71
|
+
("└─ ", ThemeKey.LINES),
|
|
72
|
+
("sub-agents:", ThemeKey.WELCOME_INFO),
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
sub_agent_items = list(e.sub_agent_models.items())
|
|
76
|
+
max_type_len = max(len(t) for t in e.sub_agent_models)
|
|
77
|
+
for i, (sub_agent_type, sub_llm_config) in enumerate(sub_agent_items):
|
|
78
|
+
is_last = i == len(sub_agent_items) - 1
|
|
79
|
+
prefix = "└─ " if is_last else "├─ "
|
|
80
|
+
panel_content.append_text(
|
|
81
|
+
Text.assemble(
|
|
82
|
+
("\n", ThemeKey.WELCOME_INFO),
|
|
83
|
+
(" ", ThemeKey.WELCOME_INFO), # Indentation for sub-items
|
|
84
|
+
(prefix, ThemeKey.LINES),
|
|
85
|
+
(sub_agent_type.lower().ljust(max_type_len), ThemeKey.WELCOME_INFO),
|
|
86
|
+
(": ", ThemeKey.LINES),
|
|
87
|
+
(str(sub_llm_config.model_id), ThemeKey.WELCOME_HIGHLIGHT),
|
|
88
|
+
(" @ ", ThemeKey.WELCOME_INFO),
|
|
89
|
+
(sub_llm_config.provider_name, ThemeKey.WELCOME_INFO),
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
border_style = ThemeKey.WELCOME_DEBUG_BORDER if debug_mode else ThemeKey.LINES
|
|
94
|
+
|
|
95
|
+
if e.show_klaude_code_info:
|
|
96
|
+
groups = ["", Quote(panel_content, style=border_style, prefix="▌ "), ""]
|
|
97
|
+
else:
|
|
98
|
+
groups = [Quote(panel_content, style=border_style, prefix="▌ "), ""]
|
|
99
|
+
return Group(*groups)
|
|
@@ -26,7 +26,7 @@ from prompt_toolkit.styles import Style
|
|
|
26
26
|
from prompt_toolkit.utils import get_cwidth
|
|
27
27
|
|
|
28
28
|
from klaude_code.config import load_config
|
|
29
|
-
from klaude_code.config.
|
|
29
|
+
from klaude_code.config.model_matcher import match_model_from_config
|
|
30
30
|
from klaude_code.config.thinking import (
|
|
31
31
|
format_current_thinking,
|
|
32
32
|
get_thinking_picker_data,
|
|
@@ -452,22 +452,29 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
452
452
|
# -------------------------------------------------------------------------
|
|
453
453
|
|
|
454
454
|
def _build_model_picker_items(self) -> tuple[list[SelectItem[str]], str | None]:
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
config.iter_model_entries(only_available=True),
|
|
458
|
-
key=lambda m: m.model_name.lower(),
|
|
459
|
-
)
|
|
460
|
-
if not models:
|
|
455
|
+
result = match_model_from_config()
|
|
456
|
+
if result.error_message or not result.filtered_models:
|
|
461
457
|
return [], None
|
|
462
458
|
|
|
463
|
-
items = build_model_select_items(
|
|
459
|
+
items = build_model_select_items(result.filtered_models)
|
|
464
460
|
|
|
465
461
|
initial = None
|
|
466
462
|
if self._get_current_model_config_name is not None:
|
|
467
463
|
with contextlib.suppress(Exception):
|
|
468
464
|
initial = self._get_current_model_config_name()
|
|
469
465
|
if initial is None:
|
|
466
|
+
config = load_config()
|
|
470
467
|
initial = config.main_model
|
|
468
|
+
if isinstance(initial, str) and initial and "@" not in initial:
|
|
469
|
+
config = load_config()
|
|
470
|
+
try:
|
|
471
|
+
resolved = config.resolve_model_location_prefer_available(initial) or config.resolve_model_location(
|
|
472
|
+
initial
|
|
473
|
+
)
|
|
474
|
+
except ValueError:
|
|
475
|
+
resolved = None
|
|
476
|
+
if resolved is not None:
|
|
477
|
+
initial = f"{resolved[0]}@{resolved[1]}"
|
|
471
478
|
return items, initial
|
|
472
479
|
|
|
473
480
|
def _open_model_picker(self) -> None:
|
|
@@ -608,6 +615,10 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
608
615
|
(symbol_style, " ctrl-t "),
|
|
609
616
|
(text_style, " "),
|
|
610
617
|
(text_style, "think"),
|
|
618
|
+
(text_style, " "),
|
|
619
|
+
(symbol_style, " ctrl-v "),
|
|
620
|
+
(text_style, " "),
|
|
621
|
+
(text_style, "paste image"),
|
|
611
622
|
]
|
|
612
623
|
)
|
|
613
624
|
|
klaude_code/tui/machine.py
CHANGED
|
@@ -21,6 +21,7 @@ from klaude_code.tui.commands import (
|
|
|
21
21
|
PrintRuleLine,
|
|
22
22
|
RenderAssistantImage,
|
|
23
23
|
RenderCommand,
|
|
24
|
+
RenderCommandOutput,
|
|
24
25
|
RenderDeveloperMessage,
|
|
25
26
|
RenderError,
|
|
26
27
|
RenderInterrupt,
|
|
@@ -365,6 +366,10 @@ class DisplayStateMachine:
|
|
|
365
366
|
cmds.append(RenderDeveloperMessage(e))
|
|
366
367
|
return cmds
|
|
367
368
|
|
|
369
|
+
case events.CommandOutputEvent() as e:
|
|
370
|
+
cmds.append(RenderCommandOutput(e))
|
|
371
|
+
return cmds
|
|
372
|
+
|
|
368
373
|
case events.TurnStartEvent() as e:
|
|
369
374
|
cmds.append(RenderTurnStart(e))
|
|
370
375
|
self._spinner.clear_for_new_turn()
|
klaude_code/tui/renderer.py
CHANGED
|
@@ -31,6 +31,7 @@ from klaude_code.tui.commands import (
|
|
|
31
31
|
PrintRuleLine,
|
|
32
32
|
RenderAssistantImage,
|
|
33
33
|
RenderCommand,
|
|
34
|
+
RenderCommandOutput,
|
|
34
35
|
RenderDeveloperMessage,
|
|
35
36
|
RenderError,
|
|
36
37
|
RenderInterrupt,
|
|
@@ -53,6 +54,7 @@ from klaude_code.tui.commands import (
|
|
|
53
54
|
TaskClockStart,
|
|
54
55
|
)
|
|
55
56
|
from klaude_code.tui.components import assistant as c_assistant
|
|
57
|
+
from klaude_code.tui.components import command_output as c_command_output
|
|
56
58
|
from klaude_code.tui.components import developer as c_developer
|
|
57
59
|
from klaude_code.tui.components import errors as c_errors
|
|
58
60
|
from klaude_code.tui.components import mermaid_viewer as c_mermaid_viewer
|
|
@@ -61,6 +63,7 @@ from klaude_code.tui.components import sub_agent as c_sub_agent
|
|
|
61
63
|
from klaude_code.tui.components import thinking as c_thinking
|
|
62
64
|
from klaude_code.tui.components import tools as c_tools
|
|
63
65
|
from klaude_code.tui.components import user_input as c_user_input
|
|
66
|
+
from klaude_code.tui.components import welcome as c_welcome
|
|
64
67
|
from klaude_code.tui.components.common import truncate_head, truncate_middle
|
|
65
68
|
from klaude_code.tui.components.rich import status as r_status
|
|
66
69
|
from klaude_code.tui.components.rich.live import CropAboveLive, SingleLine
|
|
@@ -470,7 +473,6 @@ class TUICommandRenderer:
|
|
|
470
473
|
self.print()
|
|
471
474
|
case events.DeveloperMessageEvent() as e:
|
|
472
475
|
self.display_developer_message(e)
|
|
473
|
-
self.display_command_output(e)
|
|
474
476
|
case events.UserMessageEvent() as e:
|
|
475
477
|
if is_sub_agent:
|
|
476
478
|
continue
|
|
@@ -503,15 +505,13 @@ class TUICommandRenderer:
|
|
|
503
505
|
with self.session_print_context(e.session_id):
|
|
504
506
|
self.print(c_developer.render_developer_message(e))
|
|
505
507
|
|
|
506
|
-
def display_command_output(self, e: events.
|
|
507
|
-
if not c_developer.get_command_output(e.item):
|
|
508
|
-
return
|
|
508
|
+
def display_command_output(self, e: events.CommandOutputEvent) -> None:
|
|
509
509
|
with self.session_print_context(e.session_id):
|
|
510
|
-
self.print(
|
|
510
|
+
self.print(c_command_output.render_command_output(e))
|
|
511
511
|
self.print()
|
|
512
512
|
|
|
513
513
|
def display_welcome(self, event: events.WelcomeEvent) -> None:
|
|
514
|
-
self.print(
|
|
514
|
+
self.print(c_welcome.render_welcome(event))
|
|
515
515
|
|
|
516
516
|
def display_user_message(self, event: events.UserMessageEvent) -> None:
|
|
517
517
|
self.print(c_user_input.render_user_input(event.content))
|
|
@@ -567,10 +567,8 @@ class TUICommandRenderer:
|
|
|
567
567
|
self.print(
|
|
568
568
|
c_sub_agent.render_sub_agent_result(
|
|
569
569
|
event.task_result,
|
|
570
|
-
code_theme=self.themes.code_theme,
|
|
571
570
|
has_structured_output=event.has_structured_output,
|
|
572
571
|
description=description,
|
|
573
|
-
style=ThemeKey.TOOL_RESULT,
|
|
574
572
|
)
|
|
575
573
|
)
|
|
576
574
|
|
|
@@ -628,6 +626,7 @@ class TUICommandRenderer:
|
|
|
628
626
|
self.display_task_start(event)
|
|
629
627
|
case RenderDeveloperMessage(event=event):
|
|
630
628
|
self.display_developer_message(event)
|
|
629
|
+
case RenderCommandOutput(event=event):
|
|
631
630
|
self.display_command_output(event)
|
|
632
631
|
case RenderTurnStart(event=event):
|
|
633
632
|
self.display_turn_start(event)
|
klaude_code/tui/runner.py
CHANGED
|
@@ -76,14 +76,8 @@ async def submit_user_input_payload(
|
|
|
76
76
|
if len(run_ops) > 1:
|
|
77
77
|
raise ValueError("Multiple RunAgentOperation results are not supported")
|
|
78
78
|
|
|
79
|
-
for run_op in run_ops:
|
|
80
|
-
run_op.persist_user_input = cmd_result.persist
|
|
81
|
-
run_op.emit_user_message_event = False
|
|
82
|
-
|
|
83
79
|
if cmd_result.events:
|
|
84
80
|
for evt in cmd_result.events:
|
|
85
|
-
if cmd_result.persist and isinstance(evt, events.DeveloperMessageEvent):
|
|
86
|
-
agent.session.append_history([evt.item])
|
|
87
81
|
await executor.context.emit_event(evt)
|
|
88
82
|
|
|
89
83
|
submitted_ids: list[str] = []
|
|
@@ -53,19 +53,21 @@ def build_model_select_items(models: list[Any]) -> list[SelectItem[str]]:
|
|
|
53
53
|
|
|
54
54
|
items: list[SelectItem[str]] = []
|
|
55
55
|
for idx, m in enumerate(models, 1):
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
model_id_str = m.model_id or "N/A"
|
|
57
|
+
# Display the base model name only; provider stays in the meta section.
|
|
58
|
+
display_name = m.model_name
|
|
59
|
+
first_line_prefix = f"{display_name:<{max_model_name_length}} → "
|
|
58
60
|
meta_parts: list[str] = [m.provider]
|
|
59
|
-
meta_parts.extend(format_model_params(m
|
|
61
|
+
meta_parts.extend(format_model_params(m))
|
|
60
62
|
meta_str = " · ".join(meta_parts)
|
|
61
63
|
title = [
|
|
62
64
|
("class:meta", f"{idx:>{num_width}}. "),
|
|
63
65
|
("class:msg", first_line_prefix),
|
|
64
|
-
("class:msg bold",
|
|
66
|
+
("class:msg bold", model_id_str),
|
|
65
67
|
("class:meta", f" {meta_str}\n"),
|
|
66
68
|
]
|
|
67
|
-
search_text = f"{m.model_name} {
|
|
68
|
-
items.append(SelectItem(title=title, value=m.
|
|
69
|
+
search_text = f"{m.selector} {m.model_name} {model_id_str} {m.provider}"
|
|
70
|
+
items.append(SelectItem(title=title, value=m.selector, search_text=search_text))
|
|
69
71
|
|
|
70
72
|
return items
|
|
71
73
|
|