klaude-code 2.5.1__py3-none-any.whl → 2.5.2__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/.DS_Store +0 -0
- klaude_code/cli/list_model.py +8 -0
- klaude_code/cli/main.py +1 -1
- klaude_code/config/assets/builtin_config.yaml +0 -2
- klaude_code/config/config.py +30 -7
- klaude_code/config/model_matcher.py +2 -2
- klaude_code/config/sub_agent_model_helper.py +1 -1
- klaude_code/core/agent_profile.py +1 -0
- klaude_code/core/executor.py +4 -0
- klaude_code/core/loaded_skills.py +36 -0
- klaude_code/core/tool/context.py +1 -3
- klaude_code/core/turn.py +0 -6
- klaude_code/llm/anthropic/client.py +23 -11
- klaude_code/protocol/events/system.py +3 -0
- klaude_code/protocol/llm_param.py +1 -0
- klaude_code/session/export.py +259 -91
- klaude_code/session/templates/export_session.html +141 -59
- klaude_code/skill/.DS_Store +0 -0
- klaude_code/skill/assets/.DS_Store +0 -0
- klaude_code/skill/loader.py +1 -0
- klaude_code/tui/command/refresh_cmd.py +2 -0
- klaude_code/tui/components/metadata.py +6 -6
- klaude_code/tui/components/rich/markdown.py +8 -0
- klaude_code/tui/components/welcome.py +32 -0
- klaude_code/tui/renderer.py +0 -1
- {klaude_code-2.5.1.dist-info → klaude_code-2.5.2.dist-info}/METADATA +1 -1
- {klaude_code-2.5.1.dist-info → klaude_code-2.5.2.dist-info}/RECORD +29 -26
- klaude_code/skill/assets/jj-workspace/SKILL.md +0 -20
- {klaude_code-2.5.1.dist-info → klaude_code-2.5.2.dist-info}/WHEEL +0 -0
- {klaude_code-2.5.1.dist-info → klaude_code-2.5.2.dist-info}/entry_points.txt +0 -0
klaude_code/.DS_Store
ADDED
|
Binary file
|
klaude_code/cli/list_model.py
CHANGED
|
@@ -288,6 +288,14 @@ def _build_models_table(
|
|
|
288
288
|
name = Text.assemble((prefix, ThemeKey.LINES), (model.model_name, "dim"))
|
|
289
289
|
model_id = Text(model.model_id or "", style="dim")
|
|
290
290
|
params = Text("(unavailable)", style="dim")
|
|
291
|
+
elif model.disabled:
|
|
292
|
+
name = Text.assemble(
|
|
293
|
+
(prefix, ThemeKey.LINES),
|
|
294
|
+
(model.model_name, "dim strike"),
|
|
295
|
+
(" (disabled)", "dim"),
|
|
296
|
+
)
|
|
297
|
+
model_id = Text(model.model_id or "", style="dim")
|
|
298
|
+
params = Text(" · ").join(_get_model_params_display(model))
|
|
291
299
|
else:
|
|
292
300
|
# Build role tags for this model
|
|
293
301
|
roles: list[str] = []
|
klaude_code/cli/main.py
CHANGED
|
@@ -192,7 +192,7 @@ def main_callback(
|
|
|
192
192
|
if raw_model:
|
|
193
193
|
matches = [
|
|
194
194
|
m.selector
|
|
195
|
-
for m in cfg.iter_model_entries()
|
|
195
|
+
for m in cfg.iter_model_entries(only_available=True, include_disabled=False)
|
|
196
196
|
if (m.model_id or "").strip().lower() == raw_model.lower()
|
|
197
197
|
]
|
|
198
198
|
if len(matches) == 1:
|
|
@@ -96,7 +96,6 @@ provider_list:
|
|
|
96
96
|
- model_name: opus
|
|
97
97
|
model_id: anthropic/claude-4.5-opus
|
|
98
98
|
context_limit: 200000
|
|
99
|
-
verbosity: high
|
|
100
99
|
thinking:
|
|
101
100
|
type: enabled
|
|
102
101
|
budget_tokens: 2048
|
|
@@ -222,7 +221,6 @@ provider_list:
|
|
|
222
221
|
- model_name: opus
|
|
223
222
|
model_id: claude-opus-4-5-20251101
|
|
224
223
|
context_limit: 200000
|
|
225
|
-
verbosity: high
|
|
226
224
|
thinking:
|
|
227
225
|
type: enabled
|
|
228
226
|
budget_tokens: 2048
|
klaude_code/config/config.py
CHANGED
|
@@ -332,11 +332,12 @@ class Config(BaseModel):
|
|
|
332
332
|
|
|
333
333
|
raise ValueError(f"Unknown model: {model_name}")
|
|
334
334
|
|
|
335
|
-
def iter_model_entries(self, only_available: bool = False) -> list[ModelEntry]:
|
|
335
|
+
def iter_model_entries(self, only_available: bool = False, include_disabled: bool = True) -> list[ModelEntry]:
|
|
336
336
|
"""Return all model entries with their provider names.
|
|
337
337
|
|
|
338
338
|
Args:
|
|
339
339
|
only_available: If True, only return models from providers with valid API keys.
|
|
340
|
+
include_disabled: If False, exclude models with disabled=True.
|
|
340
341
|
"""
|
|
341
342
|
return [
|
|
342
343
|
ModelEntry(
|
|
@@ -347,25 +348,26 @@ class Config(BaseModel):
|
|
|
347
348
|
for provider in self.provider_list
|
|
348
349
|
if not only_available or not provider.is_api_key_missing()
|
|
349
350
|
for model in provider.model_list
|
|
351
|
+
if include_disabled or not model.disabled
|
|
350
352
|
]
|
|
351
353
|
|
|
352
354
|
def has_available_image_model(self) -> bool:
|
|
353
355
|
"""Check if any image generation model is available."""
|
|
354
|
-
for entry in self.iter_model_entries(only_available=True):
|
|
356
|
+
for entry in self.iter_model_entries(only_available=True, include_disabled=False):
|
|
355
357
|
if entry.modalities and "image" in entry.modalities:
|
|
356
358
|
return True
|
|
357
359
|
return False
|
|
358
360
|
|
|
359
361
|
def get_first_available_nano_banana_model(self) -> str | None:
|
|
360
362
|
"""Get the first available nano-banana model, or None."""
|
|
361
|
-
for entry in self.iter_model_entries(only_available=True):
|
|
363
|
+
for entry in self.iter_model_entries(only_available=True, include_disabled=False):
|
|
362
364
|
if "nano-banana" in entry.model_name:
|
|
363
365
|
return entry.model_name
|
|
364
366
|
return None
|
|
365
367
|
|
|
366
368
|
def get_first_available_image_model(self) -> str | None:
|
|
367
369
|
"""Get the first available image generation model, or None."""
|
|
368
|
-
for entry in self.iter_model_entries(only_available=True):
|
|
370
|
+
for entry in self.iter_model_entries(only_available=True, include_disabled=False):
|
|
369
371
|
if entry.modalities and "image" in entry.modalities:
|
|
370
372
|
return entry.model_name
|
|
371
373
|
return None
|
|
@@ -435,11 +437,26 @@ def _get_builtin_config() -> Config:
|
|
|
435
437
|
return Config(provider_list=providers, sub_agent_models=sub_agent_models)
|
|
436
438
|
|
|
437
439
|
|
|
440
|
+
def _merge_model(builtin: ModelConfig, user: ModelConfig) -> ModelConfig:
|
|
441
|
+
"""Merge user model config with builtin model config.
|
|
442
|
+
|
|
443
|
+
Strategy: user values take precedence if explicitly set (not default).
|
|
444
|
+
This allows users to override specific fields (e.g., disabled=true)
|
|
445
|
+
without losing other builtin settings (e.g., model_id, max_tokens).
|
|
446
|
+
"""
|
|
447
|
+
merged_data = builtin.model_dump()
|
|
448
|
+
user_data = user.model_dump(exclude_defaults=True, exclude={"model_name"})
|
|
449
|
+
for key, value in user_data.items():
|
|
450
|
+
if value is not None:
|
|
451
|
+
merged_data[key] = value
|
|
452
|
+
return ModelConfig.model_validate(merged_data)
|
|
453
|
+
|
|
454
|
+
|
|
438
455
|
def _merge_provider(builtin: ProviderConfig, user: UserProviderConfig) -> ProviderConfig:
|
|
439
456
|
"""Merge user provider config with builtin provider config.
|
|
440
457
|
|
|
441
458
|
Strategy:
|
|
442
|
-
- model_list: merge by model_name, user
|
|
459
|
+
- model_list: merge by model_name, user model fields override builtin fields
|
|
443
460
|
- Other fields (api_key, base_url, etc.): user config takes precedence if set
|
|
444
461
|
"""
|
|
445
462
|
# Merge model_list: builtin first, then user overrides/appends
|
|
@@ -447,7 +464,12 @@ def _merge_provider(builtin: ProviderConfig, user: UserProviderConfig) -> Provid
|
|
|
447
464
|
for m in builtin.model_list:
|
|
448
465
|
merged_models[m.model_name] = m
|
|
449
466
|
for m in user.model_list:
|
|
450
|
-
|
|
467
|
+
if m.model_name in merged_models:
|
|
468
|
+
# Merge with builtin model
|
|
469
|
+
merged_models[m.model_name] = _merge_model(merged_models[m.model_name], m)
|
|
470
|
+
else:
|
|
471
|
+
# New model from user
|
|
472
|
+
merged_models[m.model_name] = m
|
|
451
473
|
|
|
452
474
|
# For other fields, use user values if explicitly set, otherwise use builtin
|
|
453
475
|
# We check if user explicitly provided a value by comparing to defaults
|
|
@@ -578,7 +600,8 @@ def _load_config_cached() -> Config:
|
|
|
578
600
|
def load_config() -> Config:
|
|
579
601
|
"""Load config from disk (builtin + user merged).
|
|
580
602
|
|
|
581
|
-
Always returns a valid Config. Use
|
|
603
|
+
Always returns a valid Config. Use
|
|
604
|
+
``config.iter_model_entries(only_available=True, include_disabled=False)``
|
|
582
605
|
to check if any models are actually usable.
|
|
583
606
|
"""
|
|
584
607
|
try:
|
|
@@ -48,9 +48,9 @@ def match_model_from_config(preferred: str | None = None) -> ModelMatchResult:
|
|
|
48
48
|
"""
|
|
49
49
|
config = load_config()
|
|
50
50
|
|
|
51
|
-
# Only show models from providers with valid API keys
|
|
51
|
+
# Only show models from providers with valid API keys, exclude disabled models
|
|
52
52
|
models: list[ModelEntry] = sorted(
|
|
53
|
-
config.iter_model_entries(only_available=True),
|
|
53
|
+
config.iter_model_entries(only_available=True, include_disabled=False),
|
|
54
54
|
key=lambda m: (m.provider.lower(), m.model_name.lower()),
|
|
55
55
|
)
|
|
56
56
|
|
|
@@ -180,7 +180,7 @@ class SubAgentModelHelper:
|
|
|
180
180
|
- Returns all available models
|
|
181
181
|
"""
|
|
182
182
|
profile = get_sub_agent_profile(sub_agent_type)
|
|
183
|
-
all_models = self._config.iter_model_entries(only_available=True)
|
|
183
|
+
all_models = self._config.iter_model_entries(only_available=True, include_disabled=False)
|
|
184
184
|
|
|
185
185
|
if profile.availability_requirement == AVAILABILITY_IMAGE_MODEL:
|
|
186
186
|
return [m for m in all_models if m.modalities and "image" in m.modalities]
|
|
@@ -48,6 +48,7 @@ COMMAND_DESCRIPTIONS: dict[str, str] = {
|
|
|
48
48
|
"fd": "simple and fast alternative to find",
|
|
49
49
|
"tree": "directory listing as a tree",
|
|
50
50
|
"sg": "ast-grep - AST-aware code search",
|
|
51
|
+
"jq": "command-line JSON processor",
|
|
51
52
|
"jj": "jujutsu - Git-compatible version control system",
|
|
52
53
|
}
|
|
53
54
|
|
klaude_code/core/executor.py
CHANGED
|
@@ -18,6 +18,7 @@ from klaude_code.config import load_config
|
|
|
18
18
|
from klaude_code.config.sub_agent_model_helper import SubAgentModelHelper
|
|
19
19
|
from klaude_code.core.agent import Agent
|
|
20
20
|
from klaude_code.core.agent_profile import DefaultModelProfileProvider, ModelProfileProvider
|
|
21
|
+
from klaude_code.core.loaded_skills import get_loaded_skill_names_by_location
|
|
21
22
|
from klaude_code.core.manager import LLMClients, SubAgentManager
|
|
22
23
|
from klaude_code.llm.registry import create_llm_client
|
|
23
24
|
from klaude_code.log import DebugType, log_debug
|
|
@@ -136,6 +137,7 @@ class AgentRuntime:
|
|
|
136
137
|
session_id=session.id,
|
|
137
138
|
work_dir=str(session.work_dir),
|
|
138
139
|
llm_config=self._llm_clients.main.get_llm_config(),
|
|
140
|
+
loaded_skills=get_loaded_skill_names_by_location(),
|
|
139
141
|
)
|
|
140
142
|
)
|
|
141
143
|
|
|
@@ -192,6 +194,7 @@ class AgentRuntime:
|
|
|
192
194
|
session_id=agent.session.id,
|
|
193
195
|
work_dir=str(agent.session.work_dir),
|
|
194
196
|
llm_config=self._llm_clients.main.get_llm_config(),
|
|
197
|
+
loaded_skills=get_loaded_skill_names_by_location(),
|
|
195
198
|
)
|
|
196
199
|
)
|
|
197
200
|
|
|
@@ -215,6 +218,7 @@ class AgentRuntime:
|
|
|
215
218
|
session_id=target_session.id,
|
|
216
219
|
work_dir=str(target_session.work_dir),
|
|
217
220
|
llm_config=self._llm_clients.main.get_llm_config(),
|
|
221
|
+
loaded_skills=get_loaded_skill_names_by_location(),
|
|
218
222
|
)
|
|
219
223
|
)
|
|
220
224
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_loaded_skill_names_by_location() -> dict[str, list[str]]:
|
|
5
|
+
"""Return loaded skill names grouped by location.
|
|
6
|
+
|
|
7
|
+
The UI should not import the skill system directly. Core can expose a
|
|
8
|
+
lightweight summary suitable for WelcomeEvent rendering.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
# Import lazily to keep startup overhead minimal and avoid unnecessary
|
|
13
|
+
# coupling at module import time.
|
|
14
|
+
from klaude_code.skill.manager import get_available_skills
|
|
15
|
+
except Exception:
|
|
16
|
+
return {}
|
|
17
|
+
|
|
18
|
+
result: dict[str, list[str]] = {"user": [], "project": [], "system": []}
|
|
19
|
+
try:
|
|
20
|
+
for name, _desc, location in get_available_skills():
|
|
21
|
+
if location == "user":
|
|
22
|
+
result["user"].append(name)
|
|
23
|
+
elif location == "project":
|
|
24
|
+
result["project"].append(name)
|
|
25
|
+
elif location == "system":
|
|
26
|
+
result["system"].append(name)
|
|
27
|
+
except Exception:
|
|
28
|
+
return {}
|
|
29
|
+
|
|
30
|
+
if not result["user"] and not result["project"] and not result["system"]:
|
|
31
|
+
return {}
|
|
32
|
+
|
|
33
|
+
result["user"].sort()
|
|
34
|
+
result["project"].sort()
|
|
35
|
+
result["system"].sort()
|
|
36
|
+
return result
|
klaude_code/core/tool/context.py
CHANGED
|
@@ -89,7 +89,5 @@ class ToolContext:
|
|
|
89
89
|
def with_record_sub_agent_session_id(self, callback: Callable[[str], None] | None) -> ToolContext:
|
|
90
90
|
return replace(self, record_sub_agent_session_id=callback)
|
|
91
91
|
|
|
92
|
-
def with_register_sub_agent_metadata_getter(
|
|
93
|
-
self, callback: Callable[[GetMetadataFn], None] | None
|
|
94
|
-
) -> ToolContext:
|
|
92
|
+
def with_register_sub_agent_metadata_getter(self, callback: Callable[[GetMetadataFn], None] | None) -> ToolContext:
|
|
95
93
|
return replace(self, register_sub_agent_metadata_getter=callback)
|
klaude_code/core/turn.py
CHANGED
|
@@ -339,12 +339,6 @@ class TurnExecutor:
|
|
|
339
339
|
)
|
|
340
340
|
case message.StreamErrorItem() as msg:
|
|
341
341
|
turn_result.stream_error = msg
|
|
342
|
-
log_debug(
|
|
343
|
-
"[StreamError]",
|
|
344
|
-
msg.error,
|
|
345
|
-
style="red",
|
|
346
|
-
debug_type=DebugType.RESPONSE,
|
|
347
|
-
)
|
|
348
342
|
case message.ToolCallStartDelta() as msg:
|
|
349
343
|
if thinking_active:
|
|
350
344
|
thinking_active = False
|
|
@@ -282,8 +282,6 @@ async def parse_anthropic_stream(
|
|
|
282
282
|
max_tokens=param.max_tokens,
|
|
283
283
|
)
|
|
284
284
|
)
|
|
285
|
-
metadata_tracker.set_model_name(str(param.model_id))
|
|
286
|
-
metadata_tracker.set_response_id(state.response_id)
|
|
287
285
|
raw_stop_reason = getattr(event, "stop_reason", None)
|
|
288
286
|
if isinstance(raw_stop_reason, str):
|
|
289
287
|
state.stop_reason = _map_anthropic_stop_reason(raw_stop_reason)
|
|
@@ -293,6 +291,8 @@ async def parse_anthropic_stream(
|
|
|
293
291
|
parts = state.flush_all()
|
|
294
292
|
if parts:
|
|
295
293
|
metadata_tracker.record_token()
|
|
294
|
+
metadata_tracker.set_model_name(str(param.model_id))
|
|
295
|
+
metadata_tracker.set_response_id(state.response_id)
|
|
296
296
|
metadata = metadata_tracker.finalize()
|
|
297
297
|
yield message.AssistantMessage(
|
|
298
298
|
parts=parts,
|
|
@@ -322,15 +322,27 @@ class AnthropicLLMStream(LLMStreamABC):
|
|
|
322
322
|
return self._iterate()
|
|
323
323
|
|
|
324
324
|
async def _iterate(self) -> AsyncGenerator[message.LLMStreamItem]:
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
325
|
+
try:
|
|
326
|
+
async for item in parse_anthropic_stream(
|
|
327
|
+
self._stream,
|
|
328
|
+
self._param,
|
|
329
|
+
self._metadata_tracker,
|
|
330
|
+
self._state,
|
|
331
|
+
):
|
|
332
|
+
if isinstance(item, message.AssistantMessage):
|
|
333
|
+
self._completed = True
|
|
334
|
+
yield item
|
|
335
|
+
except (anthropic.AnthropicError, httpx.HTTPError) as e:
|
|
336
|
+
yield message.StreamErrorItem(error=f"{e.__class__.__name__} {e!s}")
|
|
337
|
+
self._metadata_tracker.set_model_name(str(self._param.model_id))
|
|
338
|
+
self._metadata_tracker.set_response_id(self._state.response_id)
|
|
339
|
+
metadata = self._metadata_tracker.finalize()
|
|
340
|
+
yield message.AssistantMessage(
|
|
341
|
+
parts=[],
|
|
342
|
+
response_id=self._state.response_id,
|
|
343
|
+
usage=metadata,
|
|
344
|
+
stop_reason="error",
|
|
345
|
+
)
|
|
334
346
|
|
|
335
347
|
def get_partial_message(self) -> message.AssistantMessage | None:
|
|
336
348
|
if self._completed:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
3
5
|
from klaude_code.protocol import llm_param
|
|
4
6
|
from klaude_code.protocol.events.chat import DeveloperMessageEvent, UserMessageEvent
|
|
5
7
|
from klaude_code.protocol.events.lifecycle import TaskFinishEvent, TaskStartEvent, TurnStartEvent
|
|
@@ -14,6 +16,7 @@ class WelcomeEvent(Event):
|
|
|
14
16
|
work_dir: str
|
|
15
17
|
llm_config: llm_param.LLMConfigParameter
|
|
16
18
|
show_klaude_code_info: bool = True
|
|
19
|
+
loaded_skills: dict[str, list[str]] = Field(default_factory=dict)
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
class ErrorEvent(Event):
|
|
@@ -120,6 +120,7 @@ class LLMConfigProviderParameter(BaseModel):
|
|
|
120
120
|
|
|
121
121
|
class LLMConfigModelParameter(BaseModel):
|
|
122
122
|
model_id: str | None = None
|
|
123
|
+
disabled: bool = False
|
|
123
124
|
temperature: float | None = None
|
|
124
125
|
max_tokens: int | None = None
|
|
125
126
|
context_limit: int | None = None
|