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.
Files changed (140) hide show
  1. klaude_code/cli/main.py +9 -4
  2. klaude_code/cli/runtime.py +42 -43
  3. klaude_code/command/__init__.py +7 -5
  4. klaude_code/command/clear_cmd.py +6 -29
  5. klaude_code/command/command_abc.py +44 -8
  6. klaude_code/command/diff_cmd.py +33 -27
  7. klaude_code/command/export_cmd.py +18 -26
  8. klaude_code/command/help_cmd.py +10 -8
  9. klaude_code/command/model_cmd.py +11 -40
  10. klaude_code/command/{prompt-update-dev-doc.md → prompt-dev-docs-update.md} +3 -2
  11. klaude_code/command/{prompt-dev-doc.md → prompt-dev-docs.md} +3 -2
  12. klaude_code/command/prompt-init.md +2 -5
  13. klaude_code/command/prompt_command.py +6 -6
  14. klaude_code/command/refresh_cmd.py +4 -5
  15. klaude_code/command/registry.py +16 -19
  16. klaude_code/command/terminal_setup_cmd.py +12 -11
  17. klaude_code/config/__init__.py +4 -0
  18. klaude_code/config/config.py +25 -26
  19. klaude_code/config/list_model.py +8 -3
  20. klaude_code/config/select_model.py +1 -1
  21. klaude_code/const/__init__.py +1 -1
  22. klaude_code/core/__init__.py +0 -3
  23. klaude_code/core/agent.py +25 -50
  24. klaude_code/core/executor.py +268 -101
  25. klaude_code/core/prompt.py +12 -12
  26. klaude_code/core/{prompt → prompts}/prompt-gemini.md +1 -1
  27. klaude_code/core/reminders.py +76 -95
  28. klaude_code/core/task.py +21 -14
  29. klaude_code/core/tool/__init__.py +45 -11
  30. klaude_code/core/tool/file/apply_patch.py +5 -1
  31. klaude_code/core/tool/file/apply_patch_tool.py +11 -13
  32. klaude_code/core/tool/file/edit_tool.py +27 -23
  33. klaude_code/core/tool/file/multi_edit_tool.py +15 -17
  34. klaude_code/core/tool/file/read_tool.py +41 -36
  35. klaude_code/core/tool/file/write_tool.py +13 -15
  36. klaude_code/core/tool/memory/memory_tool.py +85 -68
  37. klaude_code/core/tool/memory/skill_tool.py +10 -12
  38. klaude_code/core/tool/shell/bash_tool.py +24 -22
  39. klaude_code/core/tool/shell/command_safety.py +12 -1
  40. klaude_code/core/tool/sub_agent_tool.py +11 -12
  41. klaude_code/core/tool/todo/todo_write_tool.py +21 -28
  42. klaude_code/core/tool/todo/update_plan_tool.py +14 -24
  43. klaude_code/core/tool/tool_abc.py +3 -4
  44. klaude_code/core/tool/tool_context.py +7 -7
  45. klaude_code/core/tool/tool_registry.py +30 -47
  46. klaude_code/core/tool/tool_runner.py +35 -43
  47. klaude_code/core/tool/truncation.py +14 -20
  48. klaude_code/core/tool/web/mermaid_tool.py +12 -14
  49. klaude_code/core/tool/web/web_fetch_tool.py +15 -17
  50. klaude_code/core/turn.py +19 -7
  51. klaude_code/llm/__init__.py +3 -4
  52. klaude_code/llm/anthropic/client.py +30 -46
  53. klaude_code/llm/anthropic/input.py +4 -11
  54. klaude_code/llm/client.py +29 -8
  55. klaude_code/llm/input_common.py +66 -36
  56. klaude_code/llm/openai_compatible/client.py +42 -84
  57. klaude_code/llm/openai_compatible/input.py +11 -16
  58. klaude_code/llm/openai_compatible/tool_call_accumulator.py +2 -2
  59. klaude_code/llm/openrouter/client.py +40 -289
  60. klaude_code/llm/openrouter/input.py +13 -35
  61. klaude_code/llm/openrouter/reasoning_handler.py +209 -0
  62. klaude_code/llm/registry.py +5 -75
  63. klaude_code/llm/responses/client.py +34 -55
  64. klaude_code/llm/responses/input.py +24 -26
  65. klaude_code/llm/usage.py +109 -0
  66. klaude_code/protocol/__init__.py +4 -0
  67. klaude_code/protocol/events.py +3 -2
  68. klaude_code/protocol/{llm_parameter.py → llm_param.py} +12 -32
  69. klaude_code/protocol/model.py +49 -4
  70. klaude_code/protocol/op.py +18 -16
  71. klaude_code/protocol/op_handler.py +28 -0
  72. klaude_code/{core → protocol}/sub_agent.py +7 -0
  73. klaude_code/session/export.py +150 -70
  74. klaude_code/session/session.py +28 -14
  75. klaude_code/session/templates/export_session.html +180 -42
  76. klaude_code/trace/__init__.py +2 -2
  77. klaude_code/trace/log.py +11 -5
  78. klaude_code/ui/__init__.py +91 -8
  79. klaude_code/ui/core/__init__.py +1 -0
  80. klaude_code/ui/core/display.py +103 -0
  81. klaude_code/ui/core/input.py +71 -0
  82. klaude_code/ui/modes/__init__.py +1 -0
  83. klaude_code/ui/modes/debug/__init__.py +1 -0
  84. klaude_code/ui/{base/debug_event_display.py → modes/debug/display.py} +9 -5
  85. klaude_code/ui/modes/exec/__init__.py +1 -0
  86. klaude_code/ui/{base/exec_display.py → modes/exec/display.py} +28 -2
  87. klaude_code/ui/{repl → modes/repl}/__init__.py +5 -6
  88. klaude_code/ui/modes/repl/clipboard.py +152 -0
  89. klaude_code/ui/modes/repl/completers.py +429 -0
  90. klaude_code/ui/modes/repl/display.py +60 -0
  91. klaude_code/ui/modes/repl/event_handler.py +375 -0
  92. klaude_code/ui/modes/repl/input_prompt_toolkit.py +198 -0
  93. klaude_code/ui/modes/repl/key_bindings.py +170 -0
  94. klaude_code/ui/{repl → modes/repl}/renderer.py +109 -132
  95. klaude_code/ui/renderers/assistant.py +21 -0
  96. klaude_code/ui/renderers/common.py +0 -16
  97. klaude_code/ui/renderers/developer.py +18 -18
  98. klaude_code/ui/renderers/diffs.py +36 -14
  99. klaude_code/ui/renderers/errors.py +1 -1
  100. klaude_code/ui/renderers/metadata.py +50 -27
  101. klaude_code/ui/renderers/sub_agent.py +43 -9
  102. klaude_code/ui/renderers/thinking.py +33 -1
  103. klaude_code/ui/renderers/tools.py +212 -20
  104. klaude_code/ui/renderers/user_input.py +19 -23
  105. klaude_code/ui/rich/__init__.py +1 -0
  106. klaude_code/ui/{rich_ext → rich}/searchable_text.py +3 -1
  107. klaude_code/ui/{renderers → rich}/status.py +29 -18
  108. klaude_code/ui/{base → rich}/theme.py +8 -2
  109. klaude_code/ui/terminal/__init__.py +1 -0
  110. klaude_code/ui/{base/terminal_color.py → terminal/color.py} +4 -1
  111. klaude_code/ui/{base/terminal_control.py → terminal/control.py} +1 -0
  112. klaude_code/ui/{base/terminal_notifier.py → terminal/notifier.py} +5 -2
  113. klaude_code/ui/utils/__init__.py +1 -0
  114. klaude_code/ui/{base/utils.py → utils/common.py} +35 -3
  115. {klaude_code-1.2.1.dist-info → klaude_code-1.2.3.dist-info}/METADATA +1 -1
  116. klaude_code-1.2.3.dist-info/RECORD +161 -0
  117. klaude_code/core/clipboard_manifest.py +0 -124
  118. klaude_code/llm/openrouter/tool_call_accumulator.py +0 -80
  119. klaude_code/ui/base/__init__.py +0 -1
  120. klaude_code/ui/base/display_abc.py +0 -36
  121. klaude_code/ui/base/input_abc.py +0 -20
  122. klaude_code/ui/repl/display.py +0 -36
  123. klaude_code/ui/repl/event_handler.py +0 -247
  124. klaude_code/ui/repl/input.py +0 -773
  125. klaude_code/ui/rich_ext/__init__.py +0 -1
  126. klaude_code-1.2.1.dist-info/RECORD +0 -151
  127. /klaude_code/core/{prompt → prompts}/prompt-claude-code.md +0 -0
  128. /klaude_code/core/{prompt → prompts}/prompt-codex.md +0 -0
  129. /klaude_code/core/{prompt → prompts}/prompt-subagent-explore.md +0 -0
  130. /klaude_code/core/{prompt → prompts}/prompt-subagent-oracle.md +0 -0
  131. /klaude_code/core/{prompt → prompts}/prompt-subagent-webfetch.md +0 -0
  132. /klaude_code/core/{prompt → prompts}/prompt-subagent.md +0 -0
  133. /klaude_code/ui/{base → core}/stage_manager.py +0 -0
  134. /klaude_code/ui/{rich_ext → rich}/live.py +0 -0
  135. /klaude_code/ui/{rich_ext → rich}/markdown.py +0 -0
  136. /klaude_code/ui/{rich_ext → rich}/quote.py +0 -0
  137. /klaude_code/ui/{base → terminal}/progress_bar.py +0 -0
  138. /klaude_code/ui/{base → utils}/debouncer.py +0 -0
  139. {klaude_code-1.2.1.dist-info → klaude_code-1.2.3.dist-info}/WHEEL +0 -0
  140. {klaude_code-1.2.1.dist-info → klaude_code-1.2.3.dist-info}/entry_points.txt +0 -0
@@ -1,247 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Awaitable, Callable
4
-
5
- from klaude_code.const import UI_REFRESH_RATE_FPS
6
- from klaude_code.protocol import events
7
- from klaude_code.ui.base.debouncer import Debouncer
8
- from klaude_code.ui.base.progress_bar import OSC94States, emit_osc94
9
- from klaude_code.ui.base.stage_manager import Stage, StageManager
10
- from klaude_code.ui.base.terminal_notifier import Notification, NotificationType, TerminalNotifier
11
- from klaude_code.ui.base.theme import ThemeKey
12
- from klaude_code.ui.renderers import errors as r_errors
13
- from klaude_code.ui.renderers import metadata as r_metadata
14
- from klaude_code.ui.renderers import status as r_status
15
- from klaude_code.ui.renderers import sub_agent as r_sub_agent
16
- from klaude_code.ui.renderers import thinking as r_thinking
17
- from klaude_code.ui.renderers import user_input as r_user_input
18
- from klaude_code.ui.renderers.common import truncate_display
19
- from klaude_code.ui.repl.renderer import REPLRenderer
20
- from klaude_code.ui.rich_ext.markdown import MarkdownStream, NoInsetMarkdown
21
-
22
-
23
- class StreamState:
24
- def __init__(self, interval: float, flush_handler: Callable[["StreamState"], Awaitable[None]]):
25
- self.buffer: str = ""
26
- self.mdstream: MarkdownStream | None = None
27
- self._flush_handler = flush_handler
28
- self.debouncer = Debouncer(interval=interval, callback=self._debounced_flush)
29
-
30
- async def _debounced_flush(self) -> None:
31
- await self._flush_handler(self)
32
-
33
- def append(self, content: str) -> None:
34
- self.buffer += content
35
-
36
- def clear(self) -> None:
37
- self.buffer = ""
38
-
39
-
40
- class DisplayEventHandler:
41
- """Handle REPL events, buffering and delegating rendering work."""
42
-
43
- def __init__(self, renderer: REPLRenderer, notifier: TerminalNotifier | None = None):
44
- self.renderer = renderer
45
- self.notifier = notifier
46
- self.assistant_stream = StreamState(
47
- interval=1 / UI_REFRESH_RATE_FPS, flush_handler=self._flush_assistant_buffer
48
- )
49
-
50
- self.stage_manager = StageManager(
51
- finish_assistant=self.finish_assistant_stream,
52
- on_enter_thinking=self._print_thinking_prefix,
53
- )
54
-
55
- async def consume_event(self, event: events.Event) -> None:
56
- match event:
57
- case events.ReplayHistoryEvent() as replay_event:
58
- await self.renderer.replay_history(replay_event)
59
- self.renderer.spinner.stop()
60
- case events.WelcomeEvent() as welcome_event:
61
- self.renderer.print(r_metadata.render_welcome(welcome_event, box_style=self.renderer.box_style()))
62
- case events.UserMessageEvent() as user_event:
63
- self.renderer.print(r_user_input.render_user_input(user_event.content))
64
- case events.TaskStartEvent() as task_event:
65
- self.renderer.spinner.start()
66
- self.renderer.register_session(task_event.session_id, task_event.sub_agent_state)
67
- if task_event.sub_agent_state is not None:
68
- # Print sub-agent task call
69
- with self.renderer.session_print_context(task_event.session_id):
70
- self.renderer.print(
71
- r_sub_agent.render_sub_agent_call(
72
- task_event.sub_agent_state,
73
- self.renderer.get_session_sub_agent_color(task_event.session_id),
74
- )
75
- )
76
- emit_osc94(OSC94States.INDETERMINATE)
77
- case events.DeveloperMessageEvent() as developer_event:
78
- self.renderer.display_developer_message(developer_event)
79
- self.renderer.display_command_output(developer_event)
80
- case events.TurnStartEvent() as turn_start_event:
81
- emit_osc94(OSC94States.INDETERMINATE)
82
- if not self.renderer.is_sub_agent_session(turn_start_event.session_id):
83
- self.renderer.print()
84
- case events.ThinkingEvent() as thinking_event:
85
- if self.renderer.is_sub_agent_session(thinking_event.session_id):
86
- return
87
- await self.stage_manager.enter_thinking_stage()
88
- self.renderer.display_thinking(thinking_event.content)
89
- case events.AssistantMessageDeltaEvent() as assistant_delta:
90
- if self.renderer.is_sub_agent_session(assistant_delta.session_id):
91
- return
92
- if len(assistant_delta.content.strip()) == 0 and self.stage_manager.current_stage != Stage.ASSISTANT:
93
- return
94
- first_delta = self.assistant_stream.mdstream is None
95
- if first_delta:
96
- self.assistant_stream.mdstream = MarkdownStream(
97
- mdargs={"code_theme": self.renderer.themes.code_theme},
98
- theme=self.renderer.themes.markdown_theme,
99
- console=self.renderer.console,
100
- spinner=self.renderer.spinner.renderable,
101
- mark="➤",
102
- indent=2,
103
- )
104
- self.assistant_stream.append(assistant_delta.content)
105
- if first_delta and self.assistant_stream.mdstream is not None:
106
- # Stop spinner and immediately start MarkdownStream's Live
107
- # to avoid flicker. The update() call starts the Live with
108
- # the spinner embedded, providing seamless transition.
109
- self.renderer.spinner.stop()
110
- self.assistant_stream.mdstream.update(self.assistant_stream.buffer)
111
- await self.stage_manager.transition_to(Stage.ASSISTANT)
112
- self.assistant_stream.debouncer.schedule()
113
- case events.AssistantMessageEvent() as assistant_event:
114
- if self.renderer.is_sub_agent_session(assistant_event.session_id):
115
- return
116
- await self.stage_manager.transition_to(Stage.ASSISTANT)
117
- if self.assistant_stream.mdstream is not None:
118
- self.assistant_stream.debouncer.cancel()
119
- self.assistant_stream.mdstream.update(assistant_event.content.strip(), final=True)
120
- else:
121
- content = assistant_event.content.strip()
122
- if content:
123
- self.renderer.print(
124
- NoInsetMarkdown(
125
- content,
126
- code_theme=self.renderer.themes.code_theme,
127
- )
128
- )
129
- self.renderer.print()
130
- self.assistant_stream.clear()
131
- self.assistant_stream.mdstream = None
132
- await self.stage_manager.transition_to(Stage.WAITING)
133
- self.renderer.spinner.start()
134
- case events.TurnToolCallStartEvent():
135
- pass
136
- case events.ToolCallEvent() as tool_call_event:
137
- await self.stage_manager.transition_to(Stage.TOOL_CALL)
138
- with self.renderer.session_print_context(tool_call_event.session_id):
139
- self.renderer.display_tool_call(tool_call_event)
140
- case events.ToolResultEvent() as tool_result_event:
141
- if self.renderer.is_sub_agent_session(tool_result_event.session_id):
142
- return
143
- await self.stage_manager.transition_to(Stage.TOOL_RESULT)
144
- self.renderer.display_tool_call_result(tool_result_event)
145
- case events.ResponseMetadataEvent() as metadata_event:
146
- with self.renderer.session_print_context(metadata_event.session_id):
147
- self.renderer.print(r_metadata.render_response_metadata(metadata_event))
148
- self.renderer.print()
149
- case events.TodoChangeEvent() as todo_event:
150
- active_form_status_text = self._extract_active_form_text(todo_event)
151
- if len(active_form_status_text) > 0:
152
- self.renderer.spinner.update(
153
- r_status.render_status_text(active_form_status_text, ThemeKey.SPINNER_STATUS_TEXT)
154
- )
155
- else:
156
- self.renderer.spinner.update(
157
- r_status.render_status_text("Thinking …", ThemeKey.SPINNER_STATUS_TEXT)
158
- )
159
- case events.TurnEndEvent():
160
- pass
161
- case events.TaskFinishEvent() as task_finish_event:
162
- if self.renderer.is_sub_agent_session(task_finish_event.session_id):
163
- with self.renderer.session_print_context(task_finish_event.session_id):
164
- self.renderer.print(
165
- r_sub_agent.render_sub_agent_result(
166
- task_finish_event.task_result,
167
- code_theme=self.renderer.themes.code_theme,
168
- )
169
- )
170
- else:
171
- emit_osc94(OSC94States.HIDDEN)
172
- self.renderer.spinner.stop()
173
- await self.stage_manager.transition_to(Stage.WAITING)
174
- self._maybe_notify_task_finish(task_finish_event)
175
- case events.InterruptEvent():
176
- self.renderer.spinner.stop()
177
- await self.stage_manager.transition_to(Stage.WAITING)
178
- emit_osc94(OSC94States.HIDDEN)
179
- self.renderer.print(r_user_input.render_interrupt())
180
- case events.ErrorEvent() as error_event:
181
- emit_osc94(OSC94States.ERROR)
182
- await self.stage_manager.transition_to(Stage.WAITING)
183
- self.renderer.print(
184
- r_errors.render_error(
185
- self.renderer.console.render_str(truncate_display(error_event.error_message)),
186
- indent=0,
187
- )
188
- )
189
- if not error_event.can_retry:
190
- self.renderer.spinner.stop()
191
- case events.EndEvent():
192
- emit_osc94(OSC94States.HIDDEN)
193
- await self.stage_manager.transition_to(Stage.WAITING)
194
- self.renderer.spinner.stop()
195
-
196
- async def stop(self) -> None:
197
- await self.assistant_stream.debouncer.flush()
198
- self.assistant_stream.debouncer.cancel()
199
-
200
- async def finish_assistant_stream(self) -> None:
201
- if self.assistant_stream.mdstream is not None:
202
- self.assistant_stream.debouncer.cancel()
203
- self.assistant_stream.mdstream.update(self.assistant_stream.buffer, final=True)
204
- self.assistant_stream.mdstream = None
205
- self.assistant_stream.clear()
206
-
207
- def _print_thinking_prefix(self) -> None:
208
- self.renderer.print(r_thinking.thinking_prefix())
209
-
210
- async def _flush_assistant_buffer(self, state: StreamState) -> None:
211
- if state.mdstream is not None:
212
- state.mdstream.update(state.buffer)
213
-
214
- def _maybe_notify_task_finish(self, event: events.TaskFinishEvent) -> None:
215
- if self.notifier is None:
216
- return
217
- if self.renderer.is_sub_agent_session(event.session_id):
218
- return
219
- notification = self._build_task_finish_notification(event)
220
- self.notifier.notify(notification)
221
-
222
- def _build_task_finish_notification(self, event: events.TaskFinishEvent) -> Notification:
223
- body = self._compact_result_text(event.task_result)
224
- return Notification(
225
- type=NotificationType.AGENT_TASK_COMPLETE,
226
- title="Task Completed",
227
- body=body,
228
- )
229
-
230
- def _compact_result_text(self, text: str) -> str | None:
231
- stripped = text.strip()
232
- if len(stripped) == 0:
233
- return None
234
- squashed = " ".join(stripped.split())
235
- if len(squashed) > 200:
236
- return squashed[:197] + "..."
237
- return squashed
238
-
239
- def _extract_active_form_text(self, todo_event: events.TodoChangeEvent) -> str:
240
- status_text = ""
241
- for todo in todo_event.todos:
242
- if todo.status == "in_progress":
243
- if len(todo.activeForm) > 0:
244
- status_text = todo.activeForm
245
- if len(todo.content) > 0:
246
- status_text = todo.content
247
- return status_text.replace("\n", "")