klaude-code 1.2.18__py3-none-any.whl → 1.2.20__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/main.py +42 -22
- klaude_code/cli/runtime.py +46 -2
- klaude_code/{version.py → cli/self_update.py} +110 -2
- klaude_code/command/__init__.py +1 -3
- klaude_code/command/clear_cmd.py +5 -4
- klaude_code/command/command_abc.py +5 -40
- klaude_code/command/debug_cmd.py +2 -2
- klaude_code/command/diff_cmd.py +2 -1
- klaude_code/command/export_cmd.py +14 -49
- klaude_code/command/export_online_cmd.py +10 -4
- klaude_code/command/help_cmd.py +2 -1
- klaude_code/command/model_cmd.py +7 -5
- klaude_code/command/prompt-jj-workspace.md +18 -0
- klaude_code/command/prompt_command.py +16 -9
- klaude_code/command/refresh_cmd.py +3 -2
- klaude_code/command/registry.py +98 -28
- klaude_code/command/release_notes_cmd.py +2 -1
- klaude_code/command/status_cmd.py +2 -1
- klaude_code/command/terminal_setup_cmd.py +2 -1
- klaude_code/command/thinking_cmd.py +6 -4
- klaude_code/core/executor.py +187 -180
- klaude_code/core/manager/sub_agent_manager.py +3 -0
- klaude_code/core/prompt.py +4 -1
- klaude_code/core/prompts/prompt-sub-agent-explore.md +14 -2
- klaude_code/core/prompts/prompt-sub-agent-web.md +3 -3
- klaude_code/core/reminders.py +70 -26
- klaude_code/core/task.py +13 -12
- klaude_code/core/tool/__init__.py +2 -0
- klaude_code/core/tool/file/apply_patch_tool.py +3 -1
- klaude_code/core/tool/file/edit_tool.py +7 -5
- klaude_code/core/tool/file/multi_edit_tool.py +7 -5
- klaude_code/core/tool/file/read_tool.md +1 -1
- klaude_code/core/tool/file/read_tool.py +8 -4
- klaude_code/core/tool/file/write_tool.py +8 -6
- klaude_code/core/tool/memory/skill_loader.py +12 -10
- klaude_code/core/tool/shell/bash_tool.py +89 -17
- klaude_code/core/tool/sub_agent_tool.py +5 -1
- klaude_code/core/tool/tool_abc.py +18 -0
- klaude_code/core/tool/tool_context.py +6 -6
- klaude_code/core/tool/tool_registry.py +1 -1
- klaude_code/core/tool/tool_runner.py +7 -7
- klaude_code/core/tool/web/web_fetch_tool.py +77 -22
- klaude_code/core/tool/web/web_search_tool.py +5 -1
- klaude_code/llm/anthropic/client.py +25 -9
- klaude_code/llm/openai_compatible/client.py +5 -2
- klaude_code/llm/openrouter/client.py +7 -3
- klaude_code/llm/responses/client.py +6 -1
- klaude_code/protocol/model.py +8 -1
- klaude_code/protocol/op.py +47 -0
- klaude_code/protocol/op_handler.py +25 -1
- klaude_code/protocol/sub_agent/web.py +1 -1
- klaude_code/session/codec.py +71 -0
- klaude_code/session/export.py +21 -11
- klaude_code/session/session.py +186 -322
- klaude_code/session/store.py +215 -0
- klaude_code/session/templates/export_session.html +48 -47
- klaude_code/ui/modes/repl/completers.py +211 -71
- klaude_code/ui/modes/repl/event_handler.py +7 -23
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +5 -7
- klaude_code/ui/modes/repl/renderer.py +2 -2
- klaude_code/ui/renderers/common.py +54 -0
- klaude_code/ui/renderers/developer.py +2 -3
- klaude_code/ui/renderers/errors.py +1 -1
- klaude_code/ui/renderers/metadata.py +10 -1
- klaude_code/ui/renderers/tools.py +3 -4
- klaude_code/ui/rich/__init__.py +10 -1
- klaude_code/ui/rich/cjk_wrap.py +228 -0
- klaude_code/ui/rich/status.py +0 -1
- klaude_code/ui/utils/common.py +0 -18
- {klaude_code-1.2.18.dist-info → klaude_code-1.2.20.dist-info}/METADATA +18 -2
- {klaude_code-1.2.18.dist-info → klaude_code-1.2.20.dist-info}/RECORD +73 -70
- klaude_code/ui/utils/debouncer.py +0 -42
- {klaude_code-1.2.18.dist-info → klaude_code-1.2.20.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.18.dist-info → klaude_code-1.2.20.dist-info}/entry_points.txt +0 -0
|
@@ -2,8 +2,8 @@ from importlib.resources import files
|
|
|
2
2
|
|
|
3
3
|
import yaml
|
|
4
4
|
|
|
5
|
-
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
6
|
-
from klaude_code.protocol import commands
|
|
5
|
+
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
6
|
+
from klaude_code.protocol import commands, model, op
|
|
7
7
|
from klaude_code.trace import log_debug
|
|
8
8
|
|
|
9
9
|
|
|
@@ -55,16 +55,23 @@ class PromptCommand(CommandABC):
|
|
|
55
55
|
def support_addition_params(self) -> bool:
|
|
56
56
|
return True
|
|
57
57
|
|
|
58
|
-
async def run(self,
|
|
58
|
+
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
59
59
|
self._ensure_loaded()
|
|
60
60
|
template_content = self._content or ""
|
|
61
|
-
|
|
61
|
+
user_input_text = user_input.text.strip() or "<none>"
|
|
62
62
|
|
|
63
63
|
if "$ARGUMENTS" in template_content:
|
|
64
|
-
final_prompt = template_content.replace("$ARGUMENTS",
|
|
64
|
+
final_prompt = template_content.replace("$ARGUMENTS", user_input_text)
|
|
65
65
|
else:
|
|
66
66
|
final_prompt = template_content
|
|
67
|
-
if
|
|
68
|
-
final_prompt += f"\n\nAdditional Instructions:\n{
|
|
69
|
-
|
|
70
|
-
return CommandResult(
|
|
67
|
+
if user_input_text:
|
|
68
|
+
final_prompt += f"\n\nAdditional Instructions:\n{user_input_text}"
|
|
69
|
+
|
|
70
|
+
return CommandResult(
|
|
71
|
+
operations=[
|
|
72
|
+
op.RunAgentOperation(
|
|
73
|
+
session_id=agent.session.id,
|
|
74
|
+
input=model.UserInputPayload(text=final_prompt, images=user_input.images),
|
|
75
|
+
)
|
|
76
|
+
]
|
|
77
|
+
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
2
|
-
from klaude_code.protocol import commands, events
|
|
2
|
+
from klaude_code.protocol import commands, events, model
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class RefreshTerminalCommand(CommandABC):
|
|
@@ -17,7 +17,8 @@ class RefreshTerminalCommand(CommandABC):
|
|
|
17
17
|
def is_interactive(self) -> bool:
|
|
18
18
|
return True
|
|
19
19
|
|
|
20
|
-
async def run(self,
|
|
20
|
+
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
21
|
+
del user_input # unused
|
|
21
22
|
import os
|
|
22
23
|
|
|
23
24
|
os.system("cls" if os.name == "nt" else "clear")
|
klaude_code/command/registry.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from importlib.resources import files
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
|
-
from klaude_code.command.command_abc import Agent, CommandResult
|
|
4
|
+
from klaude_code.command.command_abc import Agent, CommandResult
|
|
5
5
|
from klaude_code.command.prompt_command import PromptCommand
|
|
6
|
-
from klaude_code.protocol import commands, events, model
|
|
6
|
+
from klaude_code.protocol import commands, events, model, op
|
|
7
7
|
from klaude_code.trace import log_debug
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
@@ -12,6 +12,68 @@ if TYPE_CHECKING:
|
|
|
12
12
|
_COMMANDS: dict[commands.CommandName | str, "CommandABC"] = {}
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
def _command_key_to_str(key: commands.CommandName | str) -> str:
|
|
16
|
+
if isinstance(key, commands.CommandName):
|
|
17
|
+
return key.value
|
|
18
|
+
return key
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _resolve_command_key(command_name_raw: str) -> commands.CommandName | str | None:
|
|
22
|
+
"""Resolve raw command token to a registered command key.
|
|
23
|
+
|
|
24
|
+
Resolution order:
|
|
25
|
+
1) Exact match
|
|
26
|
+
2) Enum conversion (for standard commands)
|
|
27
|
+
3) Prefix match (supports abbreviations like `exp` -> `export`)
|
|
28
|
+
|
|
29
|
+
Prefix match rules:
|
|
30
|
+
- If there's exactly one prefix match, use it.
|
|
31
|
+
- If multiple matches exist and one command name is a prefix of all others,
|
|
32
|
+
treat it as the base command and use it (e.g. `export` over `export-online`).
|
|
33
|
+
- Otherwise, consider it ambiguous and return None.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
if not command_name_raw:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
# Exact string match (works for both Enum and str keys because CommandName is a str Enum)
|
|
40
|
+
if command_name_raw in _COMMANDS:
|
|
41
|
+
return command_name_raw
|
|
42
|
+
|
|
43
|
+
# Enum conversion for standard commands
|
|
44
|
+
try:
|
|
45
|
+
enum_key = commands.CommandName(command_name_raw)
|
|
46
|
+
except ValueError:
|
|
47
|
+
enum_key = None
|
|
48
|
+
else:
|
|
49
|
+
if enum_key in _COMMANDS:
|
|
50
|
+
return enum_key
|
|
51
|
+
|
|
52
|
+
# Prefix match across all registered names
|
|
53
|
+
matching_keys: list[commands.CommandName | str] = []
|
|
54
|
+
matching_names: list[str] = []
|
|
55
|
+
for key in _COMMANDS:
|
|
56
|
+
key_str = _command_key_to_str(key)
|
|
57
|
+
if key_str.startswith(command_name_raw):
|
|
58
|
+
matching_keys.append(key)
|
|
59
|
+
matching_names.append(key_str)
|
|
60
|
+
|
|
61
|
+
if len(matching_keys) == 1:
|
|
62
|
+
return matching_keys[0]
|
|
63
|
+
|
|
64
|
+
if len(matching_keys) > 1:
|
|
65
|
+
# Prefer the base command when one is a prefix of all other matches.
|
|
66
|
+
base_matches = [
|
|
67
|
+
key
|
|
68
|
+
for key, key_name in zip(matching_keys, matching_names, strict=True)
|
|
69
|
+
if all(other.startswith(key_name) for other in matching_names if other != key_name)
|
|
70
|
+
]
|
|
71
|
+
if len(base_matches) == 1:
|
|
72
|
+
return base_matches[0]
|
|
73
|
+
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
15
77
|
def register(cmd: "CommandABC") -> None:
|
|
16
78
|
"""Register a command instance. Order of registration determines display order."""
|
|
17
79
|
_COMMANDS[cmd.name] = cmd
|
|
@@ -45,42 +107,53 @@ def get_commands() -> dict[commands.CommandName | str, "CommandABC"]:
|
|
|
45
107
|
|
|
46
108
|
def is_slash_command_name(name: str) -> bool:
|
|
47
109
|
_ensure_commands_loaded()
|
|
48
|
-
return name
|
|
110
|
+
return _resolve_command_key(name) is not None
|
|
49
111
|
|
|
50
112
|
|
|
51
|
-
async def dispatch_command(
|
|
113
|
+
async def dispatch_command(user_input: model.UserInputPayload, agent: Agent, *, submission_id: str) -> CommandResult:
|
|
52
114
|
_ensure_commands_loaded()
|
|
53
115
|
# Detect command name
|
|
116
|
+
raw = user_input.text
|
|
54
117
|
if not raw.startswith("/"):
|
|
55
|
-
return CommandResult(
|
|
118
|
+
return CommandResult(
|
|
119
|
+
operations=[
|
|
120
|
+
op.RunAgentOperation(
|
|
121
|
+
id=submission_id,
|
|
122
|
+
session_id=agent.session.id,
|
|
123
|
+
input=user_input,
|
|
124
|
+
)
|
|
125
|
+
]
|
|
126
|
+
)
|
|
56
127
|
|
|
57
128
|
splits = raw.split(" ", maxsplit=1)
|
|
58
129
|
command_name_raw = splits[0][1:]
|
|
59
130
|
rest = " ".join(splits[1:]) if len(splits) > 1 else ""
|
|
60
131
|
|
|
61
|
-
|
|
62
|
-
command_key = None
|
|
63
|
-
|
|
64
|
-
# First try exact string match
|
|
65
|
-
if command_name_raw in _COMMANDS:
|
|
66
|
-
command_key = command_name_raw
|
|
67
|
-
else:
|
|
68
|
-
# Then try Enum conversion for standard commands
|
|
69
|
-
try:
|
|
70
|
-
enum_key = commands.CommandName(command_name_raw)
|
|
71
|
-
if enum_key in _COMMANDS:
|
|
72
|
-
command_key = enum_key
|
|
73
|
-
except ValueError:
|
|
74
|
-
pass
|
|
75
|
-
|
|
132
|
+
command_key = _resolve_command_key(command_name_raw)
|
|
76
133
|
if command_key is None:
|
|
77
|
-
return CommandResult(
|
|
134
|
+
return CommandResult(
|
|
135
|
+
operations=[
|
|
136
|
+
op.RunAgentOperation(
|
|
137
|
+
id=submission_id,
|
|
138
|
+
session_id=agent.session.id,
|
|
139
|
+
input=user_input,
|
|
140
|
+
)
|
|
141
|
+
]
|
|
142
|
+
)
|
|
78
143
|
|
|
79
144
|
command = _COMMANDS[command_key]
|
|
80
145
|
command_identifier: commands.CommandName | str = command.name
|
|
81
146
|
|
|
82
147
|
try:
|
|
83
|
-
|
|
148
|
+
user_input_for_command = model.UserInputPayload(text=rest, images=user_input.images)
|
|
149
|
+
result = await command.run(agent, user_input_for_command)
|
|
150
|
+
ops = list(result.operations or [])
|
|
151
|
+
for operation in ops:
|
|
152
|
+
if isinstance(operation, op.RunAgentOperation):
|
|
153
|
+
operation.id = submission_id
|
|
154
|
+
if ops:
|
|
155
|
+
result.operations = ops
|
|
156
|
+
return result
|
|
84
157
|
except Exception as e:
|
|
85
158
|
command_output = (
|
|
86
159
|
model.CommandOutput(command_name=command_identifier, is_error=True)
|
|
@@ -106,11 +179,8 @@ def has_interactive_command(raw: str) -> bool:
|
|
|
106
179
|
return False
|
|
107
180
|
splits = raw.split(" ", maxsplit=1)
|
|
108
181
|
command_name_raw = splits[0][1:]
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
except ValueError:
|
|
112
|
-
return False
|
|
113
|
-
if command_name not in _COMMANDS:
|
|
182
|
+
command_key = _resolve_command_key(command_name_raw)
|
|
183
|
+
if command_key is None:
|
|
114
184
|
return False
|
|
115
|
-
command = _COMMANDS[
|
|
185
|
+
command = _COMMANDS[command_key]
|
|
116
186
|
return command.is_interactive
|
|
@@ -68,7 +68,8 @@ class ReleaseNotesCommand(CommandABC):
|
|
|
68
68
|
def summary(self) -> str:
|
|
69
69
|
return "Show the latest release notes"
|
|
70
70
|
|
|
71
|
-
async def run(self,
|
|
71
|
+
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
72
|
+
del user_input # unused
|
|
72
73
|
changelog = _read_changelog()
|
|
73
74
|
content = _extract_releases(changelog, count=10)
|
|
74
75
|
|
|
@@ -132,7 +132,8 @@ class StatusCommand(CommandABC):
|
|
|
132
132
|
def summary(self) -> str:
|
|
133
133
|
return "Show session usage statistics"
|
|
134
134
|
|
|
135
|
-
async def run(self,
|
|
135
|
+
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
136
|
+
del user_input # unused
|
|
136
137
|
session = agent.session
|
|
137
138
|
aggregated = accumulate_session_usage(session)
|
|
138
139
|
|
|
@@ -21,7 +21,8 @@ class TerminalSetupCommand(CommandABC):
|
|
|
21
21
|
def is_interactive(self) -> bool:
|
|
22
22
|
return False
|
|
23
23
|
|
|
24
|
-
async def run(self,
|
|
24
|
+
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
25
|
+
del user_input # unused
|
|
25
26
|
term_program = os.environ.get("TERM_PROGRAM", "").lower()
|
|
26
27
|
|
|
27
28
|
try:
|
|
@@ -6,9 +6,9 @@ from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
|
6
6
|
from klaude_code.protocol import commands, events, llm_param, model
|
|
7
7
|
|
|
8
8
|
# Thinking level options for different protocols
|
|
9
|
-
RESPONSES_LEVELS = ["
|
|
10
|
-
RESPONSES_GPT51_LEVELS = ["none", "
|
|
11
|
-
RESPONSES_GPT52_LEVELS = ["none", "
|
|
9
|
+
RESPONSES_LEVELS = ["low", "medium", "high"]
|
|
10
|
+
RESPONSES_GPT51_LEVELS = ["none", "low", "medium", "high"]
|
|
11
|
+
RESPONSES_GPT52_LEVELS = ["none", "low", "medium", "high", "xhigh"]
|
|
12
12
|
RESPONSES_CODEX_MAX_LEVELS = ["medium", "high", "xhigh"]
|
|
13
13
|
|
|
14
14
|
ANTHROPIC_LEVELS: list[tuple[str, int | None]] = [
|
|
@@ -169,7 +169,8 @@ class ThinkingCommand(CommandABC):
|
|
|
169
169
|
def is_interactive(self) -> bool:
|
|
170
170
|
return True
|
|
171
171
|
|
|
172
|
-
async def run(self,
|
|
172
|
+
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
173
|
+
del user_input # unused
|
|
173
174
|
if not agent.profile:
|
|
174
175
|
return self._no_change_result(agent, "No profile configured")
|
|
175
176
|
|
|
@@ -206,6 +207,7 @@ class ThinkingCommand(CommandABC):
|
|
|
206
207
|
|
|
207
208
|
# Apply the new thinking configuration
|
|
208
209
|
config.thinking = new_thinking
|
|
210
|
+
agent.session.model_thinking = new_thinking
|
|
209
211
|
new_status = _format_current_thinking(config)
|
|
210
212
|
|
|
211
213
|
return CommandResult(
|