klaude-code 2.0.2__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 (151) 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 +284 -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 +64 -99
  20. klaude_code/core/task.py +12 -20
  21. klaude_code/core/tool/__init__.py +5 -17
  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/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/op.py +5 -0
  59. klaude_code/session/session.py +6 -5
  60. klaude_code/skill/assets/create-plan/SKILL.md +76 -0
  61. klaude_code/skill/loader.py +1 -1
  62. klaude_code/skill/system_skills.py +1 -1
  63. klaude_code/tui/__init__.py +8 -0
  64. klaude_code/{command → tui/command}/clear_cmd.py +2 -1
  65. klaude_code/{command → tui/command}/debug_cmd.py +3 -2
  66. klaude_code/{command → tui/command}/export_cmd.py +2 -1
  67. klaude_code/{command → tui/command}/export_online_cmd.py +2 -1
  68. klaude_code/{command → tui/command}/fork_session_cmd.py +4 -3
  69. klaude_code/{command → tui/command}/help_cmd.py +2 -1
  70. klaude_code/{command → tui/command}/model_cmd.py +4 -3
  71. klaude_code/{command → tui/command}/model_select.py +2 -2
  72. klaude_code/{command → tui/command}/prompt_command.py +4 -3
  73. klaude_code/{command → tui/command}/refresh_cmd.py +3 -1
  74. klaude_code/{command → tui/command}/registry.py +6 -5
  75. klaude_code/{command → tui/command}/release_notes_cmd.py +2 -1
  76. klaude_code/{command → tui/command}/resume_cmd.py +4 -3
  77. klaude_code/{command → tui/command}/status_cmd.py +2 -1
  78. klaude_code/{command → tui/command}/terminal_setup_cmd.py +2 -1
  79. klaude_code/{command → tui/command}/thinking_cmd.py +3 -2
  80. klaude_code/tui/commands.py +164 -0
  81. klaude_code/{ui/renderers → tui/components}/assistant.py +3 -3
  82. klaude_code/{ui/renderers → tui/components}/bash_syntax.py +2 -2
  83. klaude_code/{ui/renderers → tui/components}/common.py +1 -1
  84. klaude_code/{ui/renderers → tui/components}/developer.py +4 -4
  85. klaude_code/{ui/renderers → tui/components}/diffs.py +2 -2
  86. klaude_code/{ui/renderers → tui/components}/errors.py +2 -2
  87. klaude_code/{ui/renderers → tui/components}/metadata.py +7 -7
  88. klaude_code/{ui → tui/components}/rich/markdown.py +9 -23
  89. klaude_code/{ui → tui/components}/rich/status.py +2 -2
  90. klaude_code/{ui → tui/components}/rich/theme.py +3 -1
  91. klaude_code/{ui/renderers → tui/components}/sub_agent.py +23 -43
  92. klaude_code/{ui/renderers → tui/components}/thinking.py +3 -3
  93. klaude_code/{ui/renderers → tui/components}/tools.py +9 -9
  94. klaude_code/{ui/renderers → tui/components}/user_input.py +3 -20
  95. klaude_code/tui/display.py +85 -0
  96. klaude_code/{ui/modes/repl → tui/input}/__init__.py +1 -1
  97. klaude_code/{ui/modes/repl → tui/input}/completers.py +1 -1
  98. klaude_code/{ui/modes/repl/input_prompt_toolkit.py → tui/input/prompt_toolkit.py} +6 -6
  99. klaude_code/tui/machine.py +606 -0
  100. klaude_code/tui/renderer.py +707 -0
  101. klaude_code/tui/runner.py +321 -0
  102. klaude_code/tui/terminal/__init__.py +56 -0
  103. klaude_code/{ui → tui}/terminal/color.py +1 -1
  104. klaude_code/{ui → tui}/terminal/control.py +1 -1
  105. klaude_code/{ui → tui}/terminal/notifier.py +1 -1
  106. klaude_code/ui/__init__.py +6 -50
  107. klaude_code/ui/core/display.py +3 -3
  108. klaude_code/ui/core/input.py +2 -1
  109. klaude_code/ui/{modes/debug/display.py → debug_mode.py} +1 -1
  110. klaude_code/ui/{modes/exec/display.py → exec_mode.py} +0 -2
  111. klaude_code/ui/terminal/__init__.py +6 -54
  112. klaude_code/ui/terminal/title.py +31 -0
  113. klaude_code/update.py +163 -0
  114. {klaude_code-2.0.2.dist-info → klaude_code-2.1.0.dist-info}/METADATA +1 -1
  115. klaude_code-2.1.0.dist-info/RECORD +235 -0
  116. klaude_code/cli/runtime.py +0 -518
  117. klaude_code/core/prompt.py +0 -108
  118. klaude_code/core/tool/tool_context.py +0 -148
  119. klaude_code/protocol/events.py +0 -195
  120. klaude_code/skill/assets/dev-docs/SKILL.md +0 -108
  121. klaude_code/trace/__init__.py +0 -21
  122. klaude_code/ui/core/stage_manager.py +0 -48
  123. klaude_code/ui/modes/__init__.py +0 -1
  124. klaude_code/ui/modes/debug/__init__.py +0 -1
  125. klaude_code/ui/modes/exec/__init__.py +0 -1
  126. klaude_code/ui/modes/repl/display.py +0 -61
  127. klaude_code/ui/modes/repl/event_handler.py +0 -629
  128. klaude_code/ui/modes/repl/renderer.py +0 -464
  129. klaude_code/ui/utils/__init__.py +0 -1
  130. klaude_code-2.0.2.dist-info/RECORD +0 -227
  131. /klaude_code/{trace/log.py → log.py} +0 -0
  132. /klaude_code/{command → tui/command}/__init__.py +0 -0
  133. /klaude_code/{command → tui/command}/command_abc.py +0 -0
  134. /klaude_code/{command → tui/command}/prompt-commit.md +0 -0
  135. /klaude_code/{command → tui/command}/prompt-init.md +0 -0
  136. /klaude_code/{ui/renderers → tui/components}/__init__.py +0 -0
  137. /klaude_code/{ui/renderers → tui/components}/mermaid_viewer.py +0 -0
  138. /klaude_code/{ui → tui/components}/rich/__init__.py +0 -0
  139. /klaude_code/{ui → tui/components}/rich/cjk_wrap.py +0 -0
  140. /klaude_code/{ui → tui/components}/rich/code_panel.py +0 -0
  141. /klaude_code/{ui → tui/components}/rich/live.py +0 -0
  142. /klaude_code/{ui → tui/components}/rich/quote.py +0 -0
  143. /klaude_code/{ui → tui/components}/rich/searchable_text.py +0 -0
  144. /klaude_code/{ui/modes/repl → tui/input}/clipboard.py +0 -0
  145. /klaude_code/{ui/modes/repl → tui/input}/key_bindings.py +0 -0
  146. /klaude_code/{ui → tui}/terminal/image.py +0 -0
  147. /klaude_code/{ui → tui}/terminal/progress_bar.py +0 -0
  148. /klaude_code/{ui → tui}/terminal/selector.py +0 -0
  149. /klaude_code/ui/{utils/common.py → common.py} +0 -0
  150. {klaude_code-2.0.2.dist-info → klaude_code-2.1.0.dist-info}/WHEEL +0 -0
  151. {klaude_code-2.0.2.dist-info → klaude_code-2.1.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,12 @@
1
+ """Application-layer runtime helpers.
2
+
3
+ This package coordinates core execution (Executor) with frontend displays.
4
+ Terminal-specific rendering and input handling live in `klaude_code.tui`.
5
+ """
6
+
7
+ from .runtime import AppInitConfig, run_exec
8
+
9
+ __all__ = [
10
+ "AppInitConfig",
11
+ "run_exec",
12
+ ]
@@ -0,0 +1,215 @@
1
+ import asyncio
2
+ import contextlib
3
+ import sys
4
+ from collections.abc import Callable
5
+ from dataclasses import dataclass
6
+
7
+ import typer
8
+
9
+ from klaude_code import ui
10
+ from klaude_code.config import Config, load_config
11
+ from klaude_code.core.agent import Agent
12
+ from klaude_code.core.agent_profile import DefaultModelProfileProvider, VanillaModelProfileProvider
13
+ from klaude_code.core.executor import Executor
14
+ from klaude_code.core.manager import build_llm_clients
15
+ from klaude_code.log import DebugType, log, set_debug_logging
16
+ from klaude_code.protocol import events, op
17
+ from klaude_code.protocol.message import UserInputPayload
18
+ from klaude_code.session.session import Session, close_default_store
19
+
20
+
21
+ @dataclass
22
+ class AppInitConfig:
23
+ """Configuration for initializing the application runtime."""
24
+
25
+ model: str | None
26
+ debug: bool
27
+ vanilla: bool
28
+ debug_filters: set[DebugType] | None = None
29
+ stream_json: bool = False
30
+
31
+
32
+ @dataclass
33
+ class AppComponents:
34
+ """Initialized runtime components."""
35
+
36
+ config: Config
37
+ executor: Executor
38
+ executor_task: asyncio.Task[None]
39
+ event_queue: asyncio.Queue[events.Event]
40
+ display: ui.DisplayABC
41
+ display_task: asyncio.Task[None]
42
+
43
+
44
+ async def initialize_app_components(
45
+ *,
46
+ init_config: AppInitConfig,
47
+ display: ui.DisplayABC,
48
+ on_model_change: Callable[[str], None] | None = None,
49
+ ) -> AppComponents:
50
+ """Initialize LLM clients, executor, and display task."""
51
+ set_debug_logging(init_config.debug, filters=init_config.debug_filters)
52
+
53
+ config = load_config()
54
+
55
+ try:
56
+ llm_clients = build_llm_clients(
57
+ config,
58
+ model_override=init_config.model,
59
+ )
60
+ except ValueError as exc:
61
+ if init_config.model:
62
+ log(
63
+ (
64
+ f"Error: model '{init_config.model}' is not defined in the config",
65
+ "red",
66
+ )
67
+ )
68
+ log(("Hint: run `klaude list` to view available models", "yellow"))
69
+ else:
70
+ log((f"Error: failed to load the default model configuration: {exc}", "red"))
71
+ raise typer.Exit(2) from None
72
+
73
+ model_profile_provider = VanillaModelProfileProvider() if init_config.vanilla else DefaultModelProfileProvider()
74
+
75
+ event_queue: asyncio.Queue[events.Event] = asyncio.Queue()
76
+
77
+ executor = Executor(
78
+ event_queue,
79
+ llm_clients,
80
+ model_profile_provider=model_profile_provider,
81
+ on_model_change=on_model_change,
82
+ )
83
+
84
+ if on_model_change is not None:
85
+ on_model_change(llm_clients.main.model_name)
86
+
87
+ executor_task = asyncio.create_task(executor.start())
88
+
89
+ def _drain_background_task_exception(task: asyncio.Task[None], *, label: str) -> None:
90
+ def _on_done(t: asyncio.Task[None]) -> None:
91
+ with contextlib.suppress(asyncio.CancelledError):
92
+ exc = t.exception()
93
+ if exc is None:
94
+ return
95
+ if isinstance(exc, KeyboardInterrupt):
96
+ return
97
+ log((f"Background task '{label}' failed: {exc}", "red"))
98
+
99
+ task.add_done_callback(_on_done)
100
+
101
+ _drain_background_task_exception(executor_task, label="executor")
102
+
103
+ display_task = asyncio.create_task(display.consume_event_loop(event_queue))
104
+ _drain_background_task_exception(display_task, label="display")
105
+
106
+ return AppComponents(
107
+ config=config,
108
+ executor=executor,
109
+ executor_task=executor_task,
110
+ event_queue=event_queue,
111
+ display=display,
112
+ display_task=display_task,
113
+ )
114
+
115
+
116
+ async def initialize_session(
117
+ executor: Executor,
118
+ event_queue: asyncio.Queue[events.Event],
119
+ session_id: str | None = None,
120
+ ) -> str | None:
121
+ """Initialize a session and return the active session id."""
122
+ await executor.submit_and_wait(op.InitAgentOperation(session_id=session_id))
123
+ await event_queue.join()
124
+
125
+ active_session_id = executor.context.current_session_id()
126
+ return active_session_id or session_id
127
+
128
+
129
+ def backfill_session_model_config(
130
+ agent: Agent | None,
131
+ model_override: str | None,
132
+ default_model: str | None,
133
+ *,
134
+ is_new_session: bool,
135
+ ) -> None:
136
+ """Backfill model_config_name and model_thinking on newly created sessions."""
137
+ if agent is None or agent.session.model_config_name is not None:
138
+ return
139
+
140
+ if model_override is not None:
141
+ agent.session.model_config_name = model_override
142
+ elif is_new_session and default_model is not None:
143
+ agent.session.model_config_name = default_model
144
+ else:
145
+ return
146
+
147
+ if agent.session.model_thinking is None and agent.profile:
148
+ agent.session.model_thinking = agent.profile.llm_client.get_llm_config().thinking
149
+
150
+
151
+ async def cleanup_app_components(components: AppComponents) -> None:
152
+ """Clean up all runtime components."""
153
+ try:
154
+ await components.executor.stop()
155
+ components.executor_task.cancel()
156
+ with contextlib.suppress(asyncio.CancelledError):
157
+ await components.executor_task
158
+ with contextlib.suppress(Exception):
159
+ await close_default_store()
160
+
161
+ await components.event_queue.put(events.EndEvent())
162
+ await components.display_task
163
+ finally:
164
+ # Ensure the terminal cursor is visible even if Rich's spinner did not stop cleanly.
165
+ with contextlib.suppress(Exception):
166
+ stream = getattr(sys, "__stdout__", None) or sys.stdout
167
+ stream.write("\033[?25h")
168
+ stream.flush()
169
+
170
+
171
+ async def handle_keyboard_interrupt(executor: Executor) -> None:
172
+ """Handle Ctrl+C by logging and sending a global interrupt."""
173
+ log("Bye!")
174
+ session_id = executor.context.current_session_id()
175
+ if session_id and Session.exists(session_id):
176
+ log(("Resume with:", "dim"), (f"klaude --resume-by-id {session_id}", "green"))
177
+ with contextlib.suppress(Exception):
178
+ await executor.submit(op.InterruptOperation(target_session_id=None))
179
+
180
+
181
+ async def run_exec(init_config: AppInitConfig, input_content: str) -> None:
182
+ """Run a single task non-interactively (exec mode)."""
183
+ from klaude_code.ui.terminal.title import update_terminal_title
184
+
185
+ display = ui.create_exec_display(debug=init_config.debug, stream_json=init_config.stream_json)
186
+ components = await initialize_app_components(
187
+ init_config=init_config,
188
+ display=display,
189
+ on_model_change=update_terminal_title,
190
+ )
191
+
192
+ try:
193
+ session_id = await initialize_session(components.executor, components.event_queue)
194
+ backfill_session_model_config(
195
+ components.executor.context.current_agent,
196
+ init_config.model,
197
+ components.config.main_model,
198
+ is_new_session=True,
199
+ )
200
+
201
+ if session_id is None:
202
+ raise RuntimeError("No active session")
203
+
204
+ op_id = await components.executor.submit(
205
+ op.RunAgentOperation(
206
+ session_id=session_id,
207
+ input=UserInputPayload(text=input_content),
208
+ )
209
+ )
210
+ await components.executor.wait_for(op_id)
211
+ await components.event_queue.join()
212
+ except KeyboardInterrupt:
213
+ await handle_keyboard_interrupt(components.executor)
214
+ finally:
215
+ await cleanup_app_components(components)
@@ -6,8 +6,8 @@ import webbrowser
6
6
  import typer
7
7
  from prompt_toolkit.styles import Style
8
8
 
9
- from klaude_code.trace import log
10
- from klaude_code.ui.terminal.selector import SelectItem, select_one
9
+ from klaude_code.log import log
10
+ from klaude_code.tui.terminal.selector import SelectItem, select_one
11
11
 
12
12
  _SELECT_STYLE = Style(
13
13
  [
@@ -7,7 +7,7 @@ import sys
7
7
  import typer
8
8
 
9
9
  from klaude_code.config import config_path, create_example_config, example_config_path, load_config
10
- from klaude_code.trace import log
10
+ from klaude_code.log import log
11
11
 
12
12
 
13
13
  def list_models(
@@ -15,7 +15,7 @@ def list_models(
15
15
  ) -> None:
16
16
  """List all models and providers configuration"""
17
17
  from klaude_code.cli.list_model import display_models_and_providers
18
- from klaude_code.ui.terminal.color import is_light_terminal_background
18
+ from klaude_code.tui.terminal.color import is_light_terminal_background
19
19
 
20
20
  config = load_config()
21
21
 
@@ -10,9 +10,9 @@ from rich.box import Box
10
10
  from rich.console import Console
11
11
  from rich.table import Table
12
12
 
13
- from klaude_code.command.status_cmd import format_cost, format_tokens
14
13
  from klaude_code.protocol import model
15
14
  from klaude_code.session.codec import decode_jsonl_line
15
+ from klaude_code.tui.command.status_cmd import format_cost, format_tokens
16
16
 
17
17
  ASCII_HORIZONAL = Box(" -- \n \n -- \n \n -- \n -- \n \n -- \n")
18
18
 
klaude_code/cli/debug.py CHANGED
@@ -1,12 +1,10 @@
1
1
  """Debug utilities for CLI."""
2
2
 
3
- import subprocess
4
- import sys
5
3
  from pathlib import Path
6
4
 
7
5
  import typer
8
6
 
9
- from klaude_code.trace import DebugType, log
7
+ from klaude_code.log import DebugType, log, prepare_debug_log_file
10
8
 
11
9
  DEBUG_FILTER_HELP = "Comma-separated debug types: " + ", ".join(dt.value for dt in DebugType)
12
10
 
@@ -41,38 +39,16 @@ def resolve_debug_settings(flag: bool, raw_filters: str | None) -> tuple[bool, s
41
39
  return effective_flag, filters
42
40
 
43
41
 
44
- def open_log_file_in_editor(path: Path) -> None:
45
- """Open the given log file in a text editor without blocking the CLI."""
42
+ def prepare_debug_logging(debug: bool, debug_filter: str | None) -> tuple[bool, set[DebugType] | None, Path | None]:
43
+ """Resolve debug settings and prepare log file if enabled.
46
44
 
47
- editor = ""
45
+ Returns:
46
+ A tuple of (debug_enabled, debug_filters, log_path).
47
+ log_path is None if debugging is disabled.
48
+ """
48
49
 
49
- for cmd in ["code", "TextEdit", "notepad"]:
50
- try:
51
- subprocess.run(["which", cmd], check=True, capture_output=True)
52
- editor = cmd
53
- break
54
- except (subprocess.CalledProcessError, FileNotFoundError):
55
- continue
56
-
57
- if not editor:
58
- if sys.platform == "darwin":
59
- editor = "open"
60
- elif sys.platform == "win32":
61
- editor = "notepad"
62
- else:
63
- editor = "xdg-open"
64
-
65
- try:
66
- # Detach stdin to prevent the editor from interfering with terminal input state.
67
- # Without this, the spawned process inherits the parent's TTY and can disrupt
68
- # prompt_toolkit's keyboard handling (e.g., history navigation with up/down keys).
69
- subprocess.Popen(
70
- [editor, str(path)],
71
- stdin=subprocess.DEVNULL,
72
- stdout=subprocess.DEVNULL,
73
- stderr=subprocess.DEVNULL,
74
- )
75
- except FileNotFoundError:
76
- log((f"Error: Editor '{editor}' not found", "red"))
77
- except Exception as exc: # pragma: no cover - best effort
78
- log((f"Warning: failed to open log file in editor: {exc}", "yellow"))
50
+ debug_enabled, debug_filters = resolve_debug_settings(debug, debug_filter)
51
+ log_path: Path | None = None
52
+ if debug_enabled:
53
+ log_path = prepare_debug_log_file()
54
+ return debug_enabled, debug_filters, log_path
@@ -9,9 +9,9 @@ from klaude_code.config import Config
9
9
  from klaude_code.config.config import ModelConfig, ProviderConfig, parse_env_var_syntax
10
10
  from klaude_code.protocol.llm_param import LLMClientProtocol
11
11
  from klaude_code.protocol.sub_agent import iter_sub_agent_profiles
12
- from klaude_code.ui.rich.quote import Quote
13
- from klaude_code.ui.rich.theme import ThemeKey, get_theme
14
- from klaude_code.ui.utils.common import format_model_params
12
+ from klaude_code.tui.components.rich.quote import Quote
13
+ from klaude_code.tui.components.rich.theme import ThemeKey, get_theme
14
+ from klaude_code.ui.common import format_model_params
15
15
 
16
16
 
17
17
  def _get_codex_status_rows() -> list[tuple[Text, Text]]:
klaude_code/cli/main.py CHANGED
@@ -1,62 +1,17 @@
1
1
  import asyncio
2
- import contextlib
3
- import os
4
2
  import sys
5
- from pathlib import Path
6
3
 
7
4
  import typer
8
5
 
9
6
  from klaude_code.cli.auth_cmd import register_auth_commands
10
7
  from klaude_code.cli.config_cmd import register_config_commands
11
8
  from klaude_code.cli.cost_cmd import register_cost_commands
12
- from klaude_code.cli.debug import DEBUG_FILTER_HELP, open_log_file_in_editor, resolve_debug_settings
9
+ from klaude_code.cli.debug import DEBUG_FILTER_HELP, prepare_debug_logging
13
10
  from klaude_code.cli.self_update import register_self_update_commands, version_option_callback
14
11
  from klaude_code.cli.session_cmd import register_session_commands
15
- from klaude_code.command.resume_cmd import select_session_sync
16
12
  from klaude_code.session import Session
17
- from klaude_code.trace import DebugType, prepare_debug_log_file
18
-
19
-
20
- def set_terminal_title(title: str) -> None:
21
- """Set terminal window title using ANSI escape sequence."""
22
- # Never write terminal control sequences when stdout is not a TTY (pipes/CI/redirects).
23
- # This avoids corrupting machine-readable output (e.g., JSON streaming) and log captures.
24
- #
25
- # Use the original stdout to bypass prompt_toolkit's `patch_stdout()`. Writing OSC
26
- # sequences to the patched stdout can cause them to appear as visible text.
27
- stream = getattr(sys, "__stdout__", None) or sys.stdout
28
- try:
29
- if not stream.isatty():
30
- return
31
- except Exception:
32
- return
33
-
34
- stream.write(f"\033]0;{title}\007")
35
- with contextlib.suppress(Exception):
36
- stream.flush()
37
-
38
-
39
- def update_terminal_title(model_name: str | None = None) -> None:
40
- """Update terminal title with folder name and optional model name."""
41
- folder_name = os.path.basename(os.getcwd())
42
- if model_name:
43
- set_terminal_title(f"{folder_name}: klaude ✳ {model_name}")
44
- else:
45
- set_terminal_title(f"{folder_name}: klaude")
46
-
47
-
48
- def prepare_debug_logging(debug: bool, debug_filter: str | None) -> tuple[bool, set[DebugType] | None, Path | None]:
49
- """Resolve debug settings and prepare log file if debugging is enabled.
50
-
51
- Returns:
52
- A tuple of (debug_enabled, debug_filters, log_path).
53
- log_path is None if debugging is disabled.
54
- """
55
- debug_enabled, debug_filters = resolve_debug_settings(debug, debug_filter)
56
- log_path: Path | None = None
57
- if debug_enabled:
58
- log_path = prepare_debug_log_file()
59
- return debug_enabled, debug_filters, log_path
13
+ from klaude_code.tui.command.resume_cmd import select_session_sync
14
+ from klaude_code.ui.terminal.title import update_terminal_title
60
15
 
61
16
 
62
17
  def read_input_content(cli_argument: str) -> str | None:
@@ -68,7 +23,7 @@ def read_input_content(cli_argument: str) -> str | None:
68
23
  Returns:
69
24
  The merged input content, or None if no input was provided.
70
25
  """
71
- from klaude_code.trace import log
26
+ from klaude_code.log import log
72
27
 
73
28
  parts: list[str] = []
74
29
 
@@ -169,9 +124,9 @@ def exec_command(
169
124
  if merged_input is None:
170
125
  raise typer.Exit(1)
171
126
 
172
- from klaude_code.cli.runtime import AppInitConfig, run_exec
173
- from klaude_code.command.model_select import select_model_interactive
127
+ from klaude_code.app.runtime import AppInitConfig, run_exec
174
128
  from klaude_code.config import load_config
129
+ from klaude_code.tui.command.model_select import select_model_interactive
175
130
 
176
131
  chosen_model = model
177
132
  if model or select_model:
@@ -188,7 +143,7 @@ def exec_command(
188
143
  # Save the selection as default
189
144
  config.main_model = chosen_model
190
145
  from klaude_code.config.config import config_path
191
- from klaude_code.trace import log
146
+ from klaude_code.log import log
192
147
 
193
148
  asyncio.run(config.save())
194
149
  log(f"Saved main_model={chosen_model} to {config_path}", style="cyan")
@@ -199,13 +154,14 @@ def exec_command(
199
154
  model=chosen_model,
200
155
  debug=debug_enabled,
201
156
  vanilla=vanilla,
202
- is_exec_mode=True,
203
157
  debug_filters=debug_filters,
204
158
  stream_json=stream_json,
205
159
  )
206
160
 
207
161
  if log_path:
208
- open_log_file_in_editor(log_path)
162
+ from klaude_code.log import log
163
+
164
+ log(f"Debug log: {log_path}", style="dim")
209
165
 
210
166
  asyncio.run(
211
167
  run_exec(
@@ -269,7 +225,7 @@ def main_callback(
269
225
  ) -> None:
270
226
  # Only run interactive mode when no subcommand is invoked
271
227
  if ctx.invoked_subcommand is None:
272
- from klaude_code.trace import log
228
+ from klaude_code.log import log
273
229
 
274
230
  resume_by_id_value = resume_by_id.strip() if resume_by_id is not None else None
275
231
  if resume_by_id_value == "":
@@ -304,8 +260,9 @@ def main_callback(
304
260
  )
305
261
  return
306
262
 
307
- from klaude_code.cli.runtime import AppInitConfig, run_interactive
308
- from klaude_code.command.model_select import select_model_interactive
263
+ from klaude_code.app.runtime import AppInitConfig
264
+ from klaude_code.tui.command.model_select import select_model_interactive
265
+ from klaude_code.tui.runner import run_interactive
309
266
 
310
267
  update_terminal_title()
311
268
 
@@ -333,7 +290,7 @@ def main_callback(
333
290
 
334
291
  if session_id is not None and chosen_model is None:
335
292
  from klaude_code.config import load_config
336
- from klaude_code.trace import log
293
+ from klaude_code.log import log
337
294
 
338
295
  session_meta = Session.load_meta(session_id)
339
296
  cfg = load_config()
@@ -372,7 +329,7 @@ def main_callback(
372
329
  # Save the selection as default
373
330
  cfg.main_model = chosen_model
374
331
  from klaude_code.config.config import config_path
375
- from klaude_code.trace import log
332
+ from klaude_code.log import log
376
333
 
377
334
  asyncio.run(cfg.save())
378
335
  log(f"Saved main_model={chosen_model} to {config_path}", style="dim")
@@ -387,7 +344,7 @@ def main_callback(
387
344
  )
388
345
 
389
346
  if log_path:
390
- open_log_file_in_editor(log_path)
347
+ log(f"Debug log: {log_path}", style="dim")
391
348
 
392
349
  asyncio.run(
393
350
  run_interactive(