klaude-code 1.2.6__py3-none-any.whl → 1.8.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 (205) hide show
  1. klaude_code/auth/__init__.py +24 -0
  2. klaude_code/auth/codex/__init__.py +20 -0
  3. klaude_code/auth/codex/exceptions.py +17 -0
  4. klaude_code/auth/codex/jwt_utils.py +45 -0
  5. klaude_code/auth/codex/oauth.py +229 -0
  6. klaude_code/auth/codex/token_manager.py +84 -0
  7. klaude_code/cli/auth_cmd.py +73 -0
  8. klaude_code/cli/config_cmd.py +91 -0
  9. klaude_code/cli/cost_cmd.py +338 -0
  10. klaude_code/cli/debug.py +78 -0
  11. klaude_code/cli/list_model.py +307 -0
  12. klaude_code/cli/main.py +233 -134
  13. klaude_code/cli/runtime.py +309 -117
  14. klaude_code/{version.py → cli/self_update.py} +114 -5
  15. klaude_code/cli/session_cmd.py +37 -21
  16. klaude_code/command/__init__.py +88 -27
  17. klaude_code/command/clear_cmd.py +8 -7
  18. klaude_code/command/command_abc.py +31 -31
  19. klaude_code/command/debug_cmd.py +79 -0
  20. klaude_code/command/export_cmd.py +19 -53
  21. klaude_code/command/export_online_cmd.py +154 -0
  22. klaude_code/command/fork_session_cmd.py +267 -0
  23. klaude_code/command/help_cmd.py +7 -8
  24. klaude_code/command/model_cmd.py +60 -10
  25. klaude_code/command/model_select.py +84 -0
  26. klaude_code/command/prompt-jj-describe.md +32 -0
  27. klaude_code/command/prompt_command.py +19 -11
  28. klaude_code/command/refresh_cmd.py +8 -10
  29. klaude_code/command/registry.py +139 -40
  30. klaude_code/command/release_notes_cmd.py +84 -0
  31. klaude_code/command/resume_cmd.py +111 -0
  32. klaude_code/command/status_cmd.py +104 -60
  33. klaude_code/command/terminal_setup_cmd.py +7 -9
  34. klaude_code/command/thinking_cmd.py +98 -0
  35. klaude_code/config/__init__.py +14 -6
  36. klaude_code/config/assets/__init__.py +1 -0
  37. klaude_code/config/assets/builtin_config.yaml +303 -0
  38. klaude_code/config/builtin_config.py +38 -0
  39. klaude_code/config/config.py +378 -109
  40. klaude_code/config/select_model.py +117 -53
  41. klaude_code/config/thinking.py +269 -0
  42. klaude_code/{const/__init__.py → const.py} +50 -19
  43. klaude_code/core/agent.py +20 -28
  44. klaude_code/core/executor.py +327 -112
  45. klaude_code/core/manager/__init__.py +2 -4
  46. klaude_code/core/manager/llm_clients.py +1 -15
  47. klaude_code/core/manager/llm_clients_builder.py +10 -11
  48. klaude_code/core/manager/sub_agent_manager.py +37 -6
  49. klaude_code/core/prompt.py +63 -44
  50. klaude_code/core/prompts/prompt-claude-code.md +2 -13
  51. klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +117 -0
  52. klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +117 -0
  53. klaude_code/core/prompts/prompt-codex.md +9 -42
  54. klaude_code/core/prompts/prompt-minimal.md +12 -0
  55. klaude_code/core/prompts/{prompt-subagent-explore.md → prompt-sub-agent-explore.md} +16 -3
  56. klaude_code/core/prompts/{prompt-subagent-oracle.md → prompt-sub-agent-oracle.md} +1 -2
  57. klaude_code/core/prompts/prompt-sub-agent-web.md +51 -0
  58. klaude_code/core/reminders.py +283 -95
  59. klaude_code/core/task.py +113 -75
  60. klaude_code/core/tool/__init__.py +24 -31
  61. klaude_code/core/tool/file/_utils.py +36 -0
  62. klaude_code/core/tool/file/apply_patch.py +17 -25
  63. klaude_code/core/tool/file/apply_patch_tool.py +57 -77
  64. klaude_code/core/tool/file/diff_builder.py +151 -0
  65. klaude_code/core/tool/file/edit_tool.py +50 -63
  66. klaude_code/core/tool/file/move_tool.md +41 -0
  67. klaude_code/core/tool/file/move_tool.py +435 -0
  68. klaude_code/core/tool/file/read_tool.md +1 -1
  69. klaude_code/core/tool/file/read_tool.py +86 -86
  70. klaude_code/core/tool/file/write_tool.py +59 -69
  71. klaude_code/core/tool/report_back_tool.py +84 -0
  72. klaude_code/core/tool/shell/bash_tool.py +265 -22
  73. klaude_code/core/tool/shell/command_safety.py +3 -6
  74. klaude_code/core/tool/{memory → skill}/skill_tool.py +16 -26
  75. klaude_code/core/tool/sub_agent_tool.py +13 -2
  76. klaude_code/core/tool/todo/todo_write_tool.md +0 -157
  77. klaude_code/core/tool/todo/todo_write_tool.py +1 -1
  78. klaude_code/core/tool/todo/todo_write_tool_raw.md +182 -0
  79. klaude_code/core/tool/todo/update_plan_tool.py +1 -1
  80. klaude_code/core/tool/tool_abc.py +18 -0
  81. klaude_code/core/tool/tool_context.py +27 -12
  82. klaude_code/core/tool/tool_registry.py +7 -7
  83. klaude_code/core/tool/tool_runner.py +44 -36
  84. klaude_code/core/tool/truncation.py +29 -14
  85. klaude_code/core/tool/web/mermaid_tool.md +43 -0
  86. klaude_code/core/tool/web/mermaid_tool.py +2 -5
  87. klaude_code/core/tool/web/web_fetch_tool.md +1 -1
  88. klaude_code/core/tool/web/web_fetch_tool.py +112 -22
  89. klaude_code/core/tool/web/web_search_tool.md +23 -0
  90. klaude_code/core/tool/web/web_search_tool.py +130 -0
  91. klaude_code/core/turn.py +168 -66
  92. klaude_code/llm/__init__.py +2 -10
  93. klaude_code/llm/anthropic/client.py +190 -178
  94. klaude_code/llm/anthropic/input.py +39 -15
  95. klaude_code/llm/bedrock/__init__.py +3 -0
  96. klaude_code/llm/bedrock/client.py +60 -0
  97. klaude_code/llm/client.py +7 -21
  98. klaude_code/llm/codex/__init__.py +5 -0
  99. klaude_code/llm/codex/client.py +149 -0
  100. klaude_code/llm/google/__init__.py +3 -0
  101. klaude_code/llm/google/client.py +309 -0
  102. klaude_code/llm/google/input.py +215 -0
  103. klaude_code/llm/input_common.py +3 -9
  104. klaude_code/llm/openai_compatible/client.py +72 -164
  105. klaude_code/llm/openai_compatible/input.py +6 -4
  106. klaude_code/llm/openai_compatible/stream.py +273 -0
  107. klaude_code/llm/openai_compatible/tool_call_accumulator.py +17 -1
  108. klaude_code/llm/openrouter/client.py +89 -160
  109. klaude_code/llm/openrouter/input.py +18 -30
  110. klaude_code/llm/openrouter/reasoning.py +118 -0
  111. klaude_code/llm/registry.py +39 -7
  112. klaude_code/llm/responses/client.py +184 -171
  113. klaude_code/llm/responses/input.py +20 -1
  114. klaude_code/llm/usage.py +17 -12
  115. klaude_code/protocol/commands.py +17 -1
  116. klaude_code/protocol/events.py +31 -4
  117. klaude_code/protocol/llm_param.py +13 -10
  118. klaude_code/protocol/model.py +232 -29
  119. klaude_code/protocol/op.py +90 -1
  120. klaude_code/protocol/op_handler.py +35 -1
  121. klaude_code/protocol/sub_agent/__init__.py +117 -0
  122. klaude_code/protocol/sub_agent/explore.py +63 -0
  123. klaude_code/protocol/sub_agent/oracle.py +91 -0
  124. klaude_code/protocol/sub_agent/task.py +61 -0
  125. klaude_code/protocol/sub_agent/web.py +79 -0
  126. klaude_code/protocol/tools.py +4 -2
  127. klaude_code/session/__init__.py +2 -2
  128. klaude_code/session/codec.py +71 -0
  129. klaude_code/session/export.py +293 -86
  130. klaude_code/session/selector.py +89 -67
  131. klaude_code/session/session.py +320 -309
  132. klaude_code/session/store.py +220 -0
  133. klaude_code/session/templates/export_session.html +595 -83
  134. klaude_code/session/templates/mermaid_viewer.html +926 -0
  135. klaude_code/skill/__init__.py +27 -0
  136. klaude_code/skill/assets/deslop/SKILL.md +17 -0
  137. klaude_code/skill/assets/dev-docs/SKILL.md +108 -0
  138. klaude_code/skill/assets/handoff/SKILL.md +39 -0
  139. klaude_code/skill/assets/jj-workspace/SKILL.md +20 -0
  140. klaude_code/skill/assets/skill-creator/SKILL.md +139 -0
  141. klaude_code/{core/tool/memory/skill_loader.py → skill/loader.py} +55 -15
  142. klaude_code/skill/manager.py +70 -0
  143. klaude_code/skill/system_skills.py +192 -0
  144. klaude_code/trace/__init__.py +20 -2
  145. klaude_code/trace/log.py +150 -5
  146. klaude_code/ui/__init__.py +4 -9
  147. klaude_code/ui/core/input.py +1 -1
  148. klaude_code/ui/core/stage_manager.py +7 -7
  149. klaude_code/ui/modes/debug/display.py +2 -1
  150. klaude_code/ui/modes/repl/__init__.py +3 -48
  151. klaude_code/ui/modes/repl/clipboard.py +5 -5
  152. klaude_code/ui/modes/repl/completers.py +487 -123
  153. klaude_code/ui/modes/repl/display.py +5 -4
  154. klaude_code/ui/modes/repl/event_handler.py +370 -117
  155. klaude_code/ui/modes/repl/input_prompt_toolkit.py +552 -105
  156. klaude_code/ui/modes/repl/key_bindings.py +146 -23
  157. klaude_code/ui/modes/repl/renderer.py +189 -99
  158. klaude_code/ui/renderers/assistant.py +9 -2
  159. klaude_code/ui/renderers/bash_syntax.py +178 -0
  160. klaude_code/ui/renderers/common.py +78 -0
  161. klaude_code/ui/renderers/developer.py +104 -48
  162. klaude_code/ui/renderers/diffs.py +87 -6
  163. klaude_code/ui/renderers/errors.py +11 -6
  164. klaude_code/ui/renderers/mermaid_viewer.py +57 -0
  165. klaude_code/ui/renderers/metadata.py +112 -76
  166. klaude_code/ui/renderers/sub_agent.py +92 -7
  167. klaude_code/ui/renderers/thinking.py +40 -18
  168. klaude_code/ui/renderers/tools.py +405 -227
  169. klaude_code/ui/renderers/user_input.py +73 -13
  170. klaude_code/ui/rich/__init__.py +10 -1
  171. klaude_code/ui/rich/cjk_wrap.py +228 -0
  172. klaude_code/ui/rich/code_panel.py +131 -0
  173. klaude_code/ui/rich/live.py +17 -0
  174. klaude_code/ui/rich/markdown.py +305 -170
  175. klaude_code/ui/rich/searchable_text.py +10 -13
  176. klaude_code/ui/rich/status.py +190 -49
  177. klaude_code/ui/rich/theme.py +135 -39
  178. klaude_code/ui/terminal/__init__.py +55 -0
  179. klaude_code/ui/terminal/color.py +1 -1
  180. klaude_code/ui/terminal/control.py +13 -22
  181. klaude_code/ui/terminal/notifier.py +44 -4
  182. klaude_code/ui/terminal/selector.py +658 -0
  183. klaude_code/ui/utils/common.py +0 -18
  184. klaude_code-1.8.0.dist-info/METADATA +377 -0
  185. klaude_code-1.8.0.dist-info/RECORD +219 -0
  186. {klaude_code-1.2.6.dist-info → klaude_code-1.8.0.dist-info}/entry_points.txt +1 -0
  187. klaude_code/command/diff_cmd.py +0 -138
  188. klaude_code/command/prompt-dev-docs-update.md +0 -56
  189. klaude_code/command/prompt-dev-docs.md +0 -46
  190. klaude_code/config/list_model.py +0 -162
  191. klaude_code/core/manager/agent_manager.py +0 -127
  192. klaude_code/core/prompts/prompt-subagent-webfetch.md +0 -46
  193. klaude_code/core/tool/file/multi_edit_tool.md +0 -42
  194. klaude_code/core/tool/file/multi_edit_tool.py +0 -199
  195. klaude_code/core/tool/memory/memory_tool.md +0 -16
  196. klaude_code/core/tool/memory/memory_tool.py +0 -462
  197. klaude_code/llm/openrouter/reasoning_handler.py +0 -209
  198. klaude_code/protocol/sub_agent.py +0 -348
  199. klaude_code/ui/utils/debouncer.py +0 -42
  200. klaude_code-1.2.6.dist-info/METADATA +0 -178
  201. klaude_code-1.2.6.dist-info/RECORD +0 -167
  202. /klaude_code/core/prompts/{prompt-subagent.md → prompt-sub-agent.md} +0 -0
  203. /klaude_code/core/tool/{memory → skill}/__init__.py +0 -0
  204. /klaude_code/core/tool/{memory → skill}/skill_tool.md +0 -0
  205. {klaude_code-1.2.6.dist-info → klaude_code-1.8.0.dist-info}/WHEEL +0 -0
@@ -1,47 +1,70 @@
1
- from klaude_code.command.command_abc import CommandABC, CommandResult
2
- from klaude_code.command.registry import register_command
3
- from klaude_code.core.agent import Agent
1
+ from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
4
2
  from klaude_code.protocol import commands, events, model
5
3
  from klaude_code.session.session import Session
6
4
 
7
5
 
8
- def accumulate_session_usage(session: Session) -> tuple[model.Usage, int]:
9
- """Accumulate usage statistics from all ResponseMetadataItems in session history.
6
+ class AggregatedUsage(model.BaseModel):
7
+ """Aggregated usage statistics including per-model breakdown."""
10
8
 
11
- Returns:
12
- A tuple of (accumulated_usage, task_count)
9
+ total: model.Usage
10
+ by_model: list[model.TaskMetadata]
11
+ task_count: int
12
+
13
+
14
+ def accumulate_session_usage(session: Session) -> AggregatedUsage:
15
+ """Accumulate usage statistics from all TaskMetadataItems in session history.
16
+
17
+ Includes both main agent and sub-agent task metadata, grouped by model+provider.
13
18
  """
14
- total = model.Usage()
19
+ all_metadata: list[model.TaskMetadata] = []
15
20
  task_count = 0
16
21
 
17
22
  for item in session.conversation_history:
18
- if isinstance(item, model.ResponseMetadataItem) and item.usage:
23
+ if isinstance(item, model.TaskMetadataItem):
19
24
  task_count += 1
20
- usage = item.usage
21
- total.input_tokens += usage.input_tokens
22
- total.cached_tokens += usage.cached_tokens
23
- total.reasoning_tokens += usage.reasoning_tokens
24
- total.output_tokens += usage.output_tokens
25
- total.total_tokens += usage.total_tokens
26
-
27
- # Accumulate costs
28
- if usage.input_cost is not None:
29
- total.input_cost = (total.input_cost or 0.0) + usage.input_cost
30
- if usage.output_cost is not None:
31
- total.output_cost = (total.output_cost or 0.0) + usage.output_cost
32
- if usage.cache_read_cost is not None:
33
- total.cache_read_cost = (total.cache_read_cost or 0.0) + usage.cache_read_cost
34
- if usage.total_cost is not None:
35
- total.total_cost = (total.total_cost or 0.0) + usage.total_cost
36
-
37
- # Keep the latest context_usage_percent
38
- if usage.context_usage_percent is not None:
39
- total.context_usage_percent = usage.context_usage_percent
40
-
41
- return total, task_count
42
-
43
-
44
- def _format_tokens(tokens: int) -> str:
25
+ all_metadata.append(item.main_agent)
26
+ all_metadata.extend(item.sub_agent_task_metadata)
27
+
28
+ # Aggregate by model+provider
29
+ by_model = model.TaskMetadata.aggregate_by_model(all_metadata)
30
+
31
+ # Calculate total from aggregated results
32
+ total = model.Usage()
33
+ for meta in by_model:
34
+ if not meta.usage:
35
+ continue
36
+ usage = meta.usage
37
+
38
+ # Set currency from first
39
+ if total.currency == "USD" and usage.currency:
40
+ total.currency = usage.currency
41
+
42
+ # Accumulate primary token fields (total_tokens is computed)
43
+ total.input_tokens += usage.input_tokens
44
+ total.cached_tokens += usage.cached_tokens
45
+ total.reasoning_tokens += usage.reasoning_tokens
46
+ total.output_tokens += usage.output_tokens
47
+
48
+ # Accumulate cost components (total_cost is computed)
49
+ if usage.input_cost is not None:
50
+ total.input_cost = (total.input_cost or 0.0) + usage.input_cost
51
+ if usage.output_cost is not None:
52
+ total.output_cost = (total.output_cost or 0.0) + usage.output_cost
53
+ if usage.cache_read_cost is not None:
54
+ total.cache_read_cost = (total.cache_read_cost or 0.0) + usage.cache_read_cost
55
+
56
+ # Track peak context window size (max across all tasks)
57
+ if usage.context_size is not None:
58
+ total.context_size = usage.context_size
59
+
60
+ # Keep the latest context_limit for computed context_usage_percent
61
+ if usage.context_limit is not None:
62
+ total.context_limit = usage.context_limit
63
+
64
+ return AggregatedUsage(total=total, by_model=by_model, task_count=task_count)
65
+
66
+
67
+ def format_tokens(tokens: int) -> str:
45
68
  """Format token count with K/M suffix for readability."""
46
69
  if tokens >= 1_000_000:
47
70
  return f"{tokens / 1_000_000:.2f}M"
@@ -50,32 +73,54 @@ def _format_tokens(tokens: int) -> str:
50
73
  return str(tokens)
51
74
 
52
75
 
53
- def _format_cost(cost: float | None) -> str:
54
- """Format cost in USD."""
76
+ def format_cost(cost: float | None, currency: str = "USD") -> str:
77
+ """Format cost with currency symbol."""
55
78
  if cost is None:
56
79
  return "-"
80
+ symbol = "¥" if currency == "CNY" else "$"
57
81
  if cost < 0.01:
58
- return f"${cost:.4f}"
59
- return f"${cost:.2f}"
82
+ return f"{symbol}{cost:.4f}"
83
+ return f"{symbol}{cost:.2f}"
84
+
85
+
86
+ def _format_model_usage_line(meta: model.TaskMetadata) -> str:
87
+ """Format a single model's usage as a line."""
88
+ model_label = meta.model_name
89
+ if meta.provider:
90
+ model_label = f"{meta.model_name} ({meta.provider})"
91
+
92
+ usage = meta.usage
93
+ if not usage:
94
+ return f" {model_label}: no usage data"
95
+
96
+ cost_str = format_cost(usage.total_cost, usage.currency)
97
+ return (
98
+ f" {model_label}: "
99
+ f"{format_tokens(usage.input_tokens)} input, "
100
+ f"{format_tokens(usage.output_tokens)} output, "
101
+ f"{format_tokens(usage.cached_tokens)} cache read, "
102
+ f"{format_tokens(usage.reasoning_tokens)} thinking, "
103
+ f"({cost_str})"
104
+ )
60
105
 
61
106
 
62
- def format_status_content(usage: model.Usage) -> str:
63
- """Format session status as comma-separated text."""
64
- parts: list[str] = []
107
+ def format_status_content(aggregated: AggregatedUsage) -> str:
108
+ """Format session status with per-model breakdown."""
109
+ lines: list[str] = []
65
110
 
66
- parts.append(f"Input: {_format_tokens(usage.input_tokens)}")
67
- if usage.cached_tokens > 0:
68
- parts.append(f"Cached: {_format_tokens(usage.cached_tokens)}")
69
- parts.append(f"Output: {_format_tokens(usage.output_tokens)}")
70
- parts.append(f"Total: {_format_tokens(usage.total_tokens)}")
111
+ # Total cost line
112
+ total_cost_str = format_cost(aggregated.total.total_cost, aggregated.total.currency)
113
+ lines.append(f"Total cost: {total_cost_str}")
71
114
 
72
- if usage.total_cost is not None:
73
- parts.append(f"Cost: {_format_cost(usage.total_cost)}")
115
+ # Per-model breakdown
116
+ if aggregated.by_model:
117
+ lines.append("Usage by model:")
118
+ for stats in aggregated.by_model:
119
+ lines.append(_format_model_usage_line(stats))
74
120
 
75
- return ", ".join(parts)
121
+ return "\n".join(lines)
76
122
 
77
123
 
78
- @register_command
79
124
  class StatusCommand(CommandABC):
80
125
  """Display session usage statistics."""
81
126
 
@@ -87,25 +132,24 @@ class StatusCommand(CommandABC):
87
132
  def summary(self) -> str:
88
133
  return "Show session usage statistics"
89
134
 
90
- async def run(self, raw: str, agent: Agent) -> CommandResult:
135
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
136
+ del user_input # unused
91
137
  session = agent.session
92
- usage, task_count = accumulate_session_usage(session)
138
+ aggregated = accumulate_session_usage(session)
93
139
 
94
140
  event = events.DeveloperMessageEvent(
95
141
  session_id=session.id,
96
142
  item=model.DeveloperMessageItem(
97
- content=format_status_content(usage),
143
+ content=format_status_content(aggregated),
98
144
  command_output=model.CommandOutput(
99
145
  command_name=self.name,
100
- ui_extra=model.ToolResultUIExtra(
101
- type=model.ToolResultUIExtraType.SESSION_STATUS,
102
- session_status=model.SessionStatusUIExtra(
103
- usage=usage,
104
- task_count=task_count,
105
- ),
146
+ ui_extra=model.SessionStatusUIExtra(
147
+ usage=aggregated.total,
148
+ task_count=aggregated.task_count,
149
+ by_model=aggregated.by_model,
106
150
  ),
107
151
  ),
108
152
  ),
109
153
  )
110
154
 
111
- return CommandResult(events=[event])
155
+ return CommandResult(events=[event], persist_user_input=False, persist_events=False)
@@ -2,13 +2,10 @@ import os
2
2
  import subprocess
3
3
  from pathlib import Path
4
4
 
5
- from klaude_code.command.command_abc import CommandABC, CommandResult
6
- from klaude_code.command.registry import register_command
7
- from klaude_code.core.agent import Agent
5
+ from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
8
6
  from klaude_code.protocol import commands, events, model
9
7
 
10
8
 
11
- @register_command
12
9
  class TerminalSetupCommand(CommandABC):
13
10
  """Setup shift+enter newline functionality in terminal"""
14
11
 
@@ -24,7 +21,8 @@ class TerminalSetupCommand(CommandABC):
24
21
  def is_interactive(self) -> bool:
25
22
  return False
26
23
 
27
- async def run(self, raw: str, agent: Agent) -> CommandResult:
24
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
25
+ del user_input # unused
28
26
  term_program = os.environ.get("TERM_PROGRAM", "").lower()
29
27
 
30
28
  try:
@@ -42,7 +40,7 @@ class TerminalSetupCommand(CommandABC):
42
40
  return self._create_success_result(agent, message)
43
41
 
44
42
  except Exception as e:
45
- return self._create_error_result(agent, f"Error configuring terminal: {str(e)}")
43
+ return self._create_error_result(agent, f"Error configuring terminal: {e!s}")
46
44
 
47
45
  def _setup_ghostty(self) -> str:
48
46
  """Configure shift+enter newline for Ghostty terminal"""
@@ -112,7 +110,7 @@ class TerminalSetupCommand(CommandABC):
112
110
  )
113
111
 
114
112
  except Exception as e:
115
- raise Exception(f"Error configuring iTerm: {str(e)}")
113
+ raise Exception(f"Error configuring iTerm: {e!s}") from e
116
114
 
117
115
  def _setup_vscode_family(self) -> str:
118
116
  """Configure shift+enter newline for VS Code family terminals (VS Code, Windsurf, Cursor).
@@ -223,7 +221,7 @@ class TerminalSetupCommand(CommandABC):
223
221
 
224
222
  return message
225
223
 
226
- def _create_success_result(self, agent: Agent, message: str) -> CommandResult:
224
+ def _create_success_result(self, agent: "Agent", message: str) -> CommandResult:
227
225
  """Create success result"""
228
226
  return CommandResult(
229
227
  events=[
@@ -237,7 +235,7 @@ class TerminalSetupCommand(CommandABC):
237
235
  ]
238
236
  )
239
237
 
240
- def _create_error_result(self, agent: Agent, message: str) -> CommandResult:
238
+ def _create_error_result(self, agent: "Agent", message: str) -> CommandResult:
241
239
  """Create error result"""
242
240
  return CommandResult(
243
241
  events=[
@@ -0,0 +1,98 @@
1
+ import asyncio
2
+
3
+ from prompt_toolkit.styles import Style
4
+
5
+ from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
6
+ from klaude_code.config.thinking import get_thinking_picker_data, parse_thinking_value
7
+ from klaude_code.protocol import commands, events, llm_param, model, op
8
+ from klaude_code.ui.terminal.selector import SelectItem, select_one
9
+
10
+ SELECT_STYLE = Style(
11
+ [
12
+ ("instruction", "ansibrightblack"),
13
+ ("pointer", "ansigreen"),
14
+ ("highlighted", "ansigreen"),
15
+ ("text", "ansibrightblack"),
16
+ ("question", "bold"),
17
+ ]
18
+ )
19
+
20
+
21
+ def _select_thinking_sync(config: llm_param.LLMConfigParameter) -> llm_param.Thinking | None:
22
+ """Select thinking level (sync version)."""
23
+ data = get_thinking_picker_data(config)
24
+ if data is None:
25
+ return None
26
+
27
+ items: list[SelectItem[str]] = [
28
+ SelectItem(title=[("class:text", opt.label + "\n")], value=opt.value, search_text=opt.label)
29
+ for opt in data.options
30
+ ]
31
+
32
+ try:
33
+ result = select_one(
34
+ message=data.message,
35
+ items=items,
36
+ pointer="→",
37
+ style=SELECT_STYLE,
38
+ use_search_filter=False,
39
+ )
40
+ if result is None:
41
+ return None
42
+ return parse_thinking_value(result)
43
+ except KeyboardInterrupt:
44
+ return None
45
+
46
+
47
+ async def select_thinking_for_protocol(config: llm_param.LLMConfigParameter) -> llm_param.Thinking | None:
48
+ """Select thinking configuration based on the LLM protocol.
49
+
50
+ Returns the selected Thinking config, or None if user cancelled.
51
+ """
52
+ return await asyncio.to_thread(_select_thinking_sync, config)
53
+
54
+
55
+ class ThinkingCommand(CommandABC):
56
+ """Configure model thinking/reasoning level."""
57
+
58
+ @property
59
+ def name(self) -> commands.CommandName:
60
+ return commands.CommandName.THINKING
61
+
62
+ @property
63
+ def summary(self) -> str:
64
+ return "Configure model thinking/reasoning level"
65
+
66
+ @property
67
+ def is_interactive(self) -> bool:
68
+ return True
69
+
70
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
71
+ del user_input # unused
72
+ if agent.profile is None:
73
+ return CommandResult(events=[])
74
+
75
+ config = agent.profile.llm_client.get_llm_config()
76
+ new_thinking = await select_thinking_for_protocol(config)
77
+
78
+ if new_thinking is None:
79
+ return CommandResult(
80
+ events=[
81
+ events.DeveloperMessageEvent(
82
+ session_id=agent.session.id,
83
+ item=model.DeveloperMessageItem(
84
+ content="(no change)",
85
+ command_output=model.CommandOutput(command_name=self.name),
86
+ ),
87
+ )
88
+ ]
89
+ )
90
+
91
+ return CommandResult(
92
+ operations=[
93
+ op.ChangeThinkingOperation(
94
+ session_id=agent.session.id,
95
+ thinking=new_thinking,
96
+ )
97
+ ]
98
+ )
@@ -1,11 +1,19 @@
1
- from .config import Config, config_path, load_config
2
- from .list_model import display_models_and_providers
3
- from .select_model import select_model_from_config
1
+ from .config import (
2
+ Config,
3
+ UserConfig,
4
+ config_path,
5
+ create_example_config,
6
+ example_config_path,
7
+ load_config,
8
+ print_no_available_models_hint,
9
+ )
4
10
 
5
11
  __all__ = [
6
12
  "Config",
7
- "load_config",
13
+ "UserConfig",
8
14
  "config_path",
9
- "display_models_and_providers",
10
- "select_model_from_config",
15
+ "create_example_config",
16
+ "example_config_path",
17
+ "load_config",
18
+ "print_no_available_models_hint",
11
19
  ]
@@ -0,0 +1 @@
1
+ # Asset files for config module