klaude-code 1.2.16__py3-none-any.whl → 1.2.18__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/cli/config_cmd.py +1 -1
- klaude_code/cli/debug.py +1 -1
- klaude_code/cli/main.py +3 -9
- klaude_code/cli/runtime.py +20 -13
- klaude_code/command/__init__.py +7 -1
- klaude_code/command/clear_cmd.py +2 -7
- klaude_code/command/command_abc.py +33 -5
- klaude_code/command/debug_cmd.py +79 -0
- klaude_code/command/diff_cmd.py +2 -6
- klaude_code/command/export_cmd.py +7 -7
- klaude_code/command/export_online_cmd.py +145 -0
- klaude_code/command/help_cmd.py +4 -9
- klaude_code/command/model_cmd.py +10 -6
- klaude_code/command/prompt_command.py +2 -6
- klaude_code/command/refresh_cmd.py +2 -7
- klaude_code/command/registry.py +2 -4
- klaude_code/command/release_notes_cmd.py +2 -6
- klaude_code/command/status_cmd.py +2 -7
- klaude_code/command/terminal_setup_cmd.py +2 -6
- klaude_code/command/thinking_cmd.py +13 -8
- klaude_code/config/config.py +16 -17
- klaude_code/config/select_model.py +81 -5
- klaude_code/const/__init__.py +1 -1
- klaude_code/core/executor.py +236 -109
- klaude_code/core/manager/__init__.py +2 -4
- klaude_code/core/manager/sub_agent_manager.py +1 -1
- klaude_code/core/prompts/prompt-claude-code.md +1 -1
- klaude_code/core/prompts/prompt-sub-agent-oracle.md +0 -1
- klaude_code/core/prompts/prompt-sub-agent-web.md +51 -0
- klaude_code/core/reminders.py +9 -35
- klaude_code/core/task.py +8 -0
- klaude_code/core/tool/__init__.py +2 -0
- klaude_code/core/tool/file/read_tool.py +38 -10
- klaude_code/core/tool/report_back_tool.py +28 -2
- klaude_code/core/tool/shell/bash_tool.py +22 -2
- klaude_code/core/tool/tool_runner.py +26 -23
- klaude_code/core/tool/truncation.py +23 -9
- klaude_code/core/tool/web/web_fetch_tool.md +1 -1
- klaude_code/core/tool/web/web_fetch_tool.py +36 -1
- klaude_code/core/tool/web/web_search_tool.md +23 -0
- klaude_code/core/tool/web/web_search_tool.py +126 -0
- klaude_code/core/turn.py +28 -0
- klaude_code/protocol/commands.py +2 -0
- klaude_code/protocol/events.py +8 -0
- klaude_code/protocol/sub_agent/__init__.py +1 -1
- klaude_code/protocol/sub_agent/explore.py +1 -1
- klaude_code/protocol/sub_agent/web.py +79 -0
- klaude_code/protocol/tools.py +1 -0
- klaude_code/session/session.py +2 -2
- klaude_code/session/templates/export_session.html +123 -37
- klaude_code/trace/__init__.py +20 -2
- klaude_code/ui/modes/repl/completers.py +19 -2
- klaude_code/ui/modes/repl/event_handler.py +44 -15
- klaude_code/ui/modes/repl/renderer.py +3 -3
- klaude_code/ui/renderers/metadata.py +2 -4
- klaude_code/ui/renderers/sub_agent.py +14 -10
- klaude_code/ui/renderers/thinking.py +24 -8
- klaude_code/ui/renderers/tools.py +83 -20
- klaude_code/ui/rich/code_panel.py +112 -0
- klaude_code/ui/rich/markdown.py +3 -4
- klaude_code/ui/rich/status.py +30 -6
- klaude_code/ui/rich/theme.py +10 -1
- {klaude_code-1.2.16.dist-info → klaude_code-1.2.18.dist-info}/METADATA +126 -25
- {klaude_code-1.2.16.dist-info → klaude_code-1.2.18.dist-info}/RECORD +67 -63
- klaude_code/core/manager/agent_manager.py +0 -132
- klaude_code/core/prompts/prompt-sub-agent-webfetch.md +0 -46
- klaude_code/protocol/sub_agent/web_fetch.py +0 -74
- /klaude_code/{config → cli}/list_model.py +0 -0
- {klaude_code-1.2.16.dist-info → klaude_code-1.2.18.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.16.dist-info → klaude_code-1.2.18.dist-info}/entry_points.txt +0 -0
klaude_code/command/registry.py
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
from importlib.resources import files
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
|
-
from klaude_code.command.command_abc import CommandResult, InputAction
|
|
4
|
+
from klaude_code.command.command_abc import Agent, CommandResult, InputAction
|
|
5
5
|
from klaude_code.command.prompt_command import PromptCommand
|
|
6
6
|
from klaude_code.protocol import commands, events, model
|
|
7
7
|
from klaude_code.trace import log_debug
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
|
-
from klaude_code.core.agent import Agent
|
|
11
|
-
|
|
12
10
|
from .command_abc import CommandABC
|
|
13
11
|
|
|
14
12
|
_COMMANDS: dict[commands.CommandName | str, "CommandABC"] = {}
|
|
@@ -50,7 +48,7 @@ def is_slash_command_name(name: str) -> bool:
|
|
|
50
48
|
return name in _COMMANDS
|
|
51
49
|
|
|
52
50
|
|
|
53
|
-
async def dispatch_command(raw: str, agent:
|
|
51
|
+
async def dispatch_command(raw: str, agent: Agent) -> CommandResult:
|
|
54
52
|
_ensure_commands_loaded()
|
|
55
53
|
# Detect command name
|
|
56
54
|
if not raw.startswith("/"):
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import TYPE_CHECKING
|
|
3
2
|
|
|
4
|
-
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
3
|
+
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
5
4
|
from klaude_code.protocol import commands, events, model
|
|
6
5
|
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from klaude_code.core.agent import Agent
|
|
9
|
-
|
|
10
6
|
|
|
11
7
|
def _read_changelog() -> str:
|
|
12
8
|
"""Read CHANGELOG.md from project root."""
|
|
@@ -72,7 +68,7 @@ class ReleaseNotesCommand(CommandABC):
|
|
|
72
68
|
def summary(self) -> str:
|
|
73
69
|
return "Show the latest release notes"
|
|
74
70
|
|
|
75
|
-
async def run(self, raw: str, agent:
|
|
71
|
+
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
76
72
|
changelog = _read_changelog()
|
|
77
73
|
content = _extract_releases(changelog, count=10)
|
|
78
74
|
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
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
|
-
if TYPE_CHECKING:
|
|
8
|
-
from klaude_code.core.agent import Agent
|
|
9
|
-
|
|
10
5
|
|
|
11
6
|
class AggregatedUsage(model.BaseModel):
|
|
12
7
|
"""Aggregated usage statistics including per-model breakdown."""
|
|
@@ -137,7 +132,7 @@ class StatusCommand(CommandABC):
|
|
|
137
132
|
def summary(self) -> str:
|
|
138
133
|
return "Show session usage statistics"
|
|
139
134
|
|
|
140
|
-
async def run(self, raw: str, agent:
|
|
135
|
+
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
141
136
|
session = agent.session
|
|
142
137
|
aggregated = accumulate_session_usage(session)
|
|
143
138
|
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import subprocess
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
5
4
|
|
|
6
|
-
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
5
|
+
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
7
6
|
from klaude_code.protocol import commands, events, model
|
|
8
7
|
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from klaude_code.core.agent import Agent
|
|
11
|
-
|
|
12
8
|
|
|
13
9
|
class TerminalSetupCommand(CommandABC):
|
|
14
10
|
"""Setup shift+enter newline functionality in terminal"""
|
|
@@ -25,7 +21,7 @@ class TerminalSetupCommand(CommandABC):
|
|
|
25
21
|
def is_interactive(self) -> bool:
|
|
26
22
|
return False
|
|
27
23
|
|
|
28
|
-
async def run(self, raw: str, agent:
|
|
24
|
+
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
29
25
|
term_program = os.environ.get("TERM_PROGRAM", "").lower()
|
|
30
26
|
|
|
31
27
|
try:
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from typing import TYPE_CHECKING
|
|
3
2
|
|
|
4
3
|
import questionary
|
|
5
4
|
|
|
6
|
-
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
5
|
+
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
7
6
|
from klaude_code.protocol import commands, events, llm_param, model
|
|
8
7
|
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from klaude_code.core.agent import Agent
|
|
11
|
-
|
|
12
|
-
|
|
13
8
|
# Thinking level options for different protocols
|
|
14
9
|
RESPONSES_LEVELS = ["minimal", "low", "medium", "high"]
|
|
15
10
|
RESPONSES_GPT51_LEVELS = ["none", "minimal", "low", "medium", "high"]
|
|
11
|
+
RESPONSES_GPT52_LEVELS = ["none", "minimal", "low", "medium", "high", "xhigh"]
|
|
16
12
|
RESPONSES_CODEX_MAX_LEVELS = ["medium", "high", "xhigh"]
|
|
17
13
|
|
|
18
14
|
ANTHROPIC_LEVELS: list[tuple[str, int | None]] = [
|
|
@@ -35,7 +31,14 @@ def _is_gpt51_model(model_name: str | None) -> bool:
|
|
|
35
31
|
"""Check if the model is GPT-5.1."""
|
|
36
32
|
if not model_name:
|
|
37
33
|
return False
|
|
38
|
-
return model_name.lower() in ["
|
|
34
|
+
return model_name.lower() in ["gpt-5.1", "openai/gpt-5.1", "gpt-5.1-codex-2025-11-13"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _is_gpt52_model(model_name: str | None) -> bool:
|
|
38
|
+
"""Check if the model is GPT-5.2."""
|
|
39
|
+
if not model_name:
|
|
40
|
+
return False
|
|
41
|
+
return model_name.lower() in ["gpt-5.2", "openai/gpt-5.2"]
|
|
39
42
|
|
|
40
43
|
|
|
41
44
|
def _is_codex_max_model(model_name: str | None) -> bool:
|
|
@@ -49,6 +52,8 @@ def _get_levels_for_responses(model_name: str | None) -> list[str]:
|
|
|
49
52
|
"""Get thinking levels for responses protocol."""
|
|
50
53
|
if _is_codex_max_model(model_name):
|
|
51
54
|
return RESPONSES_CODEX_MAX_LEVELS
|
|
55
|
+
if _is_gpt52_model(model_name):
|
|
56
|
+
return RESPONSES_GPT52_LEVELS
|
|
52
57
|
if _is_gpt51_model(model_name):
|
|
53
58
|
return RESPONSES_GPT51_LEVELS
|
|
54
59
|
return RESPONSES_LEVELS
|
|
@@ -164,7 +169,7 @@ class ThinkingCommand(CommandABC):
|
|
|
164
169
|
def is_interactive(self) -> bool:
|
|
165
170
|
return True
|
|
166
171
|
|
|
167
|
-
async def run(self, raw: str, agent:
|
|
172
|
+
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
168
173
|
if not agent.profile:
|
|
169
174
|
return self._no_change_result(agent, "No profile configured")
|
|
170
175
|
|
klaude_code/config/config.py
CHANGED
|
@@ -79,8 +79,8 @@ class Config(BaseModel):
|
|
|
79
79
|
|
|
80
80
|
def get_example_config() -> Config:
|
|
81
81
|
return Config(
|
|
82
|
-
main_model="
|
|
83
|
-
sub_agent_models={"explore": "haiku", "oracle": "gpt-5.1
|
|
82
|
+
main_model="opus",
|
|
83
|
+
sub_agent_models={"explore": "haiku", "oracle": "gpt-5.1", "webagent": "haiku", "task": "opus"},
|
|
84
84
|
provider_list=[
|
|
85
85
|
llm_param.LLMConfigProviderParameter(
|
|
86
86
|
provider_name="openai",
|
|
@@ -93,6 +93,11 @@ def get_example_config() -> Config:
|
|
|
93
93
|
protocol=llm_param.LLMClientProtocol.OPENROUTER,
|
|
94
94
|
api_key="your-openrouter-api-key",
|
|
95
95
|
),
|
|
96
|
+
llm_param.LLMConfigProviderParameter(
|
|
97
|
+
provider_name="anthropic",
|
|
98
|
+
protocol=llm_param.LLMClientProtocol.ANTHROPIC,
|
|
99
|
+
api_key="your-anthropic-api-key",
|
|
100
|
+
),
|
|
96
101
|
],
|
|
97
102
|
model_list=[
|
|
98
103
|
ModelConfig(
|
|
@@ -100,31 +105,25 @@ def get_example_config() -> Config:
|
|
|
100
105
|
provider="openai",
|
|
101
106
|
model_params=llm_param.LLMConfigModelParameter(
|
|
102
107
|
model="gpt-5.1-2025-11-13",
|
|
103
|
-
max_tokens=32000,
|
|
104
108
|
verbosity="medium",
|
|
105
109
|
thinking=llm_param.Thinking(
|
|
106
|
-
reasoning_effort="
|
|
110
|
+
reasoning_effort="high",
|
|
107
111
|
reasoning_summary="auto",
|
|
108
|
-
type="enabled",
|
|
109
|
-
budget_tokens=None,
|
|
110
112
|
),
|
|
111
|
-
context_limit=
|
|
113
|
+
context_limit=400000,
|
|
112
114
|
),
|
|
113
115
|
),
|
|
114
116
|
ModelConfig(
|
|
115
|
-
model_name="
|
|
116
|
-
provider="
|
|
117
|
+
model_name="opus",
|
|
118
|
+
provider="anthropic",
|
|
117
119
|
model_params=llm_param.LLMConfigModelParameter(
|
|
118
|
-
model="
|
|
119
|
-
|
|
120
|
-
verbosity="medium",
|
|
120
|
+
model="claude-opus-4-5-20251101",
|
|
121
|
+
verbosity="high",
|
|
121
122
|
thinking=llm_param.Thinking(
|
|
122
|
-
reasoning_effort="high",
|
|
123
|
-
reasoning_summary="auto",
|
|
124
123
|
type="enabled",
|
|
125
|
-
budget_tokens=
|
|
124
|
+
budget_tokens=31999,
|
|
126
125
|
),
|
|
127
|
-
context_limit=
|
|
126
|
+
context_limit=200000,
|
|
128
127
|
),
|
|
129
128
|
),
|
|
130
129
|
ModelConfig(
|
|
@@ -136,7 +135,7 @@ def get_example_config() -> Config:
|
|
|
136
135
|
provider_routing=llm_param.OpenRouterProviderRouting(
|
|
137
136
|
sort="throughput",
|
|
138
137
|
),
|
|
139
|
-
context_limit=
|
|
138
|
+
context_limit=200000,
|
|
140
139
|
),
|
|
141
140
|
),
|
|
142
141
|
],
|
|
@@ -1,35 +1,111 @@
|
|
|
1
|
-
from klaude_code.config.config import load_config
|
|
1
|
+
from klaude_code.config.config import ModelConfig, load_config
|
|
2
2
|
from klaude_code.trace import log
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
def _normalize_model_key(value: str) -> str:
|
|
6
|
+
"""Normalize a model identifier for loose matching.
|
|
7
|
+
|
|
8
|
+
This enables aliases like:
|
|
9
|
+
- gpt52 -> gpt-5.2
|
|
10
|
+
- gpt5.2 -> gpt-5.2
|
|
11
|
+
|
|
12
|
+
Strategy: case-fold + keep only alphanumeric characters.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
return "".join(ch for ch in value.casefold() if ch.isalnum())
|
|
16
|
+
|
|
17
|
+
|
|
5
18
|
def select_model_from_config(preferred: str | None = None) -> str | None:
|
|
6
19
|
"""
|
|
7
20
|
Interactive single-choice model selector.
|
|
8
21
|
for `--select-model`
|
|
22
|
+
|
|
23
|
+
If preferred is provided:
|
|
24
|
+
- Exact match: return immediately
|
|
25
|
+
- Single partial match (case-insensitive): return immediately
|
|
26
|
+
- Otherwise: fall through to interactive selection
|
|
9
27
|
"""
|
|
10
28
|
config = load_config()
|
|
11
29
|
assert config is not None
|
|
12
|
-
models = sorted(config.model_list, key=lambda m: m.model_name.lower())
|
|
30
|
+
models: list[ModelConfig] = sorted(config.model_list, key=lambda m: m.model_name.lower())
|
|
13
31
|
|
|
14
32
|
if not models:
|
|
15
33
|
raise ValueError("No models configured. Please update your config.yaml")
|
|
16
34
|
|
|
17
35
|
names: list[str] = [m.model_name for m in models]
|
|
18
36
|
|
|
37
|
+
# Try to match preferred model name
|
|
38
|
+
filtered_models = models
|
|
39
|
+
if preferred and preferred.strip():
|
|
40
|
+
preferred = preferred.strip()
|
|
41
|
+
# Exact match
|
|
42
|
+
if preferred in names:
|
|
43
|
+
return preferred
|
|
44
|
+
|
|
45
|
+
preferred_lower = preferred.lower()
|
|
46
|
+
# Case-insensitive exact match (model_name or model_params.model)
|
|
47
|
+
exact_ci_matches = [
|
|
48
|
+
m
|
|
49
|
+
for m in models
|
|
50
|
+
if preferred_lower == m.model_name.lower() or preferred_lower == (m.model_params.model or "").lower()
|
|
51
|
+
]
|
|
52
|
+
if len(exact_ci_matches) == 1:
|
|
53
|
+
return exact_ci_matches[0].model_name
|
|
54
|
+
|
|
55
|
+
# Normalized matching (e.g. gpt52 == gpt-5.2, gpt52 in gpt-5.2-2025-...)
|
|
56
|
+
preferred_norm = _normalize_model_key(preferred)
|
|
57
|
+
normalized_matches: list[ModelConfig] = []
|
|
58
|
+
if preferred_norm:
|
|
59
|
+
normalized_matches = [
|
|
60
|
+
m
|
|
61
|
+
for m in models
|
|
62
|
+
if preferred_norm == _normalize_model_key(m.model_name)
|
|
63
|
+
or preferred_norm == _normalize_model_key(m.model_params.model or "")
|
|
64
|
+
]
|
|
65
|
+
if len(normalized_matches) == 1:
|
|
66
|
+
return normalized_matches[0].model_name
|
|
67
|
+
|
|
68
|
+
if not normalized_matches and len(preferred_norm) >= 4:
|
|
69
|
+
normalized_matches = [
|
|
70
|
+
m
|
|
71
|
+
for m in models
|
|
72
|
+
if preferred_norm in _normalize_model_key(m.model_name)
|
|
73
|
+
or preferred_norm in _normalize_model_key(m.model_params.model or "")
|
|
74
|
+
]
|
|
75
|
+
if len(normalized_matches) == 1:
|
|
76
|
+
return normalized_matches[0].model_name
|
|
77
|
+
|
|
78
|
+
# Partial match (case-insensitive) on model_name or model_params.model.
|
|
79
|
+
# If normalized matching found candidates (even if multiple), prefer those as the filter set.
|
|
80
|
+
matches = normalized_matches or [
|
|
81
|
+
m
|
|
82
|
+
for m in models
|
|
83
|
+
if preferred_lower in m.model_name.lower() or preferred_lower in (m.model_params.model or "").lower()
|
|
84
|
+
]
|
|
85
|
+
if len(matches) == 1:
|
|
86
|
+
return matches[0].model_name
|
|
87
|
+
if matches:
|
|
88
|
+
# Multiple matches: filter the list for interactive selection
|
|
89
|
+
filtered_models = matches
|
|
90
|
+
else:
|
|
91
|
+
# No matches: show all models without filter hint
|
|
92
|
+
preferred = None
|
|
93
|
+
|
|
19
94
|
try:
|
|
20
95
|
import questionary
|
|
21
96
|
|
|
22
97
|
choices: list[questionary.Choice] = []
|
|
23
98
|
|
|
24
|
-
max_model_name_length = max(len(m.model_name) for m in
|
|
25
|
-
for m in
|
|
99
|
+
max_model_name_length = max(len(m.model_name) for m in filtered_models)
|
|
100
|
+
for m in filtered_models:
|
|
26
101
|
star = "★ " if m.model_name == config.main_model else " "
|
|
27
102
|
title = f"{star}{m.model_name:<{max_model_name_length}} → {m.model_params.model or 'N/A'} @ {m.provider}"
|
|
28
103
|
choices.append(questionary.Choice(title=title, value=m.model_name))
|
|
29
104
|
|
|
30
105
|
try:
|
|
106
|
+
message = f"Select a model (filtered by '{preferred}'):" if preferred else "Select a model:"
|
|
31
107
|
result = questionary.select(
|
|
32
|
-
message=
|
|
108
|
+
message=message,
|
|
33
109
|
choices=choices,
|
|
34
110
|
pointer="→",
|
|
35
111
|
instruction="↑↓ to move • Enter to select",
|
klaude_code/const/__init__.py
CHANGED