klaude-code 1.2.1__py3-none-any.whl → 1.2.3__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/cli/main.py +9 -4
- klaude_code/cli/runtime.py +42 -43
- klaude_code/command/__init__.py +7 -5
- klaude_code/command/clear_cmd.py +6 -29
- klaude_code/command/command_abc.py +44 -8
- klaude_code/command/diff_cmd.py +33 -27
- klaude_code/command/export_cmd.py +18 -26
- klaude_code/command/help_cmd.py +10 -8
- klaude_code/command/model_cmd.py +11 -40
- klaude_code/command/{prompt-update-dev-doc.md → prompt-dev-docs-update.md} +3 -2
- klaude_code/command/{prompt-dev-doc.md → prompt-dev-docs.md} +3 -2
- klaude_code/command/prompt-init.md +2 -5
- klaude_code/command/prompt_command.py +6 -6
- klaude_code/command/refresh_cmd.py +4 -5
- klaude_code/command/registry.py +16 -19
- klaude_code/command/terminal_setup_cmd.py +12 -11
- klaude_code/config/__init__.py +4 -0
- klaude_code/config/config.py +25 -26
- klaude_code/config/list_model.py +8 -3
- klaude_code/config/select_model.py +1 -1
- klaude_code/const/__init__.py +1 -1
- klaude_code/core/__init__.py +0 -3
- klaude_code/core/agent.py +25 -50
- klaude_code/core/executor.py +268 -101
- klaude_code/core/prompt.py +12 -12
- klaude_code/core/{prompt → prompts}/prompt-gemini.md +1 -1
- klaude_code/core/reminders.py +76 -95
- klaude_code/core/task.py +21 -14
- klaude_code/core/tool/__init__.py +45 -11
- klaude_code/core/tool/file/apply_patch.py +5 -1
- klaude_code/core/tool/file/apply_patch_tool.py +11 -13
- klaude_code/core/tool/file/edit_tool.py +27 -23
- klaude_code/core/tool/file/multi_edit_tool.py +15 -17
- klaude_code/core/tool/file/read_tool.py +41 -36
- klaude_code/core/tool/file/write_tool.py +13 -15
- klaude_code/core/tool/memory/memory_tool.py +85 -68
- klaude_code/core/tool/memory/skill_tool.py +10 -12
- klaude_code/core/tool/shell/bash_tool.py +24 -22
- klaude_code/core/tool/shell/command_safety.py +12 -1
- klaude_code/core/tool/sub_agent_tool.py +11 -12
- klaude_code/core/tool/todo/todo_write_tool.py +21 -28
- klaude_code/core/tool/todo/update_plan_tool.py +14 -24
- klaude_code/core/tool/tool_abc.py +3 -4
- klaude_code/core/tool/tool_context.py +7 -7
- klaude_code/core/tool/tool_registry.py +30 -47
- klaude_code/core/tool/tool_runner.py +35 -43
- klaude_code/core/tool/truncation.py +14 -20
- klaude_code/core/tool/web/mermaid_tool.py +12 -14
- klaude_code/core/tool/web/web_fetch_tool.py +15 -17
- klaude_code/core/turn.py +19 -7
- klaude_code/llm/__init__.py +3 -4
- klaude_code/llm/anthropic/client.py +30 -46
- klaude_code/llm/anthropic/input.py +4 -11
- klaude_code/llm/client.py +29 -8
- klaude_code/llm/input_common.py +66 -36
- klaude_code/llm/openai_compatible/client.py +42 -84
- klaude_code/llm/openai_compatible/input.py +11 -16
- klaude_code/llm/openai_compatible/tool_call_accumulator.py +2 -2
- klaude_code/llm/openrouter/client.py +40 -289
- klaude_code/llm/openrouter/input.py +13 -35
- klaude_code/llm/openrouter/reasoning_handler.py +209 -0
- klaude_code/llm/registry.py +5 -75
- klaude_code/llm/responses/client.py +34 -55
- klaude_code/llm/responses/input.py +24 -26
- klaude_code/llm/usage.py +109 -0
- klaude_code/protocol/__init__.py +4 -0
- klaude_code/protocol/events.py +3 -2
- klaude_code/protocol/{llm_parameter.py → llm_param.py} +12 -32
- klaude_code/protocol/model.py +49 -4
- klaude_code/protocol/op.py +18 -16
- klaude_code/protocol/op_handler.py +28 -0
- klaude_code/{core → protocol}/sub_agent.py +7 -0
- klaude_code/session/export.py +150 -70
- klaude_code/session/session.py +28 -14
- klaude_code/session/templates/export_session.html +180 -42
- klaude_code/trace/__init__.py +2 -2
- klaude_code/trace/log.py +11 -5
- klaude_code/ui/__init__.py +91 -8
- klaude_code/ui/core/__init__.py +1 -0
- klaude_code/ui/core/display.py +103 -0
- klaude_code/ui/core/input.py +71 -0
- klaude_code/ui/modes/__init__.py +1 -0
- klaude_code/ui/modes/debug/__init__.py +1 -0
- klaude_code/ui/{base/debug_event_display.py → modes/debug/display.py} +9 -5
- klaude_code/ui/modes/exec/__init__.py +1 -0
- klaude_code/ui/{base/exec_display.py → modes/exec/display.py} +28 -2
- klaude_code/ui/{repl → modes/repl}/__init__.py +5 -6
- klaude_code/ui/modes/repl/clipboard.py +152 -0
- klaude_code/ui/modes/repl/completers.py +429 -0
- klaude_code/ui/modes/repl/display.py +60 -0
- klaude_code/ui/modes/repl/event_handler.py +375 -0
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +198 -0
- klaude_code/ui/modes/repl/key_bindings.py +170 -0
- klaude_code/ui/{repl → modes/repl}/renderer.py +109 -132
- klaude_code/ui/renderers/assistant.py +21 -0
- klaude_code/ui/renderers/common.py +0 -16
- klaude_code/ui/renderers/developer.py +18 -18
- klaude_code/ui/renderers/diffs.py +36 -14
- klaude_code/ui/renderers/errors.py +1 -1
- klaude_code/ui/renderers/metadata.py +50 -27
- klaude_code/ui/renderers/sub_agent.py +43 -9
- klaude_code/ui/renderers/thinking.py +33 -1
- klaude_code/ui/renderers/tools.py +212 -20
- klaude_code/ui/renderers/user_input.py +19 -23
- klaude_code/ui/rich/__init__.py +1 -0
- klaude_code/ui/{rich_ext → rich}/searchable_text.py +3 -1
- klaude_code/ui/{renderers → rich}/status.py +29 -18
- klaude_code/ui/{base → rich}/theme.py +8 -2
- klaude_code/ui/terminal/__init__.py +1 -0
- klaude_code/ui/{base/terminal_color.py → terminal/color.py} +4 -1
- klaude_code/ui/{base/terminal_control.py → terminal/control.py} +1 -0
- klaude_code/ui/{base/terminal_notifier.py → terminal/notifier.py} +5 -2
- klaude_code/ui/utils/__init__.py +1 -0
- klaude_code/ui/{base/utils.py → utils/common.py} +35 -3
- {klaude_code-1.2.1.dist-info → klaude_code-1.2.3.dist-info}/METADATA +1 -1
- klaude_code-1.2.3.dist-info/RECORD +161 -0
- klaude_code/core/clipboard_manifest.py +0 -124
- klaude_code/llm/openrouter/tool_call_accumulator.py +0 -80
- klaude_code/ui/base/__init__.py +0 -1
- klaude_code/ui/base/display_abc.py +0 -36
- klaude_code/ui/base/input_abc.py +0 -20
- klaude_code/ui/repl/display.py +0 -36
- klaude_code/ui/repl/event_handler.py +0 -247
- klaude_code/ui/repl/input.py +0 -773
- klaude_code/ui/rich_ext/__init__.py +0 -1
- klaude_code-1.2.1.dist-info/RECORD +0 -151
- /klaude_code/core/{prompt → prompts}/prompt-claude-code.md +0 -0
- /klaude_code/core/{prompt → prompts}/prompt-codex.md +0 -0
- /klaude_code/core/{prompt → prompts}/prompt-subagent-explore.md +0 -0
- /klaude_code/core/{prompt → prompts}/prompt-subagent-oracle.md +0 -0
- /klaude_code/core/{prompt → prompts}/prompt-subagent-webfetch.md +0 -0
- /klaude_code/core/{prompt → prompts}/prompt-subagent.md +0 -0
- /klaude_code/ui/{base → core}/stage_manager.py +0 -0
- /klaude_code/ui/{rich_ext → rich}/live.py +0 -0
- /klaude_code/ui/{rich_ext → rich}/markdown.py +0 -0
- /klaude_code/ui/{rich_ext → rich}/quote.py +0 -0
- /klaude_code/ui/{base → terminal}/progress_bar.py +0 -0
- /klaude_code/ui/{base → utils}/debouncer.py +0 -0
- {klaude_code-1.2.1.dist-info → klaude_code-1.2.3.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.1.dist-info → klaude_code-1.2.3.dist-info}/entry_points.txt +0 -0
|
@@ -4,9 +4,9 @@ from rich.padding import Padding
|
|
|
4
4
|
from rich.panel import Panel
|
|
5
5
|
from rich.text import Text
|
|
6
6
|
|
|
7
|
-
from klaude_code
|
|
8
|
-
from klaude_code.ui.base.theme import ThemeKey
|
|
7
|
+
from klaude_code import const
|
|
9
8
|
from klaude_code.ui.renderers.common import create_grid
|
|
9
|
+
from klaude_code.ui.rich.theme import ThemeKey
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def _make_diff_prefix(line: str, new_ln: int | None, width: int) -> tuple[str, int | None]:
|
|
@@ -44,6 +44,8 @@ def render_diff(diff_text: str, show_file_name: bool = False) -> RenderableType:
|
|
|
44
44
|
has_rendered_file_header = False
|
|
45
45
|
# Track whether we have rendered actual diff content for the current file
|
|
46
46
|
has_rendered_diff_content = False
|
|
47
|
+
# Track the "from" file name from --- line (used for deleted files)
|
|
48
|
+
from_file_name: str | None = None
|
|
47
49
|
|
|
48
50
|
for i, line in enumerate(lines):
|
|
49
51
|
# Check for untracked files section header
|
|
@@ -61,15 +63,29 @@ def render_diff(diff_text: str, show_file_name: bool = False) -> RenderableType:
|
|
|
61
63
|
in_untracked_section = False
|
|
62
64
|
elif line.strip(): # Non-empty line in untracked section
|
|
63
65
|
file_text = Text(line.strip(), style=ThemeKey.TOOL_PARAM_BOLD)
|
|
64
|
-
grid.add_row(
|
|
66
|
+
grid.add_row(
|
|
67
|
+
Text(f"{'+':>{const.DIFF_PREFIX_WIDTH}}", style=ThemeKey.TOOL_PARAM_BOLD),
|
|
68
|
+
file_text,
|
|
69
|
+
)
|
|
65
70
|
continue
|
|
66
71
|
|
|
72
|
+
# Capture "from" file name from --- line (needed for deleted files)
|
|
73
|
+
if line.startswith("--- "):
|
|
74
|
+
raw = line[4:].strip()
|
|
75
|
+
if raw != "/dev/null":
|
|
76
|
+
if raw.startswith(("a/", "b/")):
|
|
77
|
+
from_file_name = raw[2:]
|
|
78
|
+
else:
|
|
79
|
+
from_file_name = raw
|
|
80
|
+
continue
|
|
81
|
+
|
|
67
82
|
# Parse file name from diff headers
|
|
68
83
|
if show_file_name and line.startswith("+++ "):
|
|
69
84
|
# Extract file name from +++ header with proper handling of /dev/null
|
|
70
85
|
raw = line[4:].strip()
|
|
71
86
|
if raw == "/dev/null":
|
|
72
|
-
|
|
87
|
+
# File was deleted, use the "from" file name
|
|
88
|
+
file_name = from_file_name or raw
|
|
73
89
|
elif raw.startswith(("a/", "b/")):
|
|
74
90
|
file_name = raw[2:]
|
|
75
91
|
else:
|
|
@@ -115,7 +131,10 @@ def render_diff(diff_text: str, show_file_name: bool = False) -> RenderableType:
|
|
|
115
131
|
else:
|
|
116
132
|
file_mark = "±"
|
|
117
133
|
|
|
118
|
-
grid.add_row(
|
|
134
|
+
grid.add_row(
|
|
135
|
+
Text(f"{file_mark:>{const.DIFF_PREFIX_WIDTH}} ", style=ThemeKey.DIFF_FILE_NAME),
|
|
136
|
+
file_line,
|
|
137
|
+
)
|
|
119
138
|
has_rendered_file_header = True
|
|
120
139
|
has_rendered_diff_content = False
|
|
121
140
|
continue
|
|
@@ -134,11 +153,11 @@ def render_diff(diff_text: str, show_file_name: bool = False) -> RenderableType:
|
|
|
134
153
|
except Exception:
|
|
135
154
|
new_ln = None
|
|
136
155
|
if has_rendered_diff_content:
|
|
137
|
-
grid.add_row(Text(f"{'⋮':>{DIFF_PREFIX_WIDTH}}", style=ThemeKey.TOOL_RESULT), "")
|
|
156
|
+
grid.add_row(Text(f"{'⋮':>{const.DIFF_PREFIX_WIDTH}}", style=ThemeKey.TOOL_RESULT), "")
|
|
138
157
|
continue
|
|
139
158
|
|
|
140
|
-
# Skip
|
|
141
|
-
if line.startswith("
|
|
159
|
+
# Skip +++ lines (already handled above)
|
|
160
|
+
if line.startswith("+++ "):
|
|
142
161
|
continue
|
|
143
162
|
|
|
144
163
|
# Only handle unified diff hunk lines; ignore other metadata like
|
|
@@ -147,7 +166,7 @@ def render_diff(diff_text: str, show_file_name: bool = False) -> RenderableType:
|
|
|
147
166
|
continue
|
|
148
167
|
|
|
149
168
|
# Compute line number prefix and style diff content
|
|
150
|
-
prefix, new_ln = _make_diff_prefix(line, new_ln, DIFF_PREFIX_WIDTH)
|
|
169
|
+
prefix, new_ln = _make_diff_prefix(line, new_ln, const.DIFF_PREFIX_WIDTH)
|
|
151
170
|
|
|
152
171
|
if line.startswith("-"):
|
|
153
172
|
text = Text(line[1:])
|
|
@@ -172,13 +191,16 @@ def render_diff_panel(
|
|
|
172
191
|
) -> RenderableType:
|
|
173
192
|
lines = diff_text.splitlines()
|
|
174
193
|
truncated_notice: Text | None = None
|
|
175
|
-
if len(lines) > MAX_DIFF_LINES:
|
|
176
|
-
truncated_lines = len(lines) - MAX_DIFF_LINES
|
|
177
|
-
diff_text = "\n".join(lines[:MAX_DIFF_LINES])
|
|
178
|
-
truncated_notice = Text(f"
|
|
194
|
+
if len(lines) > const.MAX_DIFF_LINES:
|
|
195
|
+
truncated_lines = len(lines) - const.MAX_DIFF_LINES
|
|
196
|
+
diff_text = "\n".join(lines[: const.MAX_DIFF_LINES])
|
|
197
|
+
truncated_notice = Text(f"… truncated {truncated_lines} lines", style=ThemeKey.TOOL_MARK)
|
|
179
198
|
|
|
180
199
|
diff_body = render_diff(diff_text, show_file_name=show_file_name)
|
|
181
|
-
renderables: list[RenderableType] = [
|
|
200
|
+
renderables: list[RenderableType] = [
|
|
201
|
+
Text(f" {heading} ", style="bold reverse"),
|
|
202
|
+
diff_body,
|
|
203
|
+
]
|
|
182
204
|
if truncated_notice is not None:
|
|
183
205
|
renderables.extend([Text(""), truncated_notice])
|
|
184
206
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from rich.console import RenderableType
|
|
2
2
|
from rich.text import Text
|
|
3
3
|
|
|
4
|
-
from klaude_code.ui.base.theme import ThemeKey
|
|
5
4
|
from klaude_code.ui.renderers.common import create_grid
|
|
5
|
+
from klaude_code.ui.rich.theme import ThemeKey
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def render_error(error_msg: Text, indent: int = 2) -> RenderableType:
|
|
@@ -8,8 +8,9 @@ from rich.panel import Panel
|
|
|
8
8
|
from rich.text import Text
|
|
9
9
|
|
|
10
10
|
from klaude_code.protocol import events
|
|
11
|
-
from klaude_code.
|
|
12
|
-
from klaude_code.ui.
|
|
11
|
+
from klaude_code.trace import is_debug_enabled
|
|
12
|
+
from klaude_code.ui.rich.theme import ThemeKey
|
|
13
|
+
from klaude_code.ui.utils.common import format_number
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def _get_version() -> str:
|
|
@@ -40,30 +41,32 @@ def render_response_metadata(e: events.ResponseMetadataEvent) -> RenderableType:
|
|
|
40
41
|
|
|
41
42
|
if metadata.usage is not None:
|
|
42
43
|
# Input
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
input_parts: list[tuple[str, str]] = [
|
|
45
|
+
("input:", ThemeKey.METADATA_DIM),
|
|
46
|
+
(format_number(metadata.usage.input_tokens), ThemeKey.METADATA_DIM),
|
|
47
|
+
]
|
|
48
|
+
if metadata.usage.input_cost is not None:
|
|
49
|
+
input_parts.append((f"(${metadata.usage.input_cost:.4f})", ThemeKey.METADATA_DIM))
|
|
50
|
+
parts.append(Text.assemble(*input_parts))
|
|
49
51
|
|
|
50
52
|
# Cached
|
|
51
53
|
if metadata.usage.cached_tokens > 0:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
)
|
|
58
|
-
)
|
|
54
|
+
cached_parts: list[tuple[str, str]] = [
|
|
55
|
+
("cached:", ThemeKey.METADATA_DIM),
|
|
56
|
+
(format_number(metadata.usage.cached_tokens), ThemeKey.METADATA_DIM),
|
|
57
|
+
]
|
|
58
|
+
if metadata.usage.cache_read_cost is not None:
|
|
59
|
+
cached_parts.append((f"(${metadata.usage.cache_read_cost:.4f})", ThemeKey.METADATA_DIM))
|
|
60
|
+
parts.append(Text.assemble(*cached_parts))
|
|
59
61
|
|
|
60
62
|
# Output
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
output_parts: list[tuple[str, str]] = [
|
|
64
|
+
("output:", ThemeKey.METADATA_DIM),
|
|
65
|
+
(format_number(metadata.usage.output_tokens), ThemeKey.METADATA_DIM),
|
|
66
|
+
]
|
|
67
|
+
if metadata.usage.output_cost is not None:
|
|
68
|
+
output_parts.append((f"(${metadata.usage.output_cost:.4f})", ThemeKey.METADATA_DIM))
|
|
69
|
+
parts.append(Text.assemble(*output_parts))
|
|
67
70
|
|
|
68
71
|
# Reasoning
|
|
69
72
|
if metadata.usage.reasoning_tokens > 0:
|
|
@@ -71,7 +74,10 @@ def render_response_metadata(e: events.ResponseMetadataEvent) -> RenderableType:
|
|
|
71
74
|
Text.assemble(
|
|
72
75
|
("thinking", ThemeKey.METADATA_DIM),
|
|
73
76
|
(":", ThemeKey.METADATA_DIM),
|
|
74
|
-
(
|
|
77
|
+
(
|
|
78
|
+
format_number(metadata.usage.reasoning_tokens),
|
|
79
|
+
ThemeKey.METADATA_DIM,
|
|
80
|
+
),
|
|
75
81
|
)
|
|
76
82
|
)
|
|
77
83
|
|
|
@@ -81,7 +87,10 @@ def render_response_metadata(e: events.ResponseMetadataEvent) -> RenderableType:
|
|
|
81
87
|
Text.assemble(
|
|
82
88
|
("context", ThemeKey.METADATA_DIM),
|
|
83
89
|
(":", ThemeKey.METADATA_DIM),
|
|
84
|
-
(
|
|
90
|
+
(
|
|
91
|
+
f"{metadata.usage.context_usage_percent:.1f}%",
|
|
92
|
+
ThemeKey.METADATA_DIM,
|
|
93
|
+
),
|
|
85
94
|
)
|
|
86
95
|
)
|
|
87
96
|
|
|
@@ -95,16 +104,26 @@ def render_response_metadata(e: events.ResponseMetadataEvent) -> RenderableType:
|
|
|
95
104
|
)
|
|
96
105
|
)
|
|
97
106
|
|
|
98
|
-
#
|
|
107
|
+
# Duration
|
|
99
108
|
if metadata.task_duration_s is not None:
|
|
100
109
|
parts.append(
|
|
101
110
|
Text.assemble(
|
|
102
|
-
("
|
|
111
|
+
("time", ThemeKey.METADATA_DIM),
|
|
103
112
|
(":", ThemeKey.METADATA_DIM),
|
|
104
113
|
(f"{metadata.task_duration_s:.1f}s", ThemeKey.METADATA_DIM),
|
|
105
114
|
)
|
|
106
115
|
)
|
|
107
116
|
|
|
117
|
+
# Cost (USD)
|
|
118
|
+
if metadata.usage is not None and metadata.usage.total_cost is not None:
|
|
119
|
+
parts.append(
|
|
120
|
+
Text.assemble(
|
|
121
|
+
("cost", ThemeKey.METADATA_DIM),
|
|
122
|
+
(":", ThemeKey.METADATA_DIM),
|
|
123
|
+
(f"${metadata.usage.total_cost:.4f}", ThemeKey.METADATA_DIM),
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
108
127
|
if parts:
|
|
109
128
|
line2 = Text("/", style=ThemeKey.METADATA_DIM).join(parts)
|
|
110
129
|
renderables.append(Padding(line2, (0, 0, 0, 2)))
|
|
@@ -117,9 +136,12 @@ def render_welcome(e: events.WelcomeEvent, *, box_style: Box | None = None) -> R
|
|
|
117
136
|
if box_style is None:
|
|
118
137
|
box_style = box.ROUNDED
|
|
119
138
|
|
|
139
|
+
debug_mode = is_debug_enabled()
|
|
140
|
+
|
|
120
141
|
# First line: Klaude Code version
|
|
142
|
+
klaude_code_style = ThemeKey.WELCOME_DEBUG_TITLE if debug_mode else ThemeKey.WELCOME_HIGHLIGHT_BOLD
|
|
121
143
|
panel_content = Text.assemble(
|
|
122
|
-
("Klaude Code",
|
|
144
|
+
("Klaude Code", klaude_code_style),
|
|
123
145
|
(f" v{_get_version()}\n", ThemeKey.WELCOME_INFO),
|
|
124
146
|
(str(e.llm_config.model), ThemeKey.WELCOME_HIGHLIGHT),
|
|
125
147
|
(" @ ", ThemeKey.WELCOME_INFO),
|
|
@@ -161,7 +183,8 @@ def render_welcome(e: events.WelcomeEvent, *, box_style: Box | None = None) -> R
|
|
|
161
183
|
)
|
|
162
184
|
)
|
|
163
185
|
|
|
186
|
+
border_style = ThemeKey.WELCOME_DEBUG_BORDER if debug_mode else ThemeKey.LINES
|
|
164
187
|
return Group(
|
|
165
|
-
Panel.fit(panel_content, border_style=
|
|
188
|
+
Panel.fit(panel_content, border_style=border_style, box=box_style),
|
|
166
189
|
"", # empty line
|
|
167
190
|
)
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
1
3
|
from rich.console import Group, RenderableType
|
|
2
4
|
from rich.panel import Panel
|
|
3
5
|
from rich.style import Style
|
|
4
6
|
from rich.text import Text
|
|
5
7
|
|
|
6
|
-
from klaude_code
|
|
7
|
-
from klaude_code.protocol import model
|
|
8
|
-
from klaude_code.
|
|
9
|
-
from klaude_code.ui.
|
|
8
|
+
from klaude_code import const
|
|
9
|
+
from klaude_code.protocol import events, model
|
|
10
|
+
from klaude_code.protocol.sub_agent import get_sub_agent_profile_by_tool
|
|
11
|
+
from klaude_code.ui.rich.markdown import NoInsetMarkdown
|
|
12
|
+
from klaude_code.ui.rich.theme import ThemeKey
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
def render_sub_agent_call(e: model.SubAgentState, style: Style | None = None) -> RenderableType:
|
|
@@ -24,14 +27,45 @@ def render_sub_agent_call(e: model.SubAgentState, style: Style | None = None) ->
|
|
|
24
27
|
def render_sub_agent_result(result: str, *, code_theme: str, style: Style | None = None) -> RenderableType:
|
|
25
28
|
stripped_result = result.strip()
|
|
26
29
|
lines = stripped_result.splitlines()
|
|
27
|
-
if len(lines) > SUB_AGENT_RESULT_MAX_LINES:
|
|
28
|
-
hidden_count = len(lines) - SUB_AGENT_RESULT_MAX_LINES
|
|
29
|
-
truncated_text = "\n".join(lines[-SUB_AGENT_RESULT_MAX_LINES:])
|
|
30
|
+
if len(lines) > const.SUB_AGENT_RESULT_MAX_LINES:
|
|
31
|
+
hidden_count = len(lines) - const.SUB_AGENT_RESULT_MAX_LINES
|
|
32
|
+
truncated_text = "\n".join(lines[-const.SUB_AGENT_RESULT_MAX_LINES :])
|
|
30
33
|
return Panel.fit(
|
|
31
34
|
Group(
|
|
32
|
-
Text(
|
|
35
|
+
Text(
|
|
36
|
+
f"… more {hidden_count} lines — use /export to view full output",
|
|
37
|
+
style=ThemeKey.TOOL_RESULT,
|
|
38
|
+
),
|
|
33
39
|
NoInsetMarkdown(truncated_text, code_theme=code_theme, style=style or ""),
|
|
34
40
|
),
|
|
35
41
|
border_style=ThemeKey.LINES,
|
|
36
42
|
)
|
|
37
|
-
return Panel.fit(
|
|
43
|
+
return Panel.fit(
|
|
44
|
+
NoInsetMarkdown(stripped_result, code_theme=code_theme),
|
|
45
|
+
border_style=ThemeKey.LINES,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def build_sub_agent_state_from_tool_call(e: events.ToolCallEvent) -> model.SubAgentState | None:
|
|
50
|
+
"""Build SubAgentState from a tool call event for replay rendering."""
|
|
51
|
+
profile = get_sub_agent_profile_by_tool(e.tool_name)
|
|
52
|
+
if profile is None:
|
|
53
|
+
return None
|
|
54
|
+
description = profile.name
|
|
55
|
+
prompt = ""
|
|
56
|
+
if e.arguments:
|
|
57
|
+
try:
|
|
58
|
+
payload: dict[str, object] = json.loads(e.arguments)
|
|
59
|
+
except json.JSONDecodeError:
|
|
60
|
+
payload = {}
|
|
61
|
+
desc_value = payload.get("description")
|
|
62
|
+
if isinstance(desc_value, str) and desc_value.strip():
|
|
63
|
+
description = desc_value.strip()
|
|
64
|
+
prompt_value = payload.get("prompt") or payload.get("task")
|
|
65
|
+
if isinstance(prompt_value, str):
|
|
66
|
+
prompt = prompt_value.strip()
|
|
67
|
+
return model.SubAgentState(
|
|
68
|
+
sub_agent_type=profile.name,
|
|
69
|
+
sub_agent_desc=description,
|
|
70
|
+
sub_agent_prompt=prompt,
|
|
71
|
+
)
|
|
@@ -1,7 +1,39 @@
|
|
|
1
|
+
from rich.console import RenderableType
|
|
2
|
+
from rich.padding import Padding
|
|
1
3
|
from rich.text import Text
|
|
2
4
|
|
|
3
|
-
from klaude_code.ui.
|
|
5
|
+
from klaude_code.ui.rich.markdown import NoInsetMarkdown
|
|
6
|
+
from klaude_code.ui.rich.theme import ThemeKey
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
def thinking_prefix() -> Text:
|
|
7
10
|
return Text.from_markup("[not italic]⸫[/not italic] Thinking …", style=ThemeKey.THINKING)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _normalize_thinking_content(content: str) -> str:
|
|
14
|
+
"""Normalize thinking content for display."""
|
|
15
|
+
return (
|
|
16
|
+
content.rstrip()
|
|
17
|
+
.replace("**\n\n", "** \n")
|
|
18
|
+
.replace("\\n\\n\n\n", "") # Weird case of Gemini 3
|
|
19
|
+
.replace("****", "**\n\n**") # remove extra newlines after bold titles
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def render_thinking(content: str, *, code_theme: str, style: str) -> RenderableType | None:
|
|
24
|
+
"""Render thinking content as indented markdown.
|
|
25
|
+
|
|
26
|
+
Returns None if content is empty.
|
|
27
|
+
Note: Caller should push thinking_markdown_theme before printing.
|
|
28
|
+
"""
|
|
29
|
+
if len(content.strip()) == 0:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
return Padding.indent(
|
|
33
|
+
NoInsetMarkdown(
|
|
34
|
+
_normalize_thinking_content(content),
|
|
35
|
+
code_theme=code_theme,
|
|
36
|
+
style=style,
|
|
37
|
+
),
|
|
38
|
+
level=2,
|
|
39
|
+
)
|