klaude-code 1.2.22__py3-none-any.whl → 1.2.24__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/command/prompt-jj-describe.md +32 -0
- klaude_code/command/status_cmd.py +1 -1
- klaude_code/{const/__init__.py → const.py} +11 -2
- klaude_code/core/executor.py +1 -1
- klaude_code/core/manager/sub_agent_manager.py +1 -1
- klaude_code/core/reminders.py +51 -0
- klaude_code/core/task.py +37 -18
- klaude_code/core/tool/__init__.py +1 -4
- klaude_code/core/tool/file/read_tool.py +23 -1
- klaude_code/core/tool/file/write_tool.py +7 -3
- klaude_code/core/tool/skill/__init__.py +0 -0
- klaude_code/core/tool/{memory → skill}/skill_tool.py +16 -39
- klaude_code/llm/openai_compatible/client.py +29 -102
- klaude_code/llm/openai_compatible/stream.py +272 -0
- klaude_code/llm/openrouter/client.py +29 -109
- klaude_code/llm/openrouter/{reasoning_handler.py → reasoning.py} +24 -2
- klaude_code/protocol/model.py +15 -2
- klaude_code/session/export.py +1 -1
- klaude_code/session/store.py +4 -2
- klaude_code/skill/__init__.py +27 -0
- klaude_code/skill/assets/deslop/SKILL.md +17 -0
- klaude_code/skill/assets/dev-docs/SKILL.md +108 -0
- klaude_code/skill/assets/handoff/SKILL.md +39 -0
- klaude_code/skill/assets/jj-workspace/SKILL.md +20 -0
- klaude_code/skill/assets/skill-creator/SKILL.md +139 -0
- klaude_code/{core/tool/memory/skill_loader.py → skill/loader.py} +60 -24
- klaude_code/skill/manager.py +70 -0
- klaude_code/skill/system_skills.py +192 -0
- klaude_code/ui/core/stage_manager.py +0 -3
- klaude_code/ui/modes/repl/completers.py +103 -3
- klaude_code/ui/modes/repl/event_handler.py +101 -49
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +55 -6
- klaude_code/ui/modes/repl/renderer.py +24 -17
- klaude_code/ui/renderers/assistant.py +7 -2
- klaude_code/ui/renderers/developer.py +12 -0
- klaude_code/ui/renderers/diffs.py +1 -1
- klaude_code/ui/renderers/metadata.py +6 -8
- klaude_code/ui/renderers/sub_agent.py +28 -5
- klaude_code/ui/renderers/thinking.py +16 -10
- klaude_code/ui/renderers/tools.py +83 -34
- klaude_code/ui/renderers/user_input.py +32 -2
- klaude_code/ui/rich/markdown.py +40 -20
- klaude_code/ui/rich/status.py +15 -19
- klaude_code/ui/rich/theme.py +70 -17
- {klaude_code-1.2.22.dist-info → klaude_code-1.2.24.dist-info}/METADATA +18 -13
- {klaude_code-1.2.22.dist-info → klaude_code-1.2.24.dist-info}/RECORD +49 -45
- klaude_code/command/prompt-deslop.md +0 -14
- klaude_code/command/prompt-dev-docs-update.md +0 -56
- klaude_code/command/prompt-dev-docs.md +0 -46
- klaude_code/command/prompt-handoff.md +0 -33
- klaude_code/command/prompt-jj-workspace.md +0 -18
- klaude_code/core/tool/memory/__init__.py +0 -5
- klaude_code/llm/openai_compatible/stream_processor.py +0 -83
- /klaude_code/core/tool/{memory → skill}/skill_tool.md +0 -0
- {klaude_code-1.2.22.dist-info → klaude_code-1.2.24.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.22.dist-info → klaude_code-1.2.24.dist-info}/entry_points.txt +0 -0
|
@@ -7,6 +7,7 @@ from typing import NamedTuple, override
|
|
|
7
7
|
|
|
8
8
|
from prompt_toolkit import PromptSession
|
|
9
9
|
from prompt_toolkit.completion import ThreadedCompleter
|
|
10
|
+
from prompt_toolkit.cursor_shapes import CursorShape
|
|
10
11
|
from prompt_toolkit.formatted_text import FormattedText
|
|
11
12
|
from prompt_toolkit.history import FileHistory
|
|
12
13
|
from prompt_toolkit.patch_stdout import patch_stdout
|
|
@@ -17,6 +18,8 @@ from klaude_code.ui.core.input import InputProviderABC
|
|
|
17
18
|
from klaude_code.ui.modes.repl.clipboard import capture_clipboard_tag, copy_to_clipboard, extract_images_from_text
|
|
18
19
|
from klaude_code.ui.modes.repl.completers import AT_TOKEN_PATTERN, create_repl_completer
|
|
19
20
|
from klaude_code.ui.modes.repl.key_bindings import create_key_bindings
|
|
21
|
+
from klaude_code.ui.renderers.user_input import USER_MESSAGE_MARK
|
|
22
|
+
from klaude_code.ui.terminal.color import is_light_terminal_background
|
|
20
23
|
from klaude_code.ui.utils.common import get_current_git_branch, show_path_with_tilde
|
|
21
24
|
|
|
22
25
|
|
|
@@ -30,18 +33,27 @@ class REPLStatusSnapshot(NamedTuple):
|
|
|
30
33
|
update_message: str | None = None
|
|
31
34
|
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
COMPLETION_SELECTED_DARK_BG = "#8b9bff"
|
|
37
|
+
COMPLETION_SELECTED_LIGHT_BG = "#5869f7"
|
|
38
|
+
COMPLETION_SELECTED_UNKNOWN_BG = "#7080f0"
|
|
34
39
|
COMPLETION_MENU = "ansibrightblack"
|
|
35
|
-
INPUT_PROMPT_STYLE = "ansimagenta"
|
|
40
|
+
INPUT_PROMPT_STYLE = "ansimagenta bold"
|
|
41
|
+
PLACEHOLDER_TEXT_STYLE_DARK_BG = "fg:#5a5a5a italic"
|
|
42
|
+
PLACEHOLDER_TEXT_STYLE_LIGHT_BG = "fg:#7a7a7a italic"
|
|
43
|
+
PLACEHOLDER_TEXT_STYLE_UNKNOWN_BG = "fg:#8a8a8a italic"
|
|
44
|
+
PLACEHOLDER_SYMBOL_STYLE_DARK_BG = "bg:#2a2a2a fg:#5a5a5a"
|
|
45
|
+
PLACEHOLDER_SYMBOL_STYLE_LIGHT_BG = "bg:#e6e6e6 fg:#7a7a7a"
|
|
46
|
+
PLACEHOLDER_SYMBOL_STYLE_UNKNOWN_BG = "bg:#2a2a2a fg:#8a8a8a"
|
|
36
47
|
|
|
37
48
|
|
|
38
49
|
class PromptToolkitInput(InputProviderABC):
|
|
39
50
|
def __init__(
|
|
40
51
|
self,
|
|
41
|
-
prompt: str =
|
|
52
|
+
prompt: str = USER_MESSAGE_MARK,
|
|
42
53
|
status_provider: Callable[[], REPLStatusSnapshot] | None = None,
|
|
43
54
|
): # ▌
|
|
44
55
|
self._status_provider = status_provider
|
|
56
|
+
self._is_light_terminal_background = is_light_terminal_background(timeout=0.2)
|
|
45
57
|
|
|
46
58
|
project = str(Path.cwd()).strip("/").replace("/", "-")
|
|
47
59
|
history_path = Path.home() / ".klaude" / "projects" / project / "input" / "input_history.txt"
|
|
@@ -56,10 +68,19 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
56
68
|
at_token_pattern=AT_TOKEN_PATTERN,
|
|
57
69
|
)
|
|
58
70
|
|
|
71
|
+
# Select completion selected color based on terminal background
|
|
72
|
+
if self._is_light_terminal_background is True:
|
|
73
|
+
completion_selected = COMPLETION_SELECTED_LIGHT_BG
|
|
74
|
+
elif self._is_light_terminal_background is False:
|
|
75
|
+
completion_selected = COMPLETION_SELECTED_DARK_BG
|
|
76
|
+
else:
|
|
77
|
+
completion_selected = COMPLETION_SELECTED_UNKNOWN_BG
|
|
78
|
+
|
|
59
79
|
self._session: PromptSession[str] = PromptSession(
|
|
60
80
|
[(INPUT_PROMPT_STYLE, prompt)],
|
|
61
81
|
history=FileHistory(str(history_path)),
|
|
62
82
|
multiline=True,
|
|
83
|
+
cursor=CursorShape.BEAM,
|
|
63
84
|
prompt_continuation=[(INPUT_PROMPT_STYLE, " ")],
|
|
64
85
|
key_bindings=kb,
|
|
65
86
|
completer=ThreadedCompleter(create_repl_completer()),
|
|
@@ -75,8 +96,8 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
75
96
|
"scrollbar.button": "bg:default",
|
|
76
97
|
"completion-menu.completion": f"bg:default fg:{COMPLETION_MENU}",
|
|
77
98
|
"completion-menu.meta.completion": f"bg:default fg:{COMPLETION_MENU}",
|
|
78
|
-
"completion-menu.completion.current": f"noreverse bg:default fg:{
|
|
79
|
-
"completion-menu.meta.completion.current": f"bg:default fg:{
|
|
99
|
+
"completion-menu.completion.current": f"noreverse bg:default fg:{completion_selected} bold",
|
|
100
|
+
"completion-menu.meta.completion.current": f"bg:default fg:{completion_selected} bold",
|
|
80
101
|
}
|
|
81
102
|
),
|
|
82
103
|
)
|
|
@@ -144,6 +165,34 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
144
165
|
toolbar_text = left_text + padding + right_text
|
|
145
166
|
return FormattedText([("#2c7eac", toolbar_text)])
|
|
146
167
|
|
|
168
|
+
def _render_input_placeholder(self) -> FormattedText:
|
|
169
|
+
if self._is_light_terminal_background is True:
|
|
170
|
+
text_style = PLACEHOLDER_TEXT_STYLE_LIGHT_BG
|
|
171
|
+
symbol_style = PLACEHOLDER_SYMBOL_STYLE_LIGHT_BG
|
|
172
|
+
elif self._is_light_terminal_background is False:
|
|
173
|
+
text_style = PLACEHOLDER_TEXT_STYLE_DARK_BG
|
|
174
|
+
symbol_style = PLACEHOLDER_SYMBOL_STYLE_DARK_BG
|
|
175
|
+
else:
|
|
176
|
+
text_style = PLACEHOLDER_TEXT_STYLE_UNKNOWN_BG
|
|
177
|
+
symbol_style = PLACEHOLDER_SYMBOL_STYLE_UNKNOWN_BG
|
|
178
|
+
|
|
179
|
+
return FormattedText(
|
|
180
|
+
[
|
|
181
|
+
(text_style, " " * 10),
|
|
182
|
+
(symbol_style, " @ "),
|
|
183
|
+
(text_style, " "),
|
|
184
|
+
(text_style, "files"),
|
|
185
|
+
(text_style, " "),
|
|
186
|
+
(symbol_style, " $ "),
|
|
187
|
+
(text_style, " "),
|
|
188
|
+
(text_style, "skills"),
|
|
189
|
+
(text_style, " "),
|
|
190
|
+
(symbol_style, " / "),
|
|
191
|
+
(text_style, " "),
|
|
192
|
+
(text_style, "commands"),
|
|
193
|
+
]
|
|
194
|
+
)
|
|
195
|
+
|
|
147
196
|
async def start(self) -> None:
|
|
148
197
|
pass
|
|
149
198
|
|
|
@@ -154,7 +203,7 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
154
203
|
async def iter_inputs(self) -> AsyncIterator[UserInputPayload]:
|
|
155
204
|
while True:
|
|
156
205
|
with patch_stdout():
|
|
157
|
-
line: str = await self._session.prompt_async()
|
|
206
|
+
line: str = await self._session.prompt_async(placeholder=self._render_input_placeholder())
|
|
158
207
|
|
|
159
208
|
# Extract images referenced in the input text
|
|
160
209
|
images = extract_images_from_text(line)
|
|
@@ -5,14 +5,13 @@ from contextlib import contextmanager
|
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from rich import box
|
|
9
|
-
from rich.box import Box
|
|
10
8
|
from rich.console import Console
|
|
11
9
|
from rich.spinner import Spinner
|
|
12
10
|
from rich.status import Status
|
|
13
11
|
from rich.style import Style, StyleType
|
|
14
12
|
from rich.text import Text
|
|
15
13
|
|
|
14
|
+
from klaude_code import const
|
|
16
15
|
from klaude_code.protocol import events, model
|
|
17
16
|
from klaude_code.ui.renderers import assistant as r_assistant
|
|
18
17
|
from klaude_code.ui.renderers import developer as r_developer
|
|
@@ -32,6 +31,7 @@ from klaude_code.ui.rich.theme import ThemeKey, get_theme
|
|
|
32
31
|
@dataclass
|
|
33
32
|
class SessionStatus:
|
|
34
33
|
color: Style | None = None
|
|
34
|
+
color_index: int | None = None
|
|
35
35
|
sub_agent_state: model.SubAgentState | None = None
|
|
36
36
|
|
|
37
37
|
|
|
@@ -43,9 +43,9 @@ class REPLRenderer:
|
|
|
43
43
|
self.console: Console = Console(theme=self.themes.app_theme)
|
|
44
44
|
self.console.push_theme(self.themes.markdown_theme)
|
|
45
45
|
self._spinner: Status = self.console.status(
|
|
46
|
-
ShimmerStatusText(
|
|
46
|
+
ShimmerStatusText(const.STATUS_DEFAULT_TEXT),
|
|
47
47
|
spinner=r_status.spinner_name(),
|
|
48
|
-
spinner_style=ThemeKey.
|
|
48
|
+
spinner_style=ThemeKey.STATUS_SPINNER,
|
|
49
49
|
)
|
|
50
50
|
|
|
51
51
|
self.session_map: dict[str, SessionStatus] = {}
|
|
@@ -57,7 +57,9 @@ class REPLRenderer:
|
|
|
57
57
|
sub_agent_state=sub_agent_state,
|
|
58
58
|
)
|
|
59
59
|
if sub_agent_state is not None:
|
|
60
|
-
|
|
60
|
+
color, color_index = self.pick_sub_agent_color()
|
|
61
|
+
session_status.color = color
|
|
62
|
+
session_status.color_index = color_index
|
|
61
63
|
self.session_map[session_id] = session_status
|
|
62
64
|
|
|
63
65
|
def is_sub_agent_session(self, session_id: str) -> bool:
|
|
@@ -70,12 +72,12 @@ class REPLRenderer:
|
|
|
70
72
|
return
|
|
71
73
|
self.sub_agent_color_index = (self.sub_agent_color_index + 1) % palette_size
|
|
72
74
|
|
|
73
|
-
def pick_sub_agent_color(self) -> Style:
|
|
75
|
+
def pick_sub_agent_color(self) -> tuple[Style, int]:
|
|
74
76
|
self._advance_sub_agent_color_index()
|
|
75
77
|
palette = self.themes.sub_agent_colors
|
|
76
78
|
if not palette:
|
|
77
|
-
return Style()
|
|
78
|
-
return palette[self.sub_agent_color_index]
|
|
79
|
+
return Style(), 0
|
|
80
|
+
return palette[self.sub_agent_color_index], self.sub_agent_color_index
|
|
79
81
|
|
|
80
82
|
def get_session_sub_agent_color(self, session_id: str) -> Style:
|
|
81
83
|
status = self.session_map.get(session_id)
|
|
@@ -83,8 +85,12 @@ class REPLRenderer:
|
|
|
83
85
|
return status.color
|
|
84
86
|
return Style()
|
|
85
87
|
|
|
86
|
-
def
|
|
87
|
-
|
|
88
|
+
def get_session_sub_agent_background(self, session_id: str) -> Style:
|
|
89
|
+
status = self.session_map.get(session_id)
|
|
90
|
+
backgrounds = self.themes.sub_agent_backgrounds
|
|
91
|
+
if status and status.color_index is not None and backgrounds:
|
|
92
|
+
return backgrounds[status.color_index]
|
|
93
|
+
return Style()
|
|
88
94
|
|
|
89
95
|
@contextmanager
|
|
90
96
|
def session_print_context(self, session_id: str) -> Iterator[None]:
|
|
@@ -114,7 +120,7 @@ class REPLRenderer:
|
|
|
114
120
|
def display_tool_call_result(self, e: events.ToolResultEvent) -> None:
|
|
115
121
|
if r_tools.is_sub_agent_tool(e.tool_name):
|
|
116
122
|
return
|
|
117
|
-
renderable = r_tools.render_tool_result(e)
|
|
123
|
+
renderable = r_tools.render_tool_result(e, code_theme=self.themes.code_theme)
|
|
118
124
|
if renderable is not None:
|
|
119
125
|
self.print(renderable)
|
|
120
126
|
|
|
@@ -152,7 +158,6 @@ class REPLRenderer:
|
|
|
152
158
|
case events.ThinkingEvent() as e:
|
|
153
159
|
if is_sub_agent:
|
|
154
160
|
continue
|
|
155
|
-
self.display_thinking_prefix()
|
|
156
161
|
self.display_thinking(e.content)
|
|
157
162
|
case events.DeveloperMessageEvent() as e:
|
|
158
163
|
self.display_developer_message(e)
|
|
@@ -196,7 +201,7 @@ class REPLRenderer:
|
|
|
196
201
|
self.print()
|
|
197
202
|
|
|
198
203
|
def display_welcome(self, event: events.WelcomeEvent) -> None:
|
|
199
|
-
self.print(r_metadata.render_welcome(event
|
|
204
|
+
self.print(r_metadata.render_welcome(event))
|
|
200
205
|
|
|
201
206
|
def display_user_message(self, event: events.UserMessageEvent) -> None:
|
|
202
207
|
self.print(r_user_input.render_user_input(event.content))
|
|
@@ -229,12 +234,17 @@ class REPLRenderer:
|
|
|
229
234
|
|
|
230
235
|
def display_task_finish(self, event: events.TaskFinishEvent) -> None:
|
|
231
236
|
if self.is_sub_agent_session(event.session_id):
|
|
237
|
+
session_status = self.session_map.get(event.session_id)
|
|
238
|
+
description = session_status.sub_agent_state.sub_agent_desc if session_status and session_status.sub_agent_state else None
|
|
239
|
+
panel_style = self.get_session_sub_agent_background(event.session_id)
|
|
232
240
|
with self.session_print_context(event.session_id):
|
|
233
241
|
self.print(
|
|
234
242
|
r_sub_agent.render_sub_agent_result(
|
|
235
243
|
event.task_result,
|
|
236
244
|
code_theme=self.themes.code_theme,
|
|
237
245
|
has_structured_output=event.has_structured_output,
|
|
246
|
+
description=description,
|
|
247
|
+
panel_style=panel_style,
|
|
238
248
|
)
|
|
239
249
|
)
|
|
240
250
|
|
|
@@ -249,9 +259,6 @@ class REPLRenderer:
|
|
|
249
259
|
)
|
|
250
260
|
)
|
|
251
261
|
|
|
252
|
-
def display_thinking_prefix(self) -> None:
|
|
253
|
-
self.print(r_thinking.thinking_prefix())
|
|
254
|
-
|
|
255
262
|
# -------------------------------------------------------------------------
|
|
256
263
|
# Spinner control methods
|
|
257
264
|
# -------------------------------------------------------------------------
|
|
@@ -266,7 +273,7 @@ class REPLRenderer:
|
|
|
266
273
|
|
|
267
274
|
def spinner_update(self, status_text: str | Text, right_text: Text | None = None) -> None:
|
|
268
275
|
"""Update the spinner status text with optional right-aligned text."""
|
|
269
|
-
self._spinner.update(ShimmerStatusText(status_text,
|
|
276
|
+
self._spinner.update(ShimmerStatusText(status_text, right_text))
|
|
270
277
|
|
|
271
278
|
def spinner_renderable(self) -> Spinner:
|
|
272
279
|
"""Return the spinner's renderable for embedding in other components."""
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
from rich.console import RenderableType
|
|
2
|
+
from rich.padding import Padding
|
|
2
3
|
|
|
4
|
+
from klaude_code import const
|
|
3
5
|
from klaude_code.ui.renderers.common import create_grid
|
|
4
6
|
from klaude_code.ui.rich.markdown import NoInsetMarkdown
|
|
5
7
|
|
|
8
|
+
# UI markers
|
|
9
|
+
ASSISTANT_MESSAGE_MARK = "◆"
|
|
10
|
+
|
|
6
11
|
|
|
7
12
|
def render_assistant_message(content: str, *, code_theme: str) -> RenderableType | None:
|
|
8
13
|
"""Render assistant message for replay history display.
|
|
@@ -15,7 +20,7 @@ def render_assistant_message(content: str, *, code_theme: str) -> RenderableType
|
|
|
15
20
|
|
|
16
21
|
grid = create_grid()
|
|
17
22
|
grid.add_row(
|
|
18
|
-
|
|
19
|
-
NoInsetMarkdown(stripped, code_theme=code_theme),
|
|
23
|
+
ASSISTANT_MESSAGE_MARK,
|
|
24
|
+
Padding(NoInsetMarkdown(stripped, code_theme=code_theme), (0, const.MARKDOWN_RIGHT_MARGIN, 0, 0)),
|
|
20
25
|
)
|
|
21
26
|
return grid
|
|
@@ -17,6 +17,7 @@ def need_render_developer_message(e: events.DeveloperMessageEvent) -> bool:
|
|
|
17
17
|
or e.item.todo_use
|
|
18
18
|
or e.item.at_files
|
|
19
19
|
or e.item.user_image_count
|
|
20
|
+
or e.item.skill_name
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
|
|
@@ -93,6 +94,17 @@ def render_developer_message(e: events.DeveloperMessageEvent) -> RenderableType:
|
|
|
93
94
|
)
|
|
94
95
|
parts.append(grid)
|
|
95
96
|
|
|
97
|
+
if sn := e.item.skill_name:
|
|
98
|
+
grid = create_grid()
|
|
99
|
+
grid.add_row(
|
|
100
|
+
Text(" +", style=ThemeKey.REMINDER),
|
|
101
|
+
Text.assemble(
|
|
102
|
+
("Activated skill ", ThemeKey.REMINDER),
|
|
103
|
+
(sn, ThemeKey.REMINDER_BOLD),
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
parts.append(grid)
|
|
107
|
+
|
|
96
108
|
return Group(*parts) if parts else Text("")
|
|
97
109
|
|
|
98
110
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from importlib.metadata import version
|
|
2
2
|
|
|
3
3
|
from rich import box
|
|
4
|
-
from rich.box import Box
|
|
5
4
|
from rich.console import Group, RenderableType
|
|
6
5
|
from rich.padding import Padding
|
|
7
6
|
from rich.panel import Panel
|
|
@@ -45,7 +44,7 @@ def _render_task_metadata_block(
|
|
|
45
44
|
currency_symbol = "¥" if currency == "CNY" else "$"
|
|
46
45
|
|
|
47
46
|
# First column: mark only
|
|
48
|
-
mark = Text("└", style=ThemeKey.METADATA_DIM) if is_sub_agent else Text("
|
|
47
|
+
mark = Text("└", style=ThemeKey.METADATA_DIM) if is_sub_agent else Text("⇅", style=ThemeKey.METADATA)
|
|
49
48
|
|
|
50
49
|
# Second column: model@provider / tokens / cost / ...
|
|
51
50
|
content = Text()
|
|
@@ -151,7 +150,9 @@ def render_task_metadata(e: events.TaskMetadataEvent) -> RenderableType:
|
|
|
151
150
|
"""Render task metadata including main agent and sub-agents, aggregated by model+provider."""
|
|
152
151
|
renderables: list[RenderableType] = []
|
|
153
152
|
|
|
154
|
-
renderables.append(
|
|
153
|
+
renderables.append(
|
|
154
|
+
_render_task_metadata_block(e.metadata.main_agent, is_sub_agent=False, show_context_and_time=True)
|
|
155
|
+
)
|
|
155
156
|
|
|
156
157
|
# Aggregate by (model_name, provider), sorted by total_cost descending
|
|
157
158
|
sorted_items = model.TaskMetadata.aggregate_by_model(e.metadata.sub_agent_task_metadata)
|
|
@@ -163,11 +164,8 @@ def render_task_metadata(e: events.TaskMetadataEvent) -> RenderableType:
|
|
|
163
164
|
return Group(*renderables)
|
|
164
165
|
|
|
165
166
|
|
|
166
|
-
def render_welcome(e: events.WelcomeEvent
|
|
167
|
+
def render_welcome(e: events.WelcomeEvent) -> RenderableType:
|
|
167
168
|
"""Render the welcome panel with model info and settings."""
|
|
168
|
-
if box_style is None:
|
|
169
|
-
box_style = box.ROUNDED
|
|
170
|
-
|
|
171
169
|
debug_mode = is_debug_enabled()
|
|
172
170
|
|
|
173
171
|
# First line: Klaude Code version
|
|
@@ -217,6 +215,6 @@ def render_welcome(e: events.WelcomeEvent, *, box_style: Box | None = None) -> R
|
|
|
217
215
|
|
|
218
216
|
border_style = ThemeKey.WELCOME_DEBUG_BORDER if debug_mode else ThemeKey.LINES
|
|
219
217
|
return Group(
|
|
220
|
-
Panel.fit(panel_content, border_style=border_style, box=
|
|
218
|
+
Panel.fit(panel_content, border_style=border_style, box=box.ROUNDED),
|
|
221
219
|
"", # empty line
|
|
222
220
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from typing import Any, cast
|
|
3
3
|
|
|
4
|
+
from rich import box
|
|
4
5
|
from rich.console import Group, RenderableType
|
|
5
6
|
from rich.json import JSON
|
|
6
7
|
from rich.panel import Panel
|
|
@@ -58,10 +59,22 @@ def render_sub_agent_call(e: model.SubAgentState, style: Style | None = None) ->
|
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
def render_sub_agent_result(
|
|
61
|
-
result: str,
|
|
62
|
+
result: str,
|
|
63
|
+
*,
|
|
64
|
+
code_theme: str,
|
|
65
|
+
style: Style | None = None,
|
|
66
|
+
has_structured_output: bool = False,
|
|
67
|
+
description: str | None = None,
|
|
68
|
+
panel_style: Style | None = None,
|
|
62
69
|
) -> RenderableType:
|
|
63
70
|
stripped_result = result.strip()
|
|
64
71
|
|
|
72
|
+
# Add markdown heading if description is provided
|
|
73
|
+
if description:
|
|
74
|
+
stripped_result = f"# {description}\n\n{stripped_result}"
|
|
75
|
+
|
|
76
|
+
result_panel_style = panel_style or ThemeKey.SUB_AGENT_RESULT_PANEL
|
|
77
|
+
|
|
65
78
|
# Use rich JSON for structured output
|
|
66
79
|
if has_structured_output:
|
|
67
80
|
try:
|
|
@@ -73,7 +86,9 @@ def render_sub_agent_result(
|
|
|
73
86
|
),
|
|
74
87
|
JSON(stripped_result),
|
|
75
88
|
),
|
|
89
|
+
box=box.SIMPLE,
|
|
76
90
|
border_style=ThemeKey.LINES,
|
|
91
|
+
style=result_panel_style,
|
|
77
92
|
)
|
|
78
93
|
except json.JSONDecodeError:
|
|
79
94
|
# Fall back to markdown if not valid JSON
|
|
@@ -82,20 +97,28 @@ def render_sub_agent_result(
|
|
|
82
97
|
lines = stripped_result.splitlines()
|
|
83
98
|
if len(lines) > const.SUB_AGENT_RESULT_MAX_LINES:
|
|
84
99
|
hidden_count = len(lines) - const.SUB_AGENT_RESULT_MAX_LINES
|
|
85
|
-
|
|
100
|
+
head_count = const.SUB_AGENT_RESULT_MAX_LINES // 2
|
|
101
|
+
tail_count = const.SUB_AGENT_RESULT_MAX_LINES - head_count
|
|
102
|
+
head_text = "\n".join(lines[:head_count])
|
|
103
|
+
tail_text = "\n".join(lines[-tail_count:])
|
|
86
104
|
return Panel.fit(
|
|
87
105
|
Group(
|
|
106
|
+
NoInsetMarkdown(head_text, code_theme=code_theme, style=style or ""),
|
|
88
107
|
Text(
|
|
89
|
-
f"… more {hidden_count} lines — use /export to view full output",
|
|
90
|
-
style=ThemeKey.
|
|
108
|
+
f"\n… more {hidden_count} lines — use /export to view full output\n",
|
|
109
|
+
style=ThemeKey.TOOL_RESULT_TRUNCATED,
|
|
91
110
|
),
|
|
92
|
-
NoInsetMarkdown(
|
|
111
|
+
NoInsetMarkdown(tail_text, code_theme=code_theme, style=style or ""),
|
|
93
112
|
),
|
|
113
|
+
box=box.SIMPLE,
|
|
94
114
|
border_style=ThemeKey.LINES,
|
|
115
|
+
style=result_panel_style,
|
|
95
116
|
)
|
|
96
117
|
return Panel.fit(
|
|
97
118
|
NoInsetMarkdown(stripped_result, code_theme=code_theme),
|
|
119
|
+
box=box.SIMPLE,
|
|
98
120
|
border_style=ThemeKey.LINES,
|
|
121
|
+
style=result_panel_style,
|
|
99
122
|
)
|
|
100
123
|
|
|
101
124
|
|
|
@@ -4,12 +4,13 @@ from rich.console import RenderableType
|
|
|
4
4
|
from rich.padding import Padding
|
|
5
5
|
from rich.text import Text
|
|
6
6
|
|
|
7
|
+
from klaude_code import const
|
|
8
|
+
from klaude_code.ui.renderers.common import create_grid
|
|
7
9
|
from klaude_code.ui.rich.markdown import ThinkingMarkdown
|
|
8
10
|
from klaude_code.ui.rich.theme import ThemeKey
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return Text.from_markup("[not italic]⸫[/not italic] Thinking …", style=ThemeKey.THINKING)
|
|
12
|
+
# UI markers
|
|
13
|
+
THINKING_MESSAGE_MARK = "⠶"
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def normalize_thinking_content(content: str) -> str:
|
|
@@ -37,7 +38,7 @@ def normalize_thinking_content(content: str) -> str:
|
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
def render_thinking(content: str, *, code_theme: str, style: str) -> RenderableType | None:
|
|
40
|
-
"""Render thinking content as
|
|
41
|
+
"""Render thinking content as markdown with left mark.
|
|
41
42
|
|
|
42
43
|
Returns None if content is empty.
|
|
43
44
|
Note: Caller should push thinking_markdown_theme before printing.
|
|
@@ -45,11 +46,16 @@ def render_thinking(content: str, *, code_theme: str, style: str) -> RenderableT
|
|
|
45
46
|
if len(content.strip()) == 0:
|
|
46
47
|
return None
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
grid = create_grid()
|
|
50
|
+
grid.add_row(
|
|
51
|
+
Text(THINKING_MESSAGE_MARK, style=ThemeKey.THINKING),
|
|
52
|
+
Padding(
|
|
53
|
+
ThinkingMarkdown(
|
|
54
|
+
normalize_thinking_content(content),
|
|
55
|
+
code_theme=code_theme,
|
|
56
|
+
style=style,
|
|
57
|
+
),
|
|
58
|
+
(0, const.MARKDOWN_RIGHT_MARGIN, 0, 0),
|
|
53
59
|
),
|
|
54
|
-
level=2,
|
|
55
60
|
)
|
|
61
|
+
return grid
|