klaude-code 1.8.0__py3-none-any.whl → 2.0.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.
- klaude_code/auth/base.py +97 -0
- klaude_code/auth/claude/__init__.py +6 -0
- klaude_code/auth/claude/exceptions.py +9 -0
- klaude_code/auth/claude/oauth.py +172 -0
- klaude_code/auth/claude/token_manager.py +26 -0
- klaude_code/auth/codex/token_manager.py +10 -50
- klaude_code/cli/auth_cmd.py +127 -46
- klaude_code/cli/config_cmd.py +4 -2
- klaude_code/cli/cost_cmd.py +14 -9
- klaude_code/cli/list_model.py +248 -200
- klaude_code/cli/main.py +1 -1
- klaude_code/cli/runtime.py +7 -5
- klaude_code/cli/self_update.py +1 -1
- klaude_code/cli/session_cmd.py +1 -1
- klaude_code/command/clear_cmd.py +6 -2
- klaude_code/command/command_abc.py +2 -2
- klaude_code/command/debug_cmd.py +4 -4
- klaude_code/command/export_cmd.py +2 -2
- klaude_code/command/export_online_cmd.py +12 -12
- klaude_code/command/fork_session_cmd.py +29 -23
- klaude_code/command/help_cmd.py +4 -4
- klaude_code/command/model_cmd.py +4 -4
- klaude_code/command/model_select.py +1 -1
- klaude_code/command/prompt-commit.md +82 -0
- klaude_code/command/prompt_command.py +3 -3
- klaude_code/command/refresh_cmd.py +2 -2
- klaude_code/command/registry.py +7 -5
- klaude_code/command/release_notes_cmd.py +4 -4
- klaude_code/command/resume_cmd.py +15 -11
- klaude_code/command/status_cmd.py +4 -4
- klaude_code/command/terminal_setup_cmd.py +8 -8
- klaude_code/command/thinking_cmd.py +4 -4
- klaude_code/config/assets/builtin_config.yaml +52 -3
- klaude_code/config/builtin_config.py +16 -5
- klaude_code/config/config.py +31 -7
- klaude_code/config/thinking.py +4 -4
- klaude_code/const.py +146 -91
- klaude_code/core/agent.py +3 -12
- klaude_code/core/executor.py +21 -13
- klaude_code/core/manager/sub_agent_manager.py +71 -7
- klaude_code/core/prompt.py +1 -1
- klaude_code/core/prompts/prompt-sub-agent-image-gen.md +1 -0
- klaude_code/core/prompts/prompt-sub-agent-web.md +27 -1
- klaude_code/core/reminders.py +88 -69
- klaude_code/core/task.py +44 -45
- klaude_code/core/tool/file/apply_patch_tool.py +9 -9
- klaude_code/core/tool/file/diff_builder.py +3 -5
- klaude_code/core/tool/file/edit_tool.py +23 -23
- klaude_code/core/tool/file/move_tool.py +43 -43
- klaude_code/core/tool/file/read_tool.py +44 -39
- klaude_code/core/tool/file/write_tool.py +14 -14
- klaude_code/core/tool/report_back_tool.py +4 -4
- klaude_code/core/tool/shell/bash_tool.py +23 -23
- klaude_code/core/tool/skill/skill_tool.py +7 -7
- klaude_code/core/tool/sub_agent_tool.py +38 -9
- klaude_code/core/tool/todo/todo_write_tool.py +8 -8
- klaude_code/core/tool/todo/update_plan_tool.py +6 -6
- klaude_code/core/tool/tool_abc.py +2 -2
- klaude_code/core/tool/tool_context.py +27 -0
- klaude_code/core/tool/tool_runner.py +88 -42
- klaude_code/core/tool/truncation.py +38 -20
- klaude_code/core/tool/web/mermaid_tool.py +6 -7
- klaude_code/core/tool/web/web_fetch_tool.py +68 -30
- klaude_code/core/tool/web/web_search_tool.py +15 -17
- klaude_code/core/turn.py +120 -73
- klaude_code/llm/anthropic/client.py +104 -44
- klaude_code/llm/anthropic/input.py +116 -108
- klaude_code/llm/bedrock/client.py +8 -5
- klaude_code/llm/claude/__init__.py +3 -0
- klaude_code/llm/claude/client.py +105 -0
- klaude_code/llm/client.py +4 -3
- klaude_code/llm/codex/client.py +16 -10
- klaude_code/llm/google/client.py +122 -60
- klaude_code/llm/google/input.py +94 -108
- klaude_code/llm/image.py +123 -0
- klaude_code/llm/input_common.py +136 -189
- klaude_code/llm/openai_compatible/client.py +17 -7
- klaude_code/llm/openai_compatible/input.py +36 -66
- klaude_code/llm/openai_compatible/stream.py +119 -67
- klaude_code/llm/openai_compatible/tool_call_accumulator.py +23 -11
- klaude_code/llm/openrouter/client.py +34 -9
- klaude_code/llm/openrouter/input.py +63 -64
- klaude_code/llm/openrouter/reasoning.py +22 -24
- klaude_code/llm/registry.py +20 -15
- klaude_code/llm/responses/client.py +107 -45
- klaude_code/llm/responses/input.py +115 -98
- klaude_code/llm/usage.py +52 -25
- klaude_code/protocol/__init__.py +1 -0
- klaude_code/protocol/events.py +16 -12
- klaude_code/protocol/llm_param.py +22 -3
- klaude_code/protocol/message.py +250 -0
- klaude_code/protocol/model.py +94 -281
- klaude_code/protocol/op.py +2 -2
- klaude_code/protocol/sub_agent/__init__.py +2 -2
- klaude_code/protocol/sub_agent/explore.py +10 -0
- klaude_code/protocol/sub_agent/image_gen.py +119 -0
- klaude_code/protocol/sub_agent/task.py +10 -0
- klaude_code/protocol/sub_agent/web.py +10 -0
- klaude_code/session/codec.py +6 -6
- klaude_code/session/export.py +261 -62
- klaude_code/session/selector.py +7 -24
- klaude_code/session/session.py +125 -53
- klaude_code/session/store.py +5 -32
- klaude_code/session/templates/export_session.html +1 -1
- klaude_code/session/templates/mermaid_viewer.html +1 -1
- klaude_code/trace/log.py +11 -6
- klaude_code/ui/core/input.py +1 -1
- klaude_code/ui/core/stage_manager.py +1 -8
- klaude_code/ui/modes/debug/display.py +2 -2
- klaude_code/ui/modes/repl/clipboard.py +2 -2
- klaude_code/ui/modes/repl/completers.py +18 -10
- klaude_code/ui/modes/repl/event_handler.py +136 -127
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +1 -1
- klaude_code/ui/modes/repl/key_bindings.py +1 -1
- klaude_code/ui/modes/repl/renderer.py +107 -15
- klaude_code/ui/renderers/assistant.py +2 -2
- klaude_code/ui/renderers/common.py +65 -7
- klaude_code/ui/renderers/developer.py +7 -6
- klaude_code/ui/renderers/diffs.py +11 -11
- klaude_code/ui/renderers/mermaid_viewer.py +49 -2
- klaude_code/ui/renderers/metadata.py +39 -31
- klaude_code/ui/renderers/sub_agent.py +57 -16
- klaude_code/ui/renderers/thinking.py +37 -2
- klaude_code/ui/renderers/tools.py +180 -165
- klaude_code/ui/rich/live.py +3 -1
- klaude_code/ui/rich/markdown.py +39 -7
- klaude_code/ui/rich/quote.py +76 -1
- klaude_code/ui/rich/status.py +14 -8
- klaude_code/ui/rich/theme.py +13 -6
- klaude_code/ui/terminal/image.py +34 -0
- klaude_code/ui/terminal/notifier.py +2 -1
- klaude_code/ui/terminal/progress_bar.py +4 -4
- klaude_code/ui/terminal/selector.py +22 -4
- klaude_code/ui/utils/common.py +55 -0
- {klaude_code-1.8.0.dist-info → klaude_code-2.0.0.dist-info}/METADATA +28 -6
- klaude_code-2.0.0.dist-info/RECORD +229 -0
- klaude_code/command/prompt-jj-describe.md +0 -32
- klaude_code/core/prompts/prompt-sub-agent-oracle.md +0 -22
- klaude_code/protocol/sub_agent/oracle.py +0 -91
- klaude_code-1.8.0.dist-info/RECORD +0 -219
- {klaude_code-1.8.0.dist-info → klaude_code-2.0.0.dist-info}/WHEEL +0 -0
- {klaude_code-1.8.0.dist-info → klaude_code-2.0.0.dist-info}/entry_points.txt +0 -0
klaude_code/cli/list_model.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
|
|
3
|
+
from rich.box import HORIZONTALS
|
|
3
4
|
from rich.console import Console, Group
|
|
4
|
-
from rich.panel import Panel
|
|
5
5
|
from rich.table import Table
|
|
6
6
|
from rich.text import Text
|
|
7
7
|
|
|
@@ -9,65 +9,127 @@ 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
|
|
12
13
|
from klaude_code.ui.rich.theme import ThemeKey, get_theme
|
|
14
|
+
from klaude_code.ui.utils.common import format_model_params
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
def
|
|
16
|
-
"""Get Codex OAuth login status as
|
|
17
|
+
def _get_codex_status_rows() -> list[tuple[Text, Text]]:
|
|
18
|
+
"""Get Codex OAuth login status as (label, value) tuples for table display."""
|
|
17
19
|
from klaude_code.auth.codex.token_manager import CodexTokenManager
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
rows: list[tuple[Text, Text]] = []
|
|
20
22
|
token_manager = CodexTokenManager()
|
|
21
23
|
state = token_manager.get_state()
|
|
22
24
|
|
|
23
25
|
if state is None:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
("Status
|
|
27
|
-
(
|
|
28
|
-
|
|
26
|
+
rows.append(
|
|
27
|
+
(
|
|
28
|
+
Text("Status", style=ThemeKey.CONFIG_PARAM_LABEL),
|
|
29
|
+
Text.assemble(
|
|
30
|
+
("Not logged in", ThemeKey.CONFIG_STATUS_ERROR),
|
|
31
|
+
(" (run 'klaude login codex' to authenticate)", "dim"),
|
|
32
|
+
),
|
|
29
33
|
)
|
|
30
34
|
)
|
|
31
35
|
elif state.is_expired():
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
("Status
|
|
35
|
-
(
|
|
36
|
-
|
|
36
|
+
rows.append(
|
|
37
|
+
(
|
|
38
|
+
Text("Status", style=ThemeKey.CONFIG_PARAM_LABEL),
|
|
39
|
+
Text.assemble(
|
|
40
|
+
("Token expired", ThemeKey.CONFIG_STATUS_ERROR),
|
|
41
|
+
(" (run 'klaude login codex' to re-authenticate)", "dim"),
|
|
42
|
+
),
|
|
37
43
|
)
|
|
38
44
|
)
|
|
39
45
|
else:
|
|
40
46
|
expires_dt = datetime.datetime.fromtimestamp(state.expires_at, tz=datetime.UTC)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
("Status
|
|
44
|
-
(
|
|
45
|
-
|
|
47
|
+
rows.append(
|
|
48
|
+
(
|
|
49
|
+
Text("Status", style=ThemeKey.CONFIG_PARAM_LABEL),
|
|
50
|
+
Text.assemble(
|
|
51
|
+
("Logged in", ThemeKey.CONFIG_STATUS_OK),
|
|
52
|
+
(
|
|
53
|
+
f" (account: {state.account_id[:8]}…, expires: {expires_dt.strftime('%Y-%m-%d %H:%M UTC')})",
|
|
54
|
+
"dim",
|
|
55
|
+
),
|
|
56
|
+
),
|
|
46
57
|
)
|
|
47
58
|
)
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
("
|
|
52
|
-
(
|
|
60
|
+
rows.append(
|
|
61
|
+
(
|
|
62
|
+
Text("Usage", style="dim"),
|
|
63
|
+
Text(
|
|
53
64
|
"https://chatgpt.com/codex/settings/usage",
|
|
54
|
-
"blue
|
|
65
|
+
style="blue link https://chatgpt.com/codex/settings/usage",
|
|
55
66
|
),
|
|
56
|
-
(" for up-to-date information on rate limits and credits", "dim"),
|
|
57
67
|
)
|
|
58
68
|
)
|
|
59
|
-
return
|
|
69
|
+
return rows
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_claude_status_rows() -> list[tuple[Text, Text]]:
|
|
73
|
+
"""Get Claude OAuth login status as (label, value) tuples for table display."""
|
|
74
|
+
from klaude_code.auth.claude.token_manager import ClaudeTokenManager
|
|
75
|
+
|
|
76
|
+
rows: list[tuple[Text, Text]] = []
|
|
77
|
+
token_manager = ClaudeTokenManager()
|
|
78
|
+
state = token_manager.get_state()
|
|
79
|
+
|
|
80
|
+
if state is None:
|
|
81
|
+
rows.append(
|
|
82
|
+
(
|
|
83
|
+
Text("Status", style=ThemeKey.CONFIG_PARAM_LABEL),
|
|
84
|
+
Text.assemble(
|
|
85
|
+
("Not logged in", ThemeKey.CONFIG_STATUS_ERROR),
|
|
86
|
+
(" (run 'klaude login claude' to authenticate)", "dim"),
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
elif state.is_expired():
|
|
91
|
+
rows.append(
|
|
92
|
+
(
|
|
93
|
+
Text("Status", style=ThemeKey.CONFIG_PARAM_LABEL),
|
|
94
|
+
Text.assemble(
|
|
95
|
+
("Token expired", ThemeKey.CONFIG_STATUS_ERROR),
|
|
96
|
+
(" (will refresh automatically on use; run 'klaude login claude' if refresh fails)", "dim"),
|
|
97
|
+
),
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
expires_dt = datetime.datetime.fromtimestamp(state.expires_at, tz=datetime.UTC)
|
|
102
|
+
rows.append(
|
|
103
|
+
(
|
|
104
|
+
Text("Status", style=ThemeKey.CONFIG_PARAM_LABEL),
|
|
105
|
+
Text.assemble(
|
|
106
|
+
("Logged in", ThemeKey.CONFIG_STATUS_OK),
|
|
107
|
+
(f" (expires: {expires_dt.strftime('%Y-%m-%d %H:%M UTC')})", "dim"),
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
rows.append(
|
|
113
|
+
(
|
|
114
|
+
Text("Usage", style="dim"),
|
|
115
|
+
Text(
|
|
116
|
+
"https://claude.ai/settings/usage",
|
|
117
|
+
style="blue link https://claude.ai/settings/usage",
|
|
118
|
+
),
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
return rows
|
|
60
122
|
|
|
61
123
|
|
|
62
124
|
def mask_api_key(api_key: str | None) -> str:
|
|
63
125
|
"""Mask API key to show only first 6 and last 6 characters with *** in between"""
|
|
64
|
-
if not api_key
|
|
65
|
-
return "
|
|
126
|
+
if not api_key:
|
|
127
|
+
return ""
|
|
66
128
|
|
|
67
129
|
if len(api_key) <= 12:
|
|
68
130
|
return api_key
|
|
69
131
|
|
|
70
|
-
return f"{api_key[:6]}
|
|
132
|
+
return f"{api_key[:6]}…{api_key[-6:]}"
|
|
71
133
|
|
|
72
134
|
|
|
73
135
|
def format_api_key_display(provider: ProviderConfig) -> Text:
|
|
@@ -91,7 +153,7 @@ def format_api_key_display(provider: ProviderConfig) -> Text:
|
|
|
91
153
|
# Plain API key
|
|
92
154
|
return Text(mask_api_key(provider.api_key))
|
|
93
155
|
else:
|
|
94
|
-
return Text("
|
|
156
|
+
return Text("")
|
|
95
157
|
|
|
96
158
|
|
|
97
159
|
def format_env_var_display(value: str | None) -> Text:
|
|
@@ -114,194 +176,180 @@ def format_env_var_display(value: str | None) -> Text:
|
|
|
114
176
|
# Plain value
|
|
115
177
|
return Text(mask_api_key(value))
|
|
116
178
|
else:
|
|
117
|
-
return Text("
|
|
179
|
+
return Text("")
|
|
118
180
|
|
|
119
181
|
|
|
120
182
|
def _get_model_params_display(model: ModelConfig) -> list[Text]:
|
|
121
183
|
"""Get display elements for model parameters."""
|
|
122
|
-
|
|
123
|
-
if
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
model.model_params.thinking.reasoning_summary,
|
|
138
|
-
)
|
|
139
|
-
)
|
|
140
|
-
if model.model_params.thinking.budget_tokens is not None:
|
|
141
|
-
params.append(
|
|
142
|
-
Text.assemble(
|
|
143
|
-
("thinking-budget-tokens", ThemeKey.CONFIG_PARAM_LABEL),
|
|
144
|
-
": ",
|
|
145
|
-
str(model.model_params.thinking.budget_tokens),
|
|
146
|
-
)
|
|
147
|
-
)
|
|
148
|
-
if model.model_params.provider_routing:
|
|
149
|
-
params.append(
|
|
150
|
-
Text.assemble(
|
|
151
|
-
("provider-routing", ThemeKey.CONFIG_PARAM_LABEL),
|
|
152
|
-
": ",
|
|
153
|
-
model.model_params.provider_routing.model_dump_json(exclude_none=True),
|
|
154
|
-
)
|
|
184
|
+
param_strings = format_model_params(model.model_params)
|
|
185
|
+
if param_strings:
|
|
186
|
+
return [Text(s) for s in param_strings]
|
|
187
|
+
return [Text("")]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _build_provider_info_panel(provider: ProviderConfig, available: bool) -> Quote:
|
|
191
|
+
"""Build a Quote containing provider name and information using a two-column grid."""
|
|
192
|
+
# Provider name as title
|
|
193
|
+
if available:
|
|
194
|
+
title = Text(provider.provider_name, style=ThemeKey.CONFIG_PROVIDER)
|
|
195
|
+
else:
|
|
196
|
+
title = Text.assemble(
|
|
197
|
+
(provider.provider_name, ThemeKey.CONFIG_PROVIDER),
|
|
198
|
+
(" (Unavailable)", ThemeKey.CONFIG_STATUS_ERROR),
|
|
155
199
|
)
|
|
156
|
-
if len(params) == 0:
|
|
157
|
-
params.append(Text("N/A", style=ThemeKey.CONFIG_PARAM_LABEL))
|
|
158
|
-
return params
|
|
159
200
|
|
|
201
|
+
# Build info table with two columns
|
|
202
|
+
info_table = Table.grid(padding=(0, 2))
|
|
203
|
+
info_table.add_column("Label", style=ThemeKey.CONFIG_PARAM_LABEL)
|
|
204
|
+
info_table.add_column("Value")
|
|
205
|
+
|
|
206
|
+
# Protocol
|
|
207
|
+
info_table.add_row(Text("Protocol"), Text(provider.protocol.value))
|
|
208
|
+
|
|
209
|
+
# Base URL (if set)
|
|
210
|
+
if provider.base_url:
|
|
211
|
+
info_table.add_row(Text("Base URL"), Text(provider.base_url))
|
|
212
|
+
|
|
213
|
+
# API key (if set)
|
|
214
|
+
if provider.api_key:
|
|
215
|
+
info_table.add_row(Text("API key"), format_api_key_display(provider))
|
|
216
|
+
|
|
217
|
+
# AWS Bedrock parameters
|
|
218
|
+
if provider.protocol == LLMClientProtocol.BEDROCK:
|
|
219
|
+
if provider.aws_access_key:
|
|
220
|
+
info_table.add_row(Text("AWS key"), format_env_var_display(provider.aws_access_key))
|
|
221
|
+
if provider.aws_secret_key:
|
|
222
|
+
info_table.add_row(Text("AWS secret"), format_env_var_display(provider.aws_secret_key))
|
|
223
|
+
if provider.aws_region:
|
|
224
|
+
info_table.add_row(Text("AWS region"), format_env_var_display(provider.aws_region))
|
|
225
|
+
if provider.aws_session_token:
|
|
226
|
+
info_table.add_row(Text("AWS token"), format_env_var_display(provider.aws_session_token))
|
|
227
|
+
if provider.aws_profile:
|
|
228
|
+
info_table.add_row(Text("AWS profile"), format_env_var_display(provider.aws_profile))
|
|
229
|
+
|
|
230
|
+
# OAuth status rows
|
|
231
|
+
if provider.protocol == LLMClientProtocol.CODEX_OAUTH:
|
|
232
|
+
for label, value in _get_codex_status_rows():
|
|
233
|
+
info_table.add_row(label, value)
|
|
234
|
+
if provider.protocol == LLMClientProtocol.CLAUDE_OAUTH:
|
|
235
|
+
for label, value in _get_claude_status_rows():
|
|
236
|
+
info_table.add_row(label, value)
|
|
237
|
+
|
|
238
|
+
return Quote(
|
|
239
|
+
Group(title, info_table),
|
|
240
|
+
style=ThemeKey.LINES,
|
|
241
|
+
prefix="┃ ",
|
|
242
|
+
)
|
|
160
243
|
|
|
161
|
-
def display_models_and_providers(config: Config):
|
|
162
|
-
"""Display models and providers configuration using rich formatting"""
|
|
163
|
-
themes = get_theme(config.theme)
|
|
164
|
-
console = Console(theme=themes.app_theme)
|
|
165
244
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
provider_info.add_column()
|
|
245
|
+
def _build_models_table(
|
|
246
|
+
provider: ProviderConfig, main_model: str | None, sub_agent_models: dict[str, str] | None = None
|
|
247
|
+
) -> Table:
|
|
248
|
+
"""Build a table for models under a provider."""
|
|
249
|
+
provider_available = not provider.is_api_key_missing()
|
|
172
250
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
Text(provider.base_url or "N/A"),
|
|
181
|
-
)
|
|
182
|
-
if provider.api_key:
|
|
183
|
-
provider_info.add_row(
|
|
184
|
-
Text("API Key:", style=ThemeKey.CONFIG_PARAM_LABEL),
|
|
185
|
-
format_api_key_display(provider),
|
|
186
|
-
)
|
|
251
|
+
# Build reverse mapping: model_name -> list of agent roles using it
|
|
252
|
+
model_to_agents: dict[str, list[str]] = {}
|
|
253
|
+
if sub_agent_models:
|
|
254
|
+
for agent_role, model_name in sub_agent_models.items():
|
|
255
|
+
if model_name not in model_to_agents:
|
|
256
|
+
model_to_agents[model_name] = []
|
|
257
|
+
model_to_agents[model_name].append(agent_role)
|
|
187
258
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
259
|
+
models_table = Table.grid(
|
|
260
|
+
padding=(0, 2),
|
|
261
|
+
)
|
|
262
|
+
models_table.add_column("Model Name", min_width=12)
|
|
263
|
+
models_table.add_column("Model ID", min_width=20, style=ThemeKey.CONFIG_MODEL_ID)
|
|
264
|
+
models_table.add_column("Params", style="dim")
|
|
265
|
+
|
|
266
|
+
model_count = len(provider.model_list)
|
|
267
|
+
for i, model in enumerate(provider.model_list):
|
|
268
|
+
is_last = i == model_count - 1
|
|
269
|
+
prefix = " └─ " if is_last else " ├─ "
|
|
270
|
+
|
|
271
|
+
if not provider_available:
|
|
272
|
+
name = Text.assemble((prefix, ThemeKey.LINES), (model.model_name, "dim"))
|
|
273
|
+
model_id = Text(model.model_params.model or "", style="dim")
|
|
274
|
+
params = Text("(unavailable)", style="dim")
|
|
275
|
+
else:
|
|
276
|
+
# Build role tags for this model
|
|
277
|
+
roles: list[str] = []
|
|
278
|
+
if model.model_name == main_model:
|
|
279
|
+
roles.append("default")
|
|
280
|
+
if model.model_name in model_to_agents:
|
|
281
|
+
roles.extend(role.lower() for role in model_to_agents[model.model_name])
|
|
282
|
+
|
|
283
|
+
if roles:
|
|
284
|
+
name = Text.assemble(
|
|
285
|
+
(prefix, ThemeKey.LINES),
|
|
286
|
+
(model.model_name, ThemeKey.CONFIG_STATUS_PRIMARY),
|
|
287
|
+
(f" ({', '.join(roles)})", "dim"),
|
|
214
288
|
)
|
|
289
|
+
else:
|
|
290
|
+
name = Text.assemble((prefix, ThemeKey.LINES), (model.model_name, ThemeKey.CONFIG_ITEM_NAME))
|
|
291
|
+
model_id = Text(model.model_params.model or "")
|
|
292
|
+
params = Text(" · ").join(_get_model_params_display(model))
|
|
215
293
|
|
|
216
|
-
|
|
217
|
-
provider_available = not provider.is_api_key_missing()
|
|
294
|
+
models_table.add_row(name, model_id, params)
|
|
218
295
|
|
|
219
|
-
|
|
220
|
-
models_table = Table.grid(padding=(0, 1), expand=True)
|
|
221
|
-
models_table.add_column(width=2, no_wrap=True) # Status
|
|
222
|
-
models_table.add_column(overflow="fold", ratio=2) # Name
|
|
223
|
-
models_table.add_column(overflow="fold", ratio=3) # Model
|
|
224
|
-
models_table.add_column(overflow="fold", ratio=4) # Params
|
|
225
|
-
|
|
226
|
-
# Add header
|
|
227
|
-
models_table.add_row(
|
|
228
|
-
Text("", style="bold"),
|
|
229
|
-
Text("Name", style=ThemeKey.CONFIG_TABLE_HEADER),
|
|
230
|
-
Text("Model", style=ThemeKey.CONFIG_TABLE_HEADER),
|
|
231
|
-
Text("Params", style=ThemeKey.CONFIG_TABLE_HEADER),
|
|
232
|
-
)
|
|
296
|
+
return models_table
|
|
233
297
|
|
|
234
|
-
# Add models for this provider
|
|
235
|
-
for model in provider.model_list:
|
|
236
|
-
if not provider_available:
|
|
237
|
-
# Provider API key not set - show as unavailable
|
|
238
|
-
status = Text("-", style="dim")
|
|
239
|
-
name = Text(model.model_name, style="dim")
|
|
240
|
-
model_id = Text(model.model_params.model or "N/A", style="dim")
|
|
241
|
-
params = [Text("(unavailable)", style="dim")]
|
|
242
|
-
elif model.model_name == config.main_model:
|
|
243
|
-
status = Text("★", style=ThemeKey.CONFIG_STATUS_PRIMARY)
|
|
244
|
-
name = Text(model.model_name, style=ThemeKey.CONFIG_STATUS_PRIMARY)
|
|
245
|
-
model_id = Text(model.model_params.model or "N/A", style="")
|
|
246
|
-
params = _get_model_params_display(model)
|
|
247
|
-
else:
|
|
248
|
-
status = Text("✔", style=ThemeKey.CONFIG_STATUS_OK)
|
|
249
|
-
name = Text(model.model_name, style=ThemeKey.CONFIG_ITEM_NAME)
|
|
250
|
-
model_id = Text(model.model_params.model or "N/A", style="")
|
|
251
|
-
params = _get_model_params_display(model)
|
|
252
|
-
|
|
253
|
-
models_table.add_row(status, name, model_id, Group(*params))
|
|
254
|
-
|
|
255
|
-
# Create panel content with provider info and models
|
|
256
|
-
panel_elements = [
|
|
257
|
-
provider_info,
|
|
258
|
-
Text(""), # Spacer
|
|
259
|
-
Text("Models:", style=ThemeKey.CONFIG_TABLE_HEADER),
|
|
260
|
-
models_table,
|
|
261
|
-
]
|
|
262
|
-
|
|
263
|
-
# Add Codex status if this is a codex provider
|
|
264
|
-
if provider.protocol == LLMClientProtocol.CODEX:
|
|
265
|
-
panel_elements.append(Text("")) # Spacer
|
|
266
|
-
panel_elements.extend(_get_codex_status_elements())
|
|
267
|
-
|
|
268
|
-
panel_content = Group(*panel_elements)
|
|
269
|
-
|
|
270
|
-
panel = Panel(
|
|
271
|
-
panel_content,
|
|
272
|
-
title=Text(f"Provider: {provider.provider_name}", style=ThemeKey.CONFIG_PROVIDER),
|
|
273
|
-
border_style=ThemeKey.CONFIG_PANEL_BORDER,
|
|
274
|
-
padding=(0, 1),
|
|
275
|
-
title_align="left",
|
|
276
|
-
)
|
|
277
298
|
|
|
278
|
-
|
|
279
|
-
|
|
299
|
+
def _display_agent_models_table(config: Config, console: Console) -> None:
|
|
300
|
+
"""Display model assignments as a table."""
|
|
301
|
+
console.print(Text(" Agent Models:", style=ThemeKey.CONFIG_TABLE_HEADER))
|
|
302
|
+
agent_table = Table(
|
|
303
|
+
box=HORIZONTALS,
|
|
304
|
+
show_header=True,
|
|
305
|
+
header_style=ThemeKey.CONFIG_TABLE_HEADER,
|
|
306
|
+
padding=(0, 2),
|
|
307
|
+
border_style=ThemeKey.LINES,
|
|
308
|
+
)
|
|
309
|
+
agent_table.add_column("Role", style="bold", min_width=10)
|
|
310
|
+
agent_table.add_column("Model", style=ThemeKey.CONFIG_STATUS_PRIMARY)
|
|
280
311
|
|
|
281
|
-
#
|
|
282
|
-
console.print()
|
|
312
|
+
# Default model
|
|
283
313
|
if config.main_model:
|
|
284
|
-
|
|
285
|
-
Text.assemble(
|
|
286
|
-
("Default Model: ", "bold"),
|
|
287
|
-
(config.main_model, ThemeKey.CONFIG_STATUS_PRIMARY),
|
|
288
|
-
)
|
|
289
|
-
)
|
|
314
|
+
agent_table.add_row("Default", config.main_model)
|
|
290
315
|
else:
|
|
291
|
-
|
|
292
|
-
Text.assemble(
|
|
293
|
-
("Default Model: ", "bold"),
|
|
294
|
-
("(not set)", ThemeKey.CONFIG_STATUS_ERROR),
|
|
295
|
-
)
|
|
296
|
-
)
|
|
316
|
+
agent_table.add_row("Default", Text("(not set)", style=ThemeKey.CONFIG_STATUS_ERROR))
|
|
297
317
|
|
|
318
|
+
# Sub-agent models
|
|
298
319
|
for profile in iter_sub_agent_profiles():
|
|
299
320
|
sub_model_name = config.sub_agent_models.get(profile.name)
|
|
300
|
-
if
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
321
|
+
if sub_model_name:
|
|
322
|
+
agent_table.add_row(profile.name, sub_model_name)
|
|
323
|
+
|
|
324
|
+
console.print(agent_table)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def display_models_and_providers(config: Config, *, show_all: bool = False):
|
|
328
|
+
"""Display models and providers configuration using rich formatting"""
|
|
329
|
+
themes = get_theme(config.theme)
|
|
330
|
+
console = Console(theme=themes.app_theme)
|
|
331
|
+
|
|
332
|
+
# Display model assignments as a table
|
|
333
|
+
_display_agent_models_table(config, console)
|
|
334
|
+
console.print()
|
|
335
|
+
|
|
336
|
+
# Sort providers: available (api_key set) first, unavailable (api_key not set) last
|
|
337
|
+
sorted_providers = sorted(config.provider_list, key=lambda p: (p.is_api_key_missing(), p.provider_name))
|
|
338
|
+
|
|
339
|
+
# Filter out unavailable providers unless show_all is True
|
|
340
|
+
if not show_all:
|
|
341
|
+
sorted_providers = [p for p in sorted_providers if not p.is_api_key_missing()]
|
|
342
|
+
|
|
343
|
+
# Display each provider with its models table
|
|
344
|
+
for provider in sorted_providers:
|
|
345
|
+
provider_available = not provider.is_api_key_missing()
|
|
346
|
+
|
|
347
|
+
# Provider info panel
|
|
348
|
+
provider_panel = _build_provider_info_panel(provider, provider_available)
|
|
349
|
+
console.print(provider_panel)
|
|
350
|
+
console.print()
|
|
351
|
+
|
|
352
|
+
# Models table for this provider
|
|
353
|
+
models_table = _build_models_table(provider, config.main_model, config.sub_agent_models)
|
|
354
|
+
console.print(models_table)
|
|
355
|
+
console.print("\n")
|
klaude_code/cli/main.py
CHANGED
|
@@ -286,7 +286,7 @@ def main_callback(
|
|
|
286
286
|
raise typer.Exit(2)
|
|
287
287
|
|
|
288
288
|
# In non-interactive environments, default to exec-mode behavior.
|
|
289
|
-
# This allows: echo "
|
|
289
|
+
# This allows: echo "…" | klaude
|
|
290
290
|
if not sys.stdin.isatty() or not sys.stdout.isatty():
|
|
291
291
|
if continue_ or resume or resume_by_id is not None:
|
|
292
292
|
log(("Error: --continue/--resume options require a TTY", "red"))
|
klaude_code/cli/runtime.py
CHANGED
|
@@ -17,8 +17,8 @@ from klaude_code.core.agent import Agent, DefaultModelProfileProvider, VanillaMo
|
|
|
17
17
|
from klaude_code.core.executor import Executor
|
|
18
18
|
from klaude_code.core.manager import build_llm_clients
|
|
19
19
|
from klaude_code.protocol import events, llm_param, op
|
|
20
|
-
from klaude_code.protocol import
|
|
21
|
-
from klaude_code.protocol.
|
|
20
|
+
from klaude_code.protocol import message as protocol_message
|
|
21
|
+
from klaude_code.protocol.message import UserInputPayload
|
|
22
22
|
from klaude_code.session.session import Session, close_default_store
|
|
23
23
|
from klaude_code.trace import DebugType, log, set_debug_logging
|
|
24
24
|
from klaude_code.ui.modes.repl import build_repl_status_snapshot
|
|
@@ -105,9 +105,11 @@ async def submit_user_input_payload(
|
|
|
105
105
|
if result.persist_user_input:
|
|
106
106
|
agent.session.append_history(
|
|
107
107
|
[
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
protocol_message.UserMessage(
|
|
109
|
+
parts=protocol_message.parts_from_text_and_images(
|
|
110
|
+
persisted_user_input.text,
|
|
111
|
+
persisted_user_input.images,
|
|
112
|
+
)
|
|
111
113
|
)
|
|
112
114
|
]
|
|
113
115
|
)
|
klaude_code/cli/self_update.py
CHANGED
|
@@ -228,7 +228,7 @@ def update_command(
|
|
|
228
228
|
log(f"To update, install uv and run `uv tool upgrade {PACKAGE_NAME}`.")
|
|
229
229
|
raise typer.Exit(1)
|
|
230
230
|
|
|
231
|
-
log(f"Running `uv tool upgrade {PACKAGE_NAME}
|
|
231
|
+
log(f"Running `uv tool upgrade {PACKAGE_NAME}`…")
|
|
232
232
|
result = subprocess.run(["uv", "tool", "upgrade", PACKAGE_NAME], check=False)
|
|
233
233
|
if result.returncode != 0:
|
|
234
234
|
log((f"Error: update failed (exit code {result.returncode}).", "red"))
|
klaude_code/cli/session_cmd.py
CHANGED
|
@@ -25,7 +25,7 @@ def _session_confirm(sessions: list[Session.SessionMetaBrief], message: str) ->
|
|
|
25
25
|
first_msg_text = s.user_messages[0] if s.user_messages else ""
|
|
26
26
|
first_msg = first_msg_text.strip().replace("\n", " ")[:50]
|
|
27
27
|
if len(first_msg_text) > 50:
|
|
28
|
-
first_msg += "
|
|
28
|
+
first_msg += "…"
|
|
29
29
|
log(f" {_fmt(s.updated_at)} {msg_count_display:>3} msgs {first_msg}")
|
|
30
30
|
|
|
31
31
|
items: list[SelectItem[bool]] = [
|
klaude_code/command/clear_cmd.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
2
|
-
from klaude_code.protocol import commands,
|
|
2
|
+
from klaude_code.protocol import commands, message, op
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class ClearCommand(CommandABC):
|
|
@@ -13,8 +13,12 @@ class ClearCommand(CommandABC):
|
|
|
13
13
|
def summary(self) -> str:
|
|
14
14
|
return "Clear conversation history and free up context"
|
|
15
15
|
|
|
16
|
-
async def run(self, agent: Agent, user_input:
|
|
16
|
+
async def run(self, agent: Agent, user_input: message.UserInputPayload) -> CommandResult:
|
|
17
17
|
del user_input # unused
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
os.system("cls" if os.name == "nt" else "clear")
|
|
21
|
+
|
|
18
22
|
return CommandResult(
|
|
19
23
|
operations=[op.ClearSessionOperation(session_id=agent.session.id)],
|
|
20
24
|
persist_user_input=False,
|
|
@@ -4,7 +4,7 @@ from typing import Protocol
|
|
|
4
4
|
from pydantic import BaseModel
|
|
5
5
|
|
|
6
6
|
from klaude_code.llm import LLMClientABC
|
|
7
|
-
from klaude_code.protocol import commands, llm_param,
|
|
7
|
+
from klaude_code.protocol import commands, llm_param, message, op
|
|
8
8
|
from klaude_code.protocol import events as protocol_events
|
|
9
9
|
from klaude_code.session.session import Session
|
|
10
10
|
|
|
@@ -78,7 +78,7 @@ class CommandABC(ABC):
|
|
|
78
78
|
return "additional instructions"
|
|
79
79
|
|
|
80
80
|
@abstractmethod
|
|
81
|
-
async def run(self, agent: Agent, user_input:
|
|
81
|
+
async def run(self, agent: Agent, user_input: message.UserInputPayload) -> CommandResult:
|
|
82
82
|
"""
|
|
83
83
|
Execute the command.
|
|
84
84
|
|
klaude_code/command/debug_cmd.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
2
|
-
from klaude_code.protocol import commands, events, model
|
|
2
|
+
from klaude_code.protocol import commands, events, message, model
|
|
3
3
|
from klaude_code.trace import DebugType, get_current_log_file, is_debug_enabled, set_debug_logging
|
|
4
4
|
|
|
5
5
|
|
|
@@ -45,7 +45,7 @@ class DebugCommand(CommandABC):
|
|
|
45
45
|
def placeholder(self) -> str:
|
|
46
46
|
return "filter types"
|
|
47
47
|
|
|
48
|
-
async def run(self, agent: Agent, user_input:
|
|
48
|
+
async def run(self, agent: Agent, user_input: message.UserInputPayload) -> CommandResult:
|
|
49
49
|
raw = user_input.text.strip()
|
|
50
50
|
|
|
51
51
|
# /debug (no args) - enable debug
|
|
@@ -70,8 +70,8 @@ class DebugCommand(CommandABC):
|
|
|
70
70
|
events=[
|
|
71
71
|
events.DeveloperMessageEvent(
|
|
72
72
|
session_id=agent.session.id,
|
|
73
|
-
item=
|
|
74
|
-
|
|
73
|
+
item=message.DeveloperMessage(
|
|
74
|
+
parts=message.text_parts_from_str(content),
|
|
75
75
|
command_output=model.CommandOutput(command_name=self.name),
|
|
76
76
|
),
|
|
77
77
|
)
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
6
|
-
from klaude_code.protocol import commands,
|
|
6
|
+
from klaude_code.protocol import commands, message, op
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class ExportCommand(CommandABC):
|
|
@@ -29,7 +29,7 @@ class ExportCommand(CommandABC):
|
|
|
29
29
|
def is_interactive(self) -> bool:
|
|
30
30
|
return False
|
|
31
31
|
|
|
32
|
-
async def run(self, agent: Agent, user_input:
|
|
32
|
+
async def run(self, agent: Agent, user_input: message.UserInputPayload) -> CommandResult:
|
|
33
33
|
output_path = self._normalize_output_path(user_input.text, agent)
|
|
34
34
|
return CommandResult(
|
|
35
35
|
operations=[
|