klaude-code 2.0.2__py3-none-any.whl → 2.1.1__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.
Files changed (157) hide show
  1. klaude_code/app/__init__.py +12 -0
  2. klaude_code/app/runtime.py +215 -0
  3. klaude_code/cli/auth_cmd.py +2 -2
  4. klaude_code/cli/config_cmd.py +2 -2
  5. klaude_code/cli/cost_cmd.py +1 -1
  6. klaude_code/cli/debug.py +12 -36
  7. klaude_code/cli/list_model.py +3 -3
  8. klaude_code/cli/main.py +17 -60
  9. klaude_code/cli/self_update.py +2 -187
  10. klaude_code/cli/session_cmd.py +2 -2
  11. klaude_code/config/config.py +1 -1
  12. klaude_code/config/select_model.py +1 -1
  13. klaude_code/const.py +9 -1
  14. klaude_code/core/agent.py +9 -62
  15. klaude_code/core/agent_profile.py +291 -0
  16. klaude_code/core/executor.py +335 -230
  17. klaude_code/core/manager/llm_clients_builder.py +1 -1
  18. klaude_code/core/manager/sub_agent_manager.py +16 -29
  19. klaude_code/core/reminders.py +84 -103
  20. klaude_code/core/task.py +12 -20
  21. klaude_code/core/tool/__init__.py +5 -19
  22. klaude_code/core/tool/context.py +84 -0
  23. klaude_code/core/tool/file/apply_patch_tool.py +18 -21
  24. klaude_code/core/tool/file/edit_tool.py +39 -42
  25. klaude_code/core/tool/file/read_tool.py +14 -9
  26. klaude_code/core/tool/file/write_tool.py +12 -13
  27. klaude_code/core/tool/report_back_tool.py +4 -1
  28. klaude_code/core/tool/shell/bash_tool.py +6 -11
  29. klaude_code/core/tool/sub_agent_tool.py +8 -7
  30. klaude_code/core/tool/todo/todo_write_tool.py +3 -9
  31. klaude_code/core/tool/todo/update_plan_tool.py +3 -5
  32. klaude_code/core/tool/tool_abc.py +2 -1
  33. klaude_code/core/tool/tool_registry.py +2 -33
  34. klaude_code/core/tool/tool_runner.py +13 -10
  35. klaude_code/core/tool/web/mermaid_tool.py +3 -1
  36. klaude_code/core/tool/web/web_fetch_tool.py +5 -3
  37. klaude_code/core/tool/web/web_search_tool.py +5 -3
  38. klaude_code/core/turn.py +87 -30
  39. klaude_code/llm/anthropic/client.py +1 -1
  40. klaude_code/llm/bedrock/client.py +1 -1
  41. klaude_code/llm/claude/client.py +1 -1
  42. klaude_code/llm/codex/client.py +1 -1
  43. klaude_code/llm/google/client.py +1 -1
  44. klaude_code/llm/openai_compatible/client.py +1 -1
  45. klaude_code/llm/openai_compatible/tool_call_accumulator.py +1 -1
  46. klaude_code/llm/openrouter/client.py +1 -1
  47. klaude_code/llm/openrouter/reasoning.py +1 -1
  48. klaude_code/llm/responses/client.py +1 -1
  49. klaude_code/protocol/commands.py +1 -0
  50. klaude_code/protocol/events/__init__.py +57 -0
  51. klaude_code/protocol/events/base.py +18 -0
  52. klaude_code/protocol/events/chat.py +20 -0
  53. klaude_code/protocol/events/lifecycle.py +22 -0
  54. klaude_code/protocol/events/metadata.py +15 -0
  55. klaude_code/protocol/events/streaming.py +43 -0
  56. klaude_code/protocol/events/system.py +53 -0
  57. klaude_code/protocol/events/tools.py +27 -0
  58. klaude_code/protocol/op.py +5 -0
  59. klaude_code/protocol/tools.py +0 -1
  60. klaude_code/session/session.py +6 -7
  61. klaude_code/skill/assets/create-plan/SKILL.md +76 -0
  62. klaude_code/skill/loader.py +32 -88
  63. klaude_code/skill/manager.py +38 -0
  64. klaude_code/skill/system_skills.py +1 -1
  65. klaude_code/tui/__init__.py +8 -0
  66. klaude_code/{command → tui/command}/__init__.py +3 -0
  67. klaude_code/{command → tui/command}/clear_cmd.py +2 -1
  68. klaude_code/tui/command/copy_cmd.py +53 -0
  69. klaude_code/{command → tui/command}/debug_cmd.py +3 -2
  70. klaude_code/{command → tui/command}/export_cmd.py +2 -1
  71. klaude_code/{command → tui/command}/export_online_cmd.py +2 -1
  72. klaude_code/{command → tui/command}/fork_session_cmd.py +4 -3
  73. klaude_code/{command → tui/command}/help_cmd.py +2 -1
  74. klaude_code/{command → tui/command}/model_cmd.py +4 -3
  75. klaude_code/{command → tui/command}/model_select.py +2 -2
  76. klaude_code/{command → tui/command}/prompt_command.py +4 -3
  77. klaude_code/{command → tui/command}/refresh_cmd.py +3 -1
  78. klaude_code/{command → tui/command}/registry.py +6 -5
  79. klaude_code/{command → tui/command}/release_notes_cmd.py +2 -1
  80. klaude_code/{command → tui/command}/resume_cmd.py +4 -3
  81. klaude_code/{command → tui/command}/status_cmd.py +2 -1
  82. klaude_code/{command → tui/command}/terminal_setup_cmd.py +2 -1
  83. klaude_code/{command → tui/command}/thinking_cmd.py +3 -2
  84. klaude_code/tui/commands.py +164 -0
  85. klaude_code/{ui/renderers → tui/components}/assistant.py +3 -3
  86. klaude_code/{ui/renderers → tui/components}/bash_syntax.py +2 -2
  87. klaude_code/{ui/renderers → tui/components}/common.py +1 -1
  88. klaude_code/{ui/renderers → tui/components}/developer.py +4 -4
  89. klaude_code/{ui/renderers → tui/components}/diffs.py +2 -2
  90. klaude_code/{ui/renderers → tui/components}/errors.py +2 -2
  91. klaude_code/{ui/renderers → tui/components}/metadata.py +7 -7
  92. klaude_code/{ui → tui/components}/rich/markdown.py +9 -23
  93. klaude_code/{ui → tui/components}/rich/status.py +2 -2
  94. klaude_code/{ui → tui/components}/rich/theme.py +3 -1
  95. klaude_code/{ui/renderers → tui/components}/sub_agent.py +23 -43
  96. klaude_code/{ui/renderers → tui/components}/thinking.py +3 -3
  97. klaude_code/{ui/renderers → tui/components}/tools.py +13 -17
  98. klaude_code/{ui/renderers → tui/components}/user_input.py +3 -20
  99. klaude_code/tui/display.py +85 -0
  100. klaude_code/{ui/modes/repl → tui/input}/__init__.py +1 -1
  101. klaude_code/{ui/modes/repl → tui/input}/completers.py +1 -1
  102. klaude_code/{ui/modes/repl/input_prompt_toolkit.py → tui/input/prompt_toolkit.py} +6 -6
  103. klaude_code/tui/machine.py +608 -0
  104. klaude_code/tui/renderer.py +707 -0
  105. klaude_code/tui/runner.py +321 -0
  106. klaude_code/tui/terminal/__init__.py +56 -0
  107. klaude_code/{ui → tui}/terminal/color.py +1 -1
  108. klaude_code/{ui → tui}/terminal/control.py +1 -1
  109. klaude_code/{ui → tui}/terminal/notifier.py +1 -1
  110. klaude_code/ui/__init__.py +6 -50
  111. klaude_code/ui/core/display.py +3 -3
  112. klaude_code/ui/core/input.py +2 -1
  113. klaude_code/ui/{modes/debug/display.py → debug_mode.py} +1 -1
  114. klaude_code/ui/{modes/exec/display.py → exec_mode.py} +0 -2
  115. klaude_code/ui/terminal/__init__.py +6 -54
  116. klaude_code/ui/terminal/title.py +31 -0
  117. klaude_code/update.py +163 -0
  118. {klaude_code-2.0.2.dist-info → klaude_code-2.1.1.dist-info}/METADATA +1 -1
  119. klaude_code-2.1.1.dist-info/RECORD +233 -0
  120. klaude_code/cli/runtime.py +0 -518
  121. klaude_code/core/prompt.py +0 -108
  122. klaude_code/core/tool/skill/skill_tool.md +0 -24
  123. klaude_code/core/tool/skill/skill_tool.py +0 -87
  124. klaude_code/core/tool/tool_context.py +0 -148
  125. klaude_code/protocol/events.py +0 -195
  126. klaude_code/skill/assets/dev-docs/SKILL.md +0 -108
  127. klaude_code/trace/__init__.py +0 -21
  128. klaude_code/ui/core/stage_manager.py +0 -48
  129. klaude_code/ui/modes/__init__.py +0 -1
  130. klaude_code/ui/modes/debug/__init__.py +0 -1
  131. klaude_code/ui/modes/exec/__init__.py +0 -1
  132. klaude_code/ui/modes/repl/display.py +0 -61
  133. klaude_code/ui/modes/repl/event_handler.py +0 -629
  134. klaude_code/ui/modes/repl/renderer.py +0 -464
  135. klaude_code/ui/renderers/__init__.py +0 -0
  136. klaude_code/ui/utils/__init__.py +0 -1
  137. klaude_code-2.0.2.dist-info/RECORD +0 -227
  138. /klaude_code/{trace/log.py → log.py} +0 -0
  139. /klaude_code/{command → tui/command}/command_abc.py +0 -0
  140. /klaude_code/{command → tui/command}/prompt-commit.md +0 -0
  141. /klaude_code/{command → tui/command}/prompt-init.md +0 -0
  142. /klaude_code/{core/tool/skill → tui/components}/__init__.py +0 -0
  143. /klaude_code/{ui/renderers → tui/components}/mermaid_viewer.py +0 -0
  144. /klaude_code/{ui → tui/components}/rich/__init__.py +0 -0
  145. /klaude_code/{ui → tui/components}/rich/cjk_wrap.py +0 -0
  146. /klaude_code/{ui → tui/components}/rich/code_panel.py +0 -0
  147. /klaude_code/{ui → tui/components}/rich/live.py +0 -0
  148. /klaude_code/{ui → tui/components}/rich/quote.py +0 -0
  149. /klaude_code/{ui → tui/components}/rich/searchable_text.py +0 -0
  150. /klaude_code/{ui/modes/repl → tui/input}/clipboard.py +0 -0
  151. /klaude_code/{ui/modes/repl → tui/input}/key_bindings.py +0 -0
  152. /klaude_code/{ui → tui}/terminal/image.py +0 -0
  153. /klaude_code/{ui → tui}/terminal/progress_bar.py +0 -0
  154. /klaude_code/{ui → tui}/terminal/selector.py +0 -0
  155. /klaude_code/ui/{utils/common.py → common.py} +0 -0
  156. {klaude_code-2.0.2.dist-info → klaude_code-2.1.1.dist-info}/WHEEL +0 -0
  157. {klaude_code-2.0.2.dist-info → klaude_code-2.1.1.dist-info}/entry_points.txt +0 -0
@@ -1,8 +1,9 @@
1
1
  from pathlib import Path
2
2
 
3
- from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
4
3
  from klaude_code.protocol import commands, events, message, model
5
4
 
5
+ from .command_abc import Agent, CommandABC, CommandResult
6
+
6
7
 
7
8
  def _read_changelog() -> str:
8
9
  """Read CHANGELOG.md from project root."""
@@ -2,11 +2,12 @@ import asyncio
2
2
 
3
3
  from prompt_toolkit.styles import Style
4
4
 
5
- from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
5
+ from klaude_code.log import log
6
6
  from klaude_code.protocol import commands, events, message, model, op
7
7
  from klaude_code.session.selector import build_session_select_options, format_user_messages_display
8
- from klaude_code.trace import log
9
- from klaude_code.ui.terminal.selector import SelectItem, select_one
8
+ from klaude_code.tui.terminal.selector import SelectItem, select_one
9
+
10
+ from .command_abc import Agent, CommandABC, CommandResult
10
11
 
11
12
  SESSION_SELECT_STYLE = Style(
12
13
  [
@@ -1,7 +1,8 @@
1
- from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
2
1
  from klaude_code.protocol import commands, events, message, model
3
2
  from klaude_code.session.session import Session
4
3
 
4
+ from .command_abc import Agent, CommandABC, CommandResult
5
+
5
6
 
6
7
  class AggregatedUsage(model.BaseModel):
7
8
  """Aggregated usage statistics including per-model breakdown."""
@@ -2,9 +2,10 @@ import os
2
2
  import subprocess
3
3
  from pathlib import Path
4
4
 
5
- from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
6
5
  from klaude_code.protocol import commands, events, message, model
7
6
 
7
+ from .command_abc import Agent, CommandABC, CommandResult
8
+
8
9
 
9
10
  class TerminalSetupCommand(CommandABC):
10
11
  """Setup shift+enter newline functionality in terminal"""
@@ -2,10 +2,11 @@ import asyncio
2
2
 
3
3
  from prompt_toolkit.styles import Style
4
4
 
5
- from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
6
5
  from klaude_code.config.thinking import get_thinking_picker_data, parse_thinking_value
7
6
  from klaude_code.protocol import commands, events, llm_param, message, model, op
8
- from klaude_code.ui.terminal.selector import SelectItem, select_one
7
+ from klaude_code.tui.terminal.selector import SelectItem, select_one
8
+
9
+ from .command_abc import Agent, CommandABC, CommandResult
9
10
 
10
11
  SELECT_STYLE = Style(
11
12
  [
@@ -0,0 +1,164 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from rich.console import RenderableType
6
+ from rich.text import Text
7
+
8
+ from klaude_code.protocol import events
9
+
10
+
11
+ @dataclass(frozen=True, slots=True)
12
+ class RenderCommand:
13
+ pass
14
+
15
+
16
+ @dataclass(frozen=True, slots=True)
17
+ class RenderReplayHistory(RenderCommand):
18
+ event: events.ReplayHistoryEvent
19
+
20
+
21
+ @dataclass(frozen=True, slots=True)
22
+ class RenderWelcome(RenderCommand):
23
+ event: events.WelcomeEvent
24
+
25
+
26
+ @dataclass(frozen=True, slots=True)
27
+ class RenderUserMessage(RenderCommand):
28
+ event: events.UserMessageEvent
29
+
30
+
31
+ @dataclass(frozen=True, slots=True)
32
+ class RenderTaskStart(RenderCommand):
33
+ event: events.TaskStartEvent
34
+
35
+
36
+ @dataclass(frozen=True, slots=True)
37
+ class RenderDeveloperMessage(RenderCommand):
38
+ event: events.DeveloperMessageEvent
39
+
40
+
41
+ @dataclass(frozen=True, slots=True)
42
+ class RenderTurnStart(RenderCommand):
43
+ event: events.TurnStartEvent
44
+
45
+
46
+ @dataclass(frozen=True, slots=True)
47
+ class RenderAssistantImage(RenderCommand):
48
+ session_id: str
49
+ file_path: str
50
+
51
+
52
+ @dataclass(frozen=True, slots=True)
53
+ class RenderToolCall(RenderCommand):
54
+ event: events.ToolCallEvent
55
+
56
+
57
+ @dataclass(frozen=True, slots=True)
58
+ class RenderToolResult(RenderCommand):
59
+ event: events.ToolResultEvent
60
+ is_sub_agent_session: bool
61
+
62
+
63
+ @dataclass(frozen=True, slots=True)
64
+ class RenderTaskMetadata(RenderCommand):
65
+ event: events.TaskMetadataEvent
66
+
67
+
68
+ @dataclass(frozen=True, slots=True)
69
+ class RenderTaskFinish(RenderCommand):
70
+ event: events.TaskFinishEvent
71
+
72
+
73
+ @dataclass(frozen=True, slots=True)
74
+ class RenderInterrupt(RenderCommand):
75
+ session_id: str
76
+
77
+
78
+ @dataclass(frozen=True, slots=True)
79
+ class RenderError(RenderCommand):
80
+ event: events.ErrorEvent
81
+
82
+
83
+ @dataclass(frozen=True, slots=True)
84
+ class StartThinkingStream(RenderCommand):
85
+ session_id: str
86
+
87
+
88
+ @dataclass(frozen=True, slots=True)
89
+ class AppendThinking(RenderCommand):
90
+ session_id: str
91
+ content: str
92
+
93
+
94
+ @dataclass(frozen=True, slots=True)
95
+ class EndThinkingStream(RenderCommand):
96
+ session_id: str
97
+
98
+
99
+ @dataclass(frozen=True, slots=True)
100
+ class StartAssistantStream(RenderCommand):
101
+ session_id: str
102
+
103
+
104
+ @dataclass(frozen=True, slots=True)
105
+ class AppendAssistant(RenderCommand):
106
+ session_id: str
107
+ content: str
108
+
109
+
110
+ @dataclass(frozen=True, slots=True)
111
+ class EndAssistantStream(RenderCommand):
112
+ session_id: str
113
+
114
+
115
+ @dataclass(frozen=True, slots=True)
116
+ class RenderThinkingHeader(RenderCommand):
117
+ session_id: str
118
+ header: str
119
+
120
+
121
+ @dataclass(frozen=True, slots=True)
122
+ class SpinnerStart(RenderCommand):
123
+ pass
124
+
125
+
126
+ @dataclass(frozen=True, slots=True)
127
+ class SpinnerStop(RenderCommand):
128
+ pass
129
+
130
+
131
+ @dataclass(frozen=True, slots=True)
132
+ class SpinnerUpdate(RenderCommand):
133
+ status_text: str | Text
134
+ right_text: RenderableType | None
135
+
136
+
137
+ @dataclass(frozen=True, slots=True)
138
+ class PrintBlankLine(RenderCommand):
139
+ pass
140
+
141
+
142
+ @dataclass(frozen=True, slots=True)
143
+ class PrintRuleLine(RenderCommand):
144
+ pass
145
+
146
+
147
+ @dataclass(frozen=True, slots=True)
148
+ class EmitOsc94Error(RenderCommand):
149
+ pass
150
+
151
+
152
+ @dataclass(frozen=True, slots=True)
153
+ class EmitTmuxSignal(RenderCommand):
154
+ pass
155
+
156
+
157
+ @dataclass(frozen=True, slots=True)
158
+ class TaskClockStart(RenderCommand):
159
+ pass
160
+
161
+
162
+ @dataclass(frozen=True, slots=True)
163
+ class TaskClockClear(RenderCommand):
164
+ pass
@@ -3,9 +3,9 @@ from rich.padding import Padding
3
3
  from rich.text import Text
4
4
 
5
5
  from klaude_code.const import MARKDOWN_RIGHT_MARGIN
6
- from klaude_code.ui.renderers.common import create_grid
7
- from klaude_code.ui.rich.markdown import NoInsetMarkdown
8
- from klaude_code.ui.rich.theme import ThemeKey
6
+ from klaude_code.tui.components.common import create_grid
7
+ from klaude_code.tui.components.rich.markdown import NoInsetMarkdown
8
+ from klaude_code.tui.components.rich.theme import ThemeKey
9
9
 
10
10
  # UI markers
11
11
  ASSISTANT_MESSAGE_MARK = "•"
@@ -8,8 +8,8 @@ from pygments.token import Token
8
8
  from rich.text import Text
9
9
 
10
10
  from klaude_code.const import BASH_MULTILINE_STRING_TRUNCATE_MAX_LINES
11
- from klaude_code.ui.renderers.common import truncate_head
12
- from klaude_code.ui.rich.theme import ThemeKey
11
+ from klaude_code.tui.components.common import truncate_head
12
+ from klaude_code.tui.components.rich.theme import ThemeKey
13
13
 
14
14
  # Token types for bash syntax highlighting
15
15
  _STRING_TOKENS = frozenset(
@@ -11,7 +11,7 @@ from klaude_code.const import (
11
11
  TRUNCATE_DISPLAY_MAX_LINES,
12
12
  TRUNCATE_HEAD_MAX_LINES,
13
13
  )
14
- from klaude_code.ui.rich.theme import ThemeKey
14
+ from klaude_code.tui.components.rich.theme import ThemeKey
15
15
 
16
16
 
17
17
  def create_grid(*, overflow: Literal["fold", "crop", "ellipsis", "ignore"] = "fold") -> Table:
@@ -4,10 +4,10 @@ from rich.table import Table
4
4
  from rich.text import Text
5
5
 
6
6
  from klaude_code.protocol import commands, events, message, model
7
- from klaude_code.ui.renderers.common import create_grid, truncate_middle
8
- from klaude_code.ui.renderers.tools import render_path
9
- from klaude_code.ui.rich.markdown import NoInsetMarkdown
10
- from klaude_code.ui.rich.theme import ThemeKey
7
+ from klaude_code.tui.components.common import create_grid, truncate_middle
8
+ from klaude_code.tui.components.rich.markdown import NoInsetMarkdown
9
+ from klaude_code.tui.components.rich.theme import ThemeKey
10
+ from klaude_code.tui.components.tools import render_path
11
11
 
12
12
  REMINDER_BULLET = " ⧉"
13
13
 
@@ -6,8 +6,8 @@ from rich.text import Text
6
6
 
7
7
  from klaude_code.const import DIFF_PREFIX_WIDTH, MAX_DIFF_LINES
8
8
  from klaude_code.protocol import model
9
- from klaude_code.ui.renderers.common import create_grid
10
- from klaude_code.ui.rich.theme import ThemeKey
9
+ from klaude_code.tui.components.common import create_grid
10
+ from klaude_code.tui.components.rich.theme import ThemeKey
11
11
 
12
12
 
13
13
  def _make_diff_prefix(line: str, new_ln: int | None, width: int) -> tuple[str, int | None]:
@@ -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.renderers.common import create_grid
5
- from klaude_code.ui.rich.theme import ThemeKey
4
+ from klaude_code.tui.components.common import create_grid
5
+ from klaude_code.tui.components.rich.theme import ThemeKey
6
6
 
7
7
 
8
8
  def render_error(error_msg: Text) -> RenderableType:
@@ -5,12 +5,12 @@ from rich.padding import Padding
5
5
  from rich.text import Text
6
6
 
7
7
  from klaude_code.const import DEFAULT_MAX_TOKENS
8
+ from klaude_code.log import is_debug_enabled
8
9
  from klaude_code.protocol import events, model
9
- from klaude_code.trace import is_debug_enabled
10
- from klaude_code.ui.renderers.common import create_grid
11
- from klaude_code.ui.rich.quote import Quote
12
- from klaude_code.ui.rich.theme import ThemeKey
13
- from klaude_code.ui.utils.common import format_model_params, format_number
10
+ from klaude_code.tui.components.common import create_grid
11
+ from klaude_code.tui.components.rich.quote import Quote
12
+ from klaude_code.tui.components.rich.theme import ThemeKey
13
+ from klaude_code.ui.common import format_model_params, format_number
14
14
 
15
15
 
16
16
  def _get_version() -> str:
@@ -99,7 +99,7 @@ def _render_task_metadata_block(
99
99
  )
100
100
  )
101
101
  if metadata.usage is not None:
102
- # Context (only for main agent)
102
+ # Context usage
103
103
  if show_context_and_time and metadata.usage.context_usage_percent is not None:
104
104
  context_size = format_number(metadata.usage.context_size or 0)
105
105
  # Calculate effective limit (same as Usage.context_usage_percent)
@@ -169,7 +169,7 @@ def render_task_metadata(e: events.TaskMetadataEvent) -> RenderableType:
169
169
 
170
170
  # Render each sub-agent metadata block
171
171
  for meta in e.metadata.sub_agent_task_metadata:
172
- renderables.append(_render_task_metadata_block(meta, is_sub_agent=True, show_context_and_time=False))
172
+ renderables.append(_render_task_metadata_block(meta, is_sub_agent=True, show_context_and_time=True))
173
173
 
174
174
  # Add total cost line when there are sub-agents
175
175
  if e.metadata.sub_agent_task_metadata:
@@ -24,7 +24,7 @@ from klaude_code.const import (
24
24
  MARKDOWN_STREAM_SYNCHRONIZED_OUTPUT_ENABLED,
25
25
  UI_REFRESH_RATE_FPS,
26
26
  )
27
- from klaude_code.ui.rich.code_panel import CodePanel
27
+ from klaude_code.tui.components.rich.code_panel import CodePanel
28
28
 
29
29
 
30
30
  class NoInsetCodeBlock(CodeBlock):
@@ -61,7 +61,13 @@ class Divider(MarkdownElement):
61
61
 
62
62
  class MarkdownTable(TableElement):
63
63
  def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
64
- table = Table(box=box.MARKDOWN, border_style=console.get_style("markdown.table.border"))
64
+ # rich.box.MARKDOWN intentionally includes a blank top/bottom edge row. Rather than
65
+ # post-processing rendered segments, disable outer edges to avoid emitting those rows.
66
+ table = Table(
67
+ box=box.MARKDOWN,
68
+ show_edge=False,
69
+ border_style=console.get_style("markdown.table.border"),
70
+ )
65
71
 
66
72
  if self.header is not None and self.header.row is not None:
67
73
  for column in self.header.row.cells:
@@ -72,27 +78,7 @@ class MarkdownTable(TableElement):
72
78
  row_content = [element.content for element in row.cells]
73
79
  table.add_row(*row_content)
74
80
 
75
- # Render table and strip top/bottom blank lines that MARKDOWN box adds
76
- segments = list(console.render(table, options))
77
-
78
- # Skip leading blank line (before first newline)
79
- first_newline_idx = next((i for i, s in enumerate(segments) if s.text == "\n"), None)
80
- if first_newline_idx is not None:
81
- first_line = "".join(s.text for s in segments[:first_newline_idx])
82
- if not first_line.strip():
83
- segments = segments[first_newline_idx + 1 :]
84
-
85
- # Skip trailing blank line (after last newline)
86
- while len(segments) >= 2 and segments[-1].text == "\n":
87
- prev_newline = next((i for i in range(len(segments) - 2, -1, -1) if segments[i].text == "\n"), None)
88
- if prev_newline is not None:
89
- between = "".join(s.text for s in segments[prev_newline + 1 : -1])
90
- if not between.strip():
91
- segments = segments[: prev_newline + 1]
92
- continue
93
- break
94
-
95
- yield from segments
81
+ yield table
96
82
 
97
83
 
98
84
  class LeftHeading(Heading):
@@ -23,8 +23,8 @@ from klaude_code.const import (
23
23
  STATUS_SHIMMER_BAND_HALF_WIDTH,
24
24
  STATUS_SHIMMER_PADDING,
25
25
  )
26
- from klaude_code.ui.rich.theme import ThemeKey
27
- from klaude_code.ui.terminal.color import get_last_terminal_background_rgb
26
+ from klaude_code.tui.components.rich.theme import ThemeKey
27
+ from klaude_code.tui.terminal.color import get_last_terminal_background_rgb
28
28
 
29
29
  # Use an existing Rich spinner name; BreathingSpinner overrides its rendering
30
30
  BREATHING_SPINNER_NAME = "dots"
@@ -138,6 +138,7 @@ class ThemeKey(str, Enum):
138
138
  STATUS_TEXT = "spinner.status.text"
139
139
  STATUS_TEXT_BOLD = "spinner.status.text.bold"
140
140
  STATUS_TEXT_BOLD_ITALIC = "spinner.status.text.bold_italic"
141
+ STATUS_TOAST = "spinner.status.toast"
141
142
  # STATUS
142
143
  STATUS_HINT = "status.hint"
143
144
  # USER_INPUT
@@ -262,6 +263,7 @@ def get_theme(theme: str | None = None) -> Themes:
262
263
  ThemeKey.STATUS_TEXT.value: palette.blue,
263
264
  ThemeKey.STATUS_TEXT_BOLD.value: "bold " + palette.blue,
264
265
  ThemeKey.STATUS_TEXT_BOLD_ITALIC.value: "bold italic " + palette.blue,
266
+ ThemeKey.STATUS_TOAST.value: "bold " + palette.yellow,
265
267
  ThemeKey.STATUS_HINT.value: palette.grey2,
266
268
  # REMINDER
267
269
  ThemeKey.REMINDER.value: palette.grey1,
@@ -275,7 +277,7 @@ def get_theme(theme: str | None = None) -> Themes:
275
277
  ThemeKey.TOOL_RESULT.value: palette.grey_green,
276
278
  ThemeKey.TOOL_RESULT_TREE_PREFIX.value: palette.grey3 + " dim",
277
279
  ThemeKey.TOOL_RESULT_BOLD.value: "bold " + palette.grey_green,
278
- ThemeKey.TOOL_RESULT_TRUNCATED.value: palette.grey1,
280
+ ThemeKey.TOOL_RESULT_TRUNCATED.value: palette.grey1 + " dim",
279
281
  ThemeKey.TOOL_MARK.value: "bold",
280
282
  ThemeKey.TOOL_APPROVED.value: palette.green + " bold reverse",
281
283
  ThemeKey.TOOL_REJECTED.value: palette.red + " bold reverse",
@@ -1,19 +1,16 @@
1
1
  import json
2
2
  from typing import Any, cast
3
3
 
4
- from rich import box
5
4
  from rich.console import Group, RenderableType
6
5
  from rich.json import JSON
7
- from rich.panel import Panel
8
- from rich.style import Style
6
+ from rich.style import Style, StyleType
9
7
  from rich.text import Text
10
8
 
11
9
  from klaude_code.const import SUB_AGENT_RESULT_MAX_LINES
12
10
  from klaude_code.protocol import events, model
13
11
  from klaude_code.protocol.sub_agent import get_sub_agent_profile_by_tool
14
- from klaude_code.ui.renderers.common import truncate_head
15
- from klaude_code.ui.rich.markdown import NoInsetMarkdown
16
- from klaude_code.ui.rich.theme import ThemeKey
12
+ from klaude_code.tui.components.common import truncate_head
13
+ from klaude_code.tui.components.rich.theme import ThemeKey
17
14
 
18
15
 
19
16
  def _compact_schema_value(value: dict[str, Any]) -> str | list[Any] | dict[str, Any]:
@@ -83,13 +80,11 @@ def render_sub_agent_result(
83
80
  result: str,
84
81
  *,
85
82
  code_theme: str,
86
- style: Style | None = None,
83
+ style: StyleType | None = None,
87
84
  has_structured_output: bool = False,
88
85
  description: str | None = None,
89
- panel_style: Style | None = None,
90
86
  ) -> RenderableType:
91
87
  stripped_result = result.strip()
92
- result_panel_style = panel_style or ThemeKey.SUB_AGENT_RESULT_PANEL
93
88
 
94
89
  # Extract agentId footer for separate styling
95
90
  main_content, agent_id_footer = _extract_agent_id_footer(stripped_result)
@@ -106,15 +101,10 @@ def render_sub_agent_result(
106
101
  JSON(stripped_result),
107
102
  ]
108
103
  if description:
109
- group_elements.insert(0, NoInsetMarkdown(f"# {description}", code_theme=code_theme, style=style or ""))
104
+ group_elements.insert(0, Text(f"\n{description}", style=style or ""))
110
105
  if agent_id_footer:
111
106
  group_elements.append(Text(agent_id_footer, style=ThemeKey.SUB_AGENT_FOOTER))
112
- return Panel.fit(
113
- Group(*group_elements),
114
- box=box.SIMPLE,
115
- border_style=ThemeKey.LINES,
116
- style=result_panel_style,
117
- )
107
+ return Group(*group_elements)
118
108
  except json.JSONDecodeError:
119
109
  # Fall back to markdown if not valid JSON
120
110
  pass
@@ -122,42 +112,32 @@ def render_sub_agent_result(
122
112
  if not stripped_result:
123
113
  return Text()
124
114
 
125
- # Add markdown heading if description is provided for non-structured output
126
- if description:
127
- stripped_result = f"# {description}\n\n{stripped_result}"
128
-
129
115
  lines = stripped_result.splitlines()
130
116
  if len(lines) > SUB_AGENT_RESULT_MAX_LINES:
131
117
  hidden_count = len(lines) - SUB_AGENT_RESULT_MAX_LINES
132
- head_count = SUB_AGENT_RESULT_MAX_LINES // 2
133
- tail_count = SUB_AGENT_RESULT_MAX_LINES - head_count
134
- head_text = "\n".join(lines[:head_count])
135
- tail_text = "\n".join(lines[-tail_count:])
136
- truncated_elements: list[RenderableType] = [
137
- NoInsetMarkdown(head_text, code_theme=code_theme, style=style or ""),
118
+ tail_text = "\n".join(lines[-SUB_AGENT_RESULT_MAX_LINES:])
119
+ truncated_elements: list[RenderableType] = []
120
+ # Add description heading separately so it won't be truncated
121
+ if description:
122
+ truncated_elements.append(Text(f"---\n{description}", style=style or ""))
123
+ truncated_elements.append(
138
124
  Text(
139
- f"( … more {hidden_count} lines — use /export to view full output )",
125
+ f"( … more {hidden_count} lines)",
140
126
  style=ThemeKey.TOOL_RESULT_TRUNCATED,
141
- ),
142
- NoInsetMarkdown(tail_text, code_theme=code_theme, style=style or ""),
143
- ]
127
+ )
128
+ )
129
+ truncated_elements.append(Text(tail_text, style=style or ""))
144
130
  if agent_id_footer:
145
131
  truncated_elements.append(Text(agent_id_footer, style=ThemeKey.SUB_AGENT_FOOTER))
146
- return Panel.fit(
147
- Group(*truncated_elements),
148
- box=box.SIMPLE,
149
- border_style=ThemeKey.LINES,
150
- style=result_panel_style,
151
- )
152
- normal_elements: list[RenderableType] = [NoInsetMarkdown(stripped_result, code_theme=code_theme)]
132
+ return Group(*truncated_elements)
133
+
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)]
153
138
  if agent_id_footer:
154
139
  normal_elements.append(Text(agent_id_footer, style=ThemeKey.SUB_AGENT_FOOTER))
155
- return Panel.fit(
156
- Group(*normal_elements),
157
- box=box.SIMPLE,
158
- border_style=ThemeKey.LINES,
159
- style=result_panel_style,
160
- )
140
+ return Group(*normal_elements)
161
141
 
162
142
 
163
143
  def build_sub_agent_state_from_tool_call(e: events.ToolCallEvent) -> model.SubAgentState | None:
@@ -5,9 +5,9 @@ from rich.padding import Padding
5
5
  from rich.text import Text
6
6
 
7
7
  from klaude_code.const import MARKDOWN_RIGHT_MARGIN
8
- from klaude_code.ui.renderers.common import create_grid
9
- from klaude_code.ui.rich.markdown import ThinkingMarkdown
10
- from klaude_code.ui.rich.theme import ThemeKey
8
+ from klaude_code.tui.components.common import create_grid
9
+ from klaude_code.tui.components.rich.markdown import ThinkingMarkdown
10
+ from klaude_code.tui.components.rich.theme import ThemeKey
11
11
 
12
12
  # UI markers
13
13
  THINKING_MESSAGE_MARK = "∴"
@@ -17,14 +17,14 @@ from klaude_code.const import (
17
17
  )
18
18
  from klaude_code.protocol import events, model, tools
19
19
  from klaude_code.protocol.sub_agent import is_sub_agent_tool as _is_sub_agent_tool
20
- from klaude_code.ui.renderers import diffs as r_diffs
21
- from klaude_code.ui.renderers import mermaid_viewer as r_mermaid_viewer
22
- from klaude_code.ui.renderers.bash_syntax import highlight_bash_command
23
- from klaude_code.ui.renderers.common import create_grid, truncate_middle
24
- from klaude_code.ui.rich.code_panel import CodePanel
25
- from klaude_code.ui.rich.markdown import NoInsetMarkdown
26
- from klaude_code.ui.rich.quote import TreeQuote
27
- from klaude_code.ui.rich.theme import ThemeKey
20
+ from klaude_code.tui.components import diffs as r_diffs
21
+ from klaude_code.tui.components import mermaid_viewer as r_mermaid_viewer
22
+ from klaude_code.tui.components.bash_syntax import highlight_bash_command
23
+ from klaude_code.tui.components.common import create_grid, truncate_middle
24
+ from klaude_code.tui.components.rich.code_panel import CodePanel
25
+ from klaude_code.tui.components.rich.markdown import NoInsetMarkdown
26
+ from klaude_code.tui.components.rich.quote import TreeQuote
27
+ from klaude_code.tui.components.rich.theme import ThemeKey
28
28
 
29
29
  # Tool markers (Unicode symbols for UI display)
30
30
  MARK_GENERIC = "⚒"
@@ -37,7 +37,6 @@ MARK_MERMAID = "⧉"
37
37
  MARK_WEB_FETCH = "→"
38
38
  MARK_WEB_SEARCH = "✱"
39
39
  MARK_DONE = "✔"
40
- MARK_SKILL = "✪"
41
40
 
42
41
  # Todo status markers
43
42
  MARK_TODO_PENDING = "▢"
@@ -487,11 +486,11 @@ def render_mermaid_tool_result(
487
486
  *,
488
487
  session_id: str | None = None,
489
488
  ) -> RenderableType:
490
- from klaude_code.ui.terminal import supports_osc8_hyperlinks
489
+ from klaude_code.tui.terminal import supports_osc8_hyperlinks
491
490
 
492
491
  link_info = _extract_mermaid_link(tr.ui_extra)
493
492
  if link_info is None:
494
- return render_generic_tool_result(tr.result, is_error=tr.status == "error")
493
+ return render_generic_tool_result(tr.result, is_error=tr.is_error)
495
494
 
496
495
  use_osc8 = supports_osc8_hyperlinks()
497
496
  viewer = _render_mermaid_viewer_link(tr, link_info, use_osc8=use_osc8)
@@ -537,7 +536,6 @@ _TOOL_ACTIVE_FORM: dict[str, str] = {
537
536
  tools.WRITE: "Writing",
538
537
  tools.TODO_WRITE: "Planning",
539
538
  tools.UPDATE_PLAN: "Planning",
540
- tools.SKILL: "Skilling",
541
539
  tools.MERMAID: "Diagramming",
542
540
  tools.WEB_FETCH: "Fetching Web",
543
541
  tools.WEB_SEARCH: "Searching Web",
@@ -589,8 +587,6 @@ def render_tool_call(e: events.ToolCallEvent) -> RenderableType | None:
589
587
  return render_update_plan_tool_call(e.arguments)
590
588
  case tools.MERMAID:
591
589
  return render_mermaid_tool_call(e.arguments)
592
- case tools.SKILL:
593
- return render_generic_tool_call(e.tool_name, e.arguments, MARK_SKILL)
594
590
  case tools.REPORT_BACK:
595
591
  return render_report_back_tool_call()
596
592
  case tools.WEB_FETCH:
@@ -649,7 +645,7 @@ def render_tool_result(
649
645
  return TreeQuote.for_tool_result(content, is_last=e.is_last_in_turn)
650
646
 
651
647
  # Handle error case
652
- if e.status == "error" and e.ui_extra is None:
648
+ if e.is_error and e.ui_extra is None:
653
649
  return wrap(render_generic_tool_result(e.result, is_error=True))
654
650
 
655
651
  # Render multiple ui blocks if present
@@ -666,7 +662,7 @@ def render_tool_result(
666
662
  # Show truncation info if output was truncated and saved to file
667
663
  truncation_info = get_truncation_info(e)
668
664
  if truncation_info:
669
- result = render_generic_tool_result(e.result, is_error=e.status == "error")
665
+ result = render_generic_tool_result(e.result, is_error=e.is_error)
670
666
  return wrap(Group(render_truncation_info(truncation_info), result))
671
667
 
672
668
  diff_ui = _extract_diff(e.ui_extra)
@@ -675,7 +671,7 @@ def render_tool_result(
675
671
  def _render_fallback() -> TreeQuote:
676
672
  if len(e.result.strip()) == 0:
677
673
  return wrap(render_generic_tool_result("(no content)"))
678
- return wrap(render_generic_tool_result(e.result, is_error=e.status == "error"))
674
+ return wrap(render_generic_tool_result(e.result, is_error=e.is_error))
679
675
 
680
676
  match e.tool_name:
681
677
  case tools.READ: