klaude-code 2.0.1__py3-none-any.whl → 2.1.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.
Files changed (160) 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 +10 -1
  14. klaude_code/core/agent.py +9 -62
  15. klaude_code/core/agent_profile.py +284 -0
  16. klaude_code/core/executor.py +343 -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 +107 -155
  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 +42 -44
  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/skill/skill_tool.py +3 -1
  30. klaude_code/core/tool/sub_agent_tool.py +8 -7
  31. klaude_code/core/tool/todo/todo_write_tool.py +3 -9
  32. klaude_code/core/tool/todo/update_plan_tool.py +3 -5
  33. klaude_code/core/tool/tool_abc.py +2 -1
  34. klaude_code/core/tool/tool_registry.py +2 -33
  35. klaude_code/core/tool/tool_runner.py +13 -10
  36. klaude_code/core/tool/web/mermaid_tool.py +3 -1
  37. klaude_code/core/tool/web/web_fetch_tool.py +5 -3
  38. klaude_code/core/tool/web/web_search_tool.py +5 -3
  39. klaude_code/core/turn.py +86 -26
  40. klaude_code/llm/anthropic/client.py +1 -1
  41. klaude_code/llm/bedrock/client.py +1 -1
  42. klaude_code/llm/claude/client.py +1 -1
  43. klaude_code/llm/codex/client.py +1 -1
  44. klaude_code/llm/google/client.py +1 -1
  45. klaude_code/llm/openai_compatible/client.py +1 -1
  46. klaude_code/llm/openai_compatible/tool_call_accumulator.py +1 -1
  47. klaude_code/llm/openrouter/client.py +1 -1
  48. klaude_code/llm/openrouter/reasoning.py +1 -1
  49. klaude_code/llm/responses/client.py +1 -1
  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 +23 -0
  58. klaude_code/protocol/message.py +3 -11
  59. klaude_code/protocol/model.py +78 -9
  60. klaude_code/protocol/op.py +5 -0
  61. klaude_code/protocol/sub_agent/explore.py +0 -15
  62. klaude_code/protocol/sub_agent/task.py +1 -1
  63. klaude_code/protocol/sub_agent/web.py +1 -17
  64. klaude_code/protocol/tools.py +0 -1
  65. klaude_code/session/session.py +6 -5
  66. klaude_code/skill/assets/create-plan/SKILL.md +76 -0
  67. klaude_code/skill/loader.py +1 -1
  68. klaude_code/skill/system_skills.py +1 -1
  69. klaude_code/tui/__init__.py +8 -0
  70. klaude_code/{command → tui/command}/clear_cmd.py +2 -1
  71. klaude_code/{command → tui/command}/debug_cmd.py +4 -3
  72. klaude_code/{command → tui/command}/export_cmd.py +2 -1
  73. klaude_code/{command → tui/command}/export_online_cmd.py +6 -5
  74. klaude_code/{command → tui/command}/fork_session_cmd.py +10 -9
  75. klaude_code/{command → tui/command}/help_cmd.py +3 -2
  76. klaude_code/{command → tui/command}/model_cmd.py +5 -4
  77. klaude_code/{command → tui/command}/model_select.py +2 -2
  78. klaude_code/{command → tui/command}/prompt_command.py +4 -3
  79. klaude_code/{command → tui/command}/refresh_cmd.py +3 -1
  80. klaude_code/{command → tui/command}/registry.py +16 -6
  81. klaude_code/{command → tui/command}/release_notes_cmd.py +3 -2
  82. klaude_code/{command → tui/command}/resume_cmd.py +6 -5
  83. klaude_code/{command → tui/command}/status_cmd.py +4 -3
  84. klaude_code/{command → tui/command}/terminal_setup_cmd.py +4 -3
  85. klaude_code/{command → tui/command}/thinking_cmd.py +4 -3
  86. klaude_code/tui/commands.py +164 -0
  87. klaude_code/{ui/renderers → tui/components}/assistant.py +3 -3
  88. klaude_code/{ui/renderers → tui/components}/bash_syntax.py +2 -2
  89. klaude_code/{ui/renderers → tui/components}/common.py +1 -1
  90. klaude_code/tui/components/developer.py +231 -0
  91. klaude_code/{ui/renderers → tui/components}/diffs.py +2 -2
  92. klaude_code/{ui/renderers → tui/components}/errors.py +2 -2
  93. klaude_code/{ui/renderers → tui/components}/metadata.py +34 -21
  94. klaude_code/{ui → tui/components}/rich/markdown.py +78 -34
  95. klaude_code/{ui → tui/components}/rich/status.py +2 -2
  96. klaude_code/{ui → tui/components}/rich/theme.py +12 -5
  97. klaude_code/{ui/renderers → tui/components}/sub_agent.py +23 -43
  98. klaude_code/{ui/renderers → tui/components}/thinking.py +3 -3
  99. klaude_code/{ui/renderers → tui/components}/tools.py +11 -48
  100. klaude_code/{ui/renderers → tui/components}/user_input.py +3 -20
  101. klaude_code/tui/display.py +85 -0
  102. klaude_code/{ui/modes/repl → tui/input}/__init__.py +1 -1
  103. klaude_code/{ui/modes/repl → tui/input}/completers.py +1 -1
  104. klaude_code/{ui/modes/repl/input_prompt_toolkit.py → tui/input/prompt_toolkit.py} +11 -7
  105. klaude_code/tui/machine.py +606 -0
  106. klaude_code/tui/renderer.py +707 -0
  107. klaude_code/tui/runner.py +321 -0
  108. klaude_code/tui/terminal/__init__.py +56 -0
  109. klaude_code/{ui → tui}/terminal/color.py +1 -1
  110. klaude_code/{ui → tui}/terminal/control.py +1 -1
  111. klaude_code/{ui → tui}/terminal/notifier.py +1 -1
  112. klaude_code/{ui → tui}/terminal/selector.py +36 -17
  113. klaude_code/ui/__init__.py +6 -50
  114. klaude_code/ui/core/display.py +3 -3
  115. klaude_code/ui/core/input.py +2 -1
  116. klaude_code/ui/{modes/debug/display.py → debug_mode.py} +1 -1
  117. klaude_code/ui/{modes/exec/display.py → exec_mode.py} +1 -4
  118. klaude_code/ui/terminal/__init__.py +6 -54
  119. klaude_code/ui/terminal/title.py +31 -0
  120. klaude_code/update.py +163 -0
  121. {klaude_code-2.0.1.dist-info → klaude_code-2.1.0.dist-info}/METADATA +1 -1
  122. klaude_code-2.1.0.dist-info/RECORD +235 -0
  123. klaude_code/cli/runtime.py +0 -525
  124. klaude_code/core/prompt.py +0 -108
  125. klaude_code/core/tool/file/move_tool.md +0 -41
  126. klaude_code/core/tool/file/move_tool.py +0 -435
  127. klaude_code/core/tool/tool_context.py +0 -148
  128. klaude_code/protocol/events.py +0 -194
  129. klaude_code/skill/assets/dev-docs/SKILL.md +0 -108
  130. klaude_code/trace/__init__.py +0 -21
  131. klaude_code/ui/core/stage_manager.py +0 -48
  132. klaude_code/ui/modes/__init__.py +0 -1
  133. klaude_code/ui/modes/debug/__init__.py +0 -1
  134. klaude_code/ui/modes/exec/__init__.py +0 -1
  135. klaude_code/ui/modes/repl/display.py +0 -61
  136. klaude_code/ui/modes/repl/event_handler.py +0 -634
  137. klaude_code/ui/modes/repl/renderer.py +0 -463
  138. klaude_code/ui/renderers/developer.py +0 -215
  139. klaude_code/ui/utils/__init__.py +0 -1
  140. klaude_code-2.0.1.dist-info/RECORD +0 -229
  141. /klaude_code/{trace/log.py → log.py} +0 -0
  142. /klaude_code/{command → tui/command}/__init__.py +0 -0
  143. /klaude_code/{command → tui/command}/command_abc.py +0 -0
  144. /klaude_code/{command → tui/command}/prompt-commit.md +0 -0
  145. /klaude_code/{command → tui/command}/prompt-init.md +0 -0
  146. /klaude_code/{ui/renderers → tui/components}/__init__.py +0 -0
  147. /klaude_code/{ui/renderers → tui/components}/mermaid_viewer.py +0 -0
  148. /klaude_code/{ui → tui/components}/rich/__init__.py +0 -0
  149. /klaude_code/{ui → tui/components}/rich/cjk_wrap.py +0 -0
  150. /klaude_code/{ui → tui/components}/rich/code_panel.py +0 -0
  151. /klaude_code/{ui → tui/components}/rich/live.py +0 -0
  152. /klaude_code/{ui → tui/components}/rich/quote.py +0 -0
  153. /klaude_code/{ui → tui/components}/rich/searchable_text.py +0 -0
  154. /klaude_code/{ui/modes/repl → tui/input}/clipboard.py +0 -0
  155. /klaude_code/{ui/modes/repl → tui/input}/key_bindings.py +0 -0
  156. /klaude_code/{ui → tui}/terminal/image.py +0 -0
  157. /klaude_code/{ui → tui}/terminal/progress_bar.py +0 -0
  158. /klaude_code/ui/{utils/common.py → common.py} +0 -0
  159. {klaude_code-2.0.1.dist-info → klaude_code-2.1.0.dist-info}/WHEEL +0 -0
  160. {klaude_code-2.0.1.dist-info → klaude_code-2.1.0.dist-info}/entry_points.txt +0 -0
@@ -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 = "⚒"
@@ -33,7 +33,6 @@ MARK_PLAN = "◈"
33
33
  MARK_READ = "→"
34
34
  MARK_EDIT = "±"
35
35
  MARK_WRITE = "+"
36
- MARK_MOVE = "±"
37
36
  MARK_MERMAID = "⧉"
38
37
  MARK_WEB_FETCH = "→"
39
38
  MARK_WEB_SEARCH = "✱"
@@ -139,6 +138,7 @@ def render_bash_tool_call(arguments: str) -> RenderableType:
139
138
  if isinstance(command, str) and command.strip():
140
139
  cmd_str = command.strip()
141
140
  highlighted = highlight_bash_command(cmd_str)
141
+ highlighted.stylize(ThemeKey.CODE_BACKGROUND)
142
142
 
143
143
  display_line_count = len(highlighted.plain.splitlines())
144
144
 
@@ -277,35 +277,6 @@ def render_write_tool_call(arguments: str) -> RenderableType:
277
277
  return _render_tool_call_tree(mark=MARK_WRITE, tool_name=tool_name, details=details)
278
278
 
279
279
 
280
- def render_move_tool_call(arguments: str) -> RenderableType:
281
- tool_name = "Move"
282
-
283
- try:
284
- payload = json.loads(arguments)
285
- except json.JSONDecodeError:
286
- details = Text(
287
- arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
288
- style=ThemeKey.INVALID_TOOL_CALL_ARGS,
289
- )
290
- return _render_tool_call_tree(mark=MARK_MOVE, tool_name=tool_name, details=details)
291
-
292
- source_path = payload.get("source_file_path", "")
293
- target_path = payload.get("target_file_path", "")
294
- start_line = payload.get("start_line", "")
295
- end_line = payload.get("end_line", "")
296
-
297
- parts = Text()
298
- if source_path:
299
- parts.append_text(render_path(source_path, ThemeKey.TOOL_PARAM_FILE_PATH))
300
- if start_line and end_line:
301
- parts.append(f":{start_line}-{end_line}", style=ThemeKey.TOOL_PARAM)
302
- parts.append(" -> ", style=ThemeKey.TOOL_PARAM)
303
- if target_path:
304
- parts.append_text(render_path(target_path, ThemeKey.TOOL_PARAM_FILE_PATH))
305
-
306
- return _render_tool_call_tree(mark=MARK_MOVE, tool_name=tool_name, details=parts)
307
-
308
-
309
280
  def render_apply_patch_tool_call(arguments: str) -> RenderableType:
310
281
  tool_name = "Apply Patch"
311
282
 
@@ -516,7 +487,7 @@ def render_mermaid_tool_result(
516
487
  *,
517
488
  session_id: str | None = None,
518
489
  ) -> RenderableType:
519
- from klaude_code.ui.terminal import supports_osc8_hyperlinks
490
+ from klaude_code.tui.terminal import supports_osc8_hyperlinks
520
491
 
521
492
  link_info = _extract_mermaid_link(tr.ui_extra)
522
493
  if link_info is None:
@@ -561,7 +532,6 @@ def render_report_back_tool_call() -> RenderableType:
561
532
  _TOOL_ACTIVE_FORM: dict[str, str] = {
562
533
  tools.BASH: "Bashing",
563
534
  tools.APPLY_PATCH: "Patching",
564
- tools.MOVE: "Moving",
565
535
  tools.EDIT: "Editing",
566
536
  tools.READ: "Reading",
567
537
  tools.WRITE: "Writing",
@@ -609,8 +579,6 @@ def render_tool_call(e: events.ToolCallEvent) -> RenderableType | None:
609
579
  return render_edit_tool_call(e.arguments)
610
580
  case tools.WRITE:
611
581
  return render_write_tool_call(e.arguments)
612
- case tools.MOVE:
613
- return render_move_tool_call(e.arguments)
614
582
  case tools.BASH:
615
583
  return render_bash_tool_call(e.arguments)
616
584
  case tools.APPLY_PATCH:
@@ -691,7 +659,7 @@ def render_tool_result(
691
659
  if isinstance(item, model.MarkdownDocUIExtra):
692
660
  rendered.append(render_markdown_doc(item, code_theme=code_theme))
693
661
  elif isinstance(item, model.DiffUIExtra):
694
- show_file_name = e.tool_name in (tools.APPLY_PATCH, tools.MOVE)
662
+ show_file_name = e.tool_name == tools.APPLY_PATCH
695
663
  rendered.append(r_diffs.render_structured_diff(item, show_file_name=show_file_name))
696
664
  return wrap(Group(*rendered)) if rendered else None
697
665
 
@@ -718,11 +686,6 @@ def render_tool_result(
718
686
  if md_ui:
719
687
  return wrap(render_markdown_doc(md_ui, code_theme=code_theme))
720
688
  return wrap(r_diffs.render_structured_diff(diff_ui) if diff_ui else Text(""))
721
- case tools.MOVE:
722
- # Same-file move returns single DiffUIExtra, cross-file returns MultiUIExtra (handled above)
723
- if diff_ui:
724
- return wrap(r_diffs.render_structured_diff(diff_ui, show_file_name=True))
725
- return None
726
689
  case tools.APPLY_PATCH:
727
690
  if md_ui:
728
691
  return wrap(render_markdown_doc(md_ui, code_theme=code_theme))
@@ -1,29 +1,12 @@
1
1
  import re
2
- from collections.abc import Callable
3
2
 
4
3
  from rich.console import Group, RenderableType
5
4
  from rich.text import Text
6
5
 
7
6
  from klaude_code.skill import get_available_skills
8
- from klaude_code.ui.renderers.common import create_grid
9
- from klaude_code.ui.rich.theme import ThemeKey
10
-
11
- # Module-level command name checker. Set by cli/runtime.py on startup.
12
- _command_name_checker: Callable[[str], bool] | None = None
13
-
14
-
15
- def set_command_name_checker(checker: Callable[[str], bool]) -> None:
16
- """Set the command name validation function (called from runtime layer)."""
17
- global _command_name_checker
18
- _command_name_checker = checker
19
-
20
-
21
- def is_slash_command_name(name: str) -> bool:
22
- """Check if name is a valid slash command using the injected checker."""
23
- if _command_name_checker is None:
24
- return False
25
- return _command_name_checker(name)
26
-
7
+ from klaude_code.tui.command import is_slash_command_name
8
+ from klaude_code.tui.components.common import create_grid
9
+ from klaude_code.tui.components.rich.theme import ThemeKey
27
10
 
28
11
  # Match @-file patterns only when they appear at the beginning of the line
29
12
  # or immediately after whitespace, to avoid treating mid-word email-like
@@ -0,0 +1,85 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import contextlib
5
+ from collections.abc import Coroutine
6
+ from typing import Any, override
7
+
8
+ from klaude_code.protocol import events
9
+ from klaude_code.tui.machine import DisplayStateMachine
10
+ from klaude_code.tui.renderer import TUICommandRenderer
11
+ from klaude_code.tui.terminal.notifier import TerminalNotifier
12
+ from klaude_code.ui.core.display import DisplayABC
13
+
14
+
15
+ class TUIDisplay(DisplayABC):
16
+ """Interactive terminal display using Rich for rendering."""
17
+
18
+ def __init__(self, theme: str | None = None, notifier: TerminalNotifier | None = None):
19
+ self._notifier = notifier or TerminalNotifier()
20
+ self._machine = DisplayStateMachine()
21
+ self._renderer = TUICommandRenderer(theme=theme, notifier=self._notifier)
22
+
23
+ self._sigint_toast_clear_handle: asyncio.Handle | None = None
24
+ self._bg_tasks: set[asyncio.Task[None]] = set()
25
+
26
+ def _create_bg_task(self, coro: Coroutine[Any, Any, None]) -> None:
27
+ task = asyncio.create_task(coro)
28
+ self._bg_tasks.add(task)
29
+ task.add_done_callback(self._bg_tasks.discard)
30
+
31
+ @override
32
+ async def consume_event(self, event: events.Event) -> None:
33
+ commands = self._machine.transition(event)
34
+ await self._renderer.execute(commands)
35
+
36
+ @override
37
+ async def start(self) -> None:
38
+ pass
39
+
40
+ @override
41
+ async def stop(self) -> None:
42
+ if self._sigint_toast_clear_handle is not None:
43
+ with contextlib.suppress(Exception):
44
+ self._sigint_toast_clear_handle.cancel()
45
+ self._sigint_toast_clear_handle = None
46
+
47
+ for task in list(self._bg_tasks):
48
+ with contextlib.suppress(Exception):
49
+ task.cancel()
50
+ self._bg_tasks.clear()
51
+
52
+ await self._renderer.stop()
53
+
54
+ with contextlib.suppress(Exception):
55
+ self._renderer.stop_bottom_live()
56
+
57
+ def show_sigint_exit_toast(self, *, window_seconds: float = 2.0) -> None:
58
+ """Show a transient Ctrl+C hint in the TUI status line."""
59
+
60
+ async def _apply_show() -> None:
61
+ await self._renderer.execute(self._machine.show_sigint_exit_toast())
62
+
63
+ async def _apply_clear() -> None:
64
+ await self._renderer.execute(self._machine.clear_sigint_exit_toast())
65
+
66
+ loop = asyncio.get_running_loop()
67
+ self._create_bg_task(_apply_show())
68
+
69
+ if self._sigint_toast_clear_handle is not None:
70
+ with contextlib.suppress(Exception):
71
+ self._sigint_toast_clear_handle.cancel()
72
+ self._sigint_toast_clear_handle = None
73
+
74
+ def _schedule_clear() -> None:
75
+ self._create_bg_task(_apply_clear())
76
+
77
+ self._sigint_toast_clear_handle = loop.call_later(window_seconds, _schedule_clear)
78
+
79
+ def hide_progress_ui(self) -> None:
80
+ """Stop transient Rich UI elements before prompt-toolkit takes control."""
81
+
82
+ with contextlib.suppress(Exception):
83
+ self._renderer.spinner_stop()
84
+ with contextlib.suppress(Exception):
85
+ self._renderer.stop_bottom_live()
@@ -1,4 +1,4 @@
1
- from klaude_code.ui.modes.repl.input_prompt_toolkit import REPLStatusSnapshot
1
+ from klaude_code.tui.input.prompt_toolkit import REPLStatusSnapshot
2
2
 
3
3
 
4
4
  def build_repl_status_snapshot(update_message: str | None) -> REPLStatusSnapshot:
@@ -28,8 +28,8 @@ from prompt_toolkit.document import Document
28
28
  from prompt_toolkit.formatted_text import FormattedText
29
29
 
30
30
  from klaude_code.const import COMPLETER_CACHE_TTL_SEC, COMPLETER_CMD_TIMEOUT_SEC, COMPLETER_DEBOUNCE_SEC
31
+ from klaude_code.log import DebugType, log_debug
31
32
  from klaude_code.protocol.commands import CommandInfo
32
- from klaude_code.trace.log import DebugType, log_debug
33
33
 
34
34
  # Pattern to match @token for completion refresh (used by key bindings).
35
35
  # Supports both plain tokens like `@src/file.py` and quoted tokens like
@@ -35,13 +35,13 @@ from klaude_code.config.thinking import (
35
35
  from klaude_code.protocol import llm_param
36
36
  from klaude_code.protocol.commands import CommandInfo
37
37
  from klaude_code.protocol.message import UserInputPayload
38
+ from klaude_code.tui.components.user_input import USER_MESSAGE_MARK
39
+ from klaude_code.tui.input.clipboard import capture_clipboard_tag, copy_to_clipboard, extract_images_from_text
40
+ from klaude_code.tui.input.completers import AT_TOKEN_PATTERN, create_repl_completer
41
+ from klaude_code.tui.input.key_bindings import create_key_bindings
42
+ from klaude_code.tui.terminal.color import is_light_terminal_background
43
+ from klaude_code.tui.terminal.selector import SelectItem, SelectOverlay, build_model_select_items
38
44
  from klaude_code.ui.core.input import InputProviderABC
39
- from klaude_code.ui.modes.repl.clipboard import capture_clipboard_tag, copy_to_clipboard, extract_images_from_text
40
- from klaude_code.ui.modes.repl.completers import AT_TOKEN_PATTERN, create_repl_completer
41
- from klaude_code.ui.modes.repl.key_bindings import create_key_bindings
42
- from klaude_code.ui.renderers.user_input import USER_MESSAGE_MARK
43
- from klaude_code.ui.terminal.color import is_light_terminal_background
44
- from klaude_code.ui.terminal.selector import SelectItem, SelectOverlay, build_model_select_items
45
45
 
46
46
 
47
47
  class REPLStatusSnapshot(NamedTuple):
@@ -314,7 +314,7 @@ class PromptToolkitInput(InputProviderABC):
314
314
  "question": "bold",
315
315
  "msg": "",
316
316
  "meta": "fg:ansibrightblack",
317
- "frame.border": "fg:ansibrightblack",
317
+ "frame.border": "fg:ansibrightblack dim",
318
318
  "search_prefix": "fg:ansibrightblack",
319
319
  "search_placeholder": "fg:ansibrightblack italic",
320
320
  "search_input": "",
@@ -604,6 +604,10 @@ class PromptToolkitInput(InputProviderABC):
604
604
  (symbol_style, " ctrl-l "),
605
605
  (text_style, " "),
606
606
  (text_style, "models"),
607
+ (text_style, " "),
608
+ (symbol_style, " ctrl-t "),
609
+ (text_style, " "),
610
+ (text_style, "think"),
607
611
  ]
608
612
  )
609
613