klaude-code 1.9.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 +2 -6
- klaude_code/cli/auth_cmd.py +4 -4
- klaude_code/cli/list_model.py +1 -1
- 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 +11 -2
- 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 +16 -0
- klaude_code/config/builtin_config.py +16 -5
- klaude_code/config/config.py +7 -2
- 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/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 +79 -44
- klaude_code/llm/anthropic/input.py +116 -108
- klaude_code/llm/bedrock/client.py +8 -5
- klaude_code/llm/claude/client.py +18 -8
- klaude_code/llm/client.py +4 -3
- klaude_code/llm/codex/client.py +15 -9
- 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 -17
- 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 +20 -2
- 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 +1 -0
- 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 +126 -54
- 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 +33 -5
- 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 +8 -2
- 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 +11 -2
- {klaude_code-1.9.0.dist-info → klaude_code-2.0.0.dist-info}/METADATA +4 -2
- klaude_code-2.0.0.dist-info/RECORD +229 -0
- klaude_code-1.9.0.dist-info/RECORD +0 -224
- {klaude_code-1.9.0.dist-info → klaude_code-2.0.0.dist-info}/WHEEL +0 -0
- {klaude_code-1.9.0.dist-info → klaude_code-2.0.0.dist-info}/entry_points.txt +0 -0
klaude_code/auth/base.py
CHANGED
|
@@ -4,11 +4,10 @@ import json
|
|
|
4
4
|
import time
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any,
|
|
7
|
+
from typing import Any, cast
|
|
8
8
|
|
|
9
9
|
from pydantic import BaseModel
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
KLAUDE_AUTH_FILE = Path.home() / ".klaude" / "klaude-auth.json"
|
|
13
12
|
|
|
14
13
|
|
|
@@ -24,10 +23,7 @@ class BaseAuthState(BaseModel):
|
|
|
24
23
|
return time.time() + buffer_seconds >= self.expires_at
|
|
25
24
|
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class BaseTokenManager(ABC, Generic[T]):
|
|
26
|
+
class BaseTokenManager[T: BaseAuthState](ABC):
|
|
31
27
|
"""Base class for OAuth token management."""
|
|
32
28
|
|
|
33
29
|
def __init__(self, auth_file: Path | None = None):
|
klaude_code/cli/auth_cmd.py
CHANGED
|
@@ -56,20 +56,20 @@ def login_command(
|
|
|
56
56
|
state = token_manager.get_state()
|
|
57
57
|
if state and not state.is_expired():
|
|
58
58
|
log(("You are already logged in to Codex.", "green"))
|
|
59
|
-
log(f" Account ID: {state.account_id[:8]}
|
|
59
|
+
log(f" Account ID: {state.account_id[:8]}…")
|
|
60
60
|
expires_dt = datetime.datetime.fromtimestamp(state.expires_at, tz=datetime.UTC)
|
|
61
61
|
log(f" Expires: {expires_dt.strftime('%Y-%m-%d %H:%M:%S UTC')}")
|
|
62
62
|
if not typer.confirm("Do you want to re-login?"):
|
|
63
63
|
return
|
|
64
64
|
|
|
65
|
-
log("Starting Codex OAuth login flow
|
|
65
|
+
log("Starting Codex OAuth login flow…")
|
|
66
66
|
log("A browser window will open for authentication.")
|
|
67
67
|
|
|
68
68
|
try:
|
|
69
69
|
oauth = CodexOAuth(token_manager)
|
|
70
70
|
state = oauth.login()
|
|
71
71
|
log(("Login successful!", "green"))
|
|
72
|
-
log(f" Account ID: {state.account_id[:8]}
|
|
72
|
+
log(f" Account ID: {state.account_id[:8]}…")
|
|
73
73
|
expires_dt = datetime.datetime.fromtimestamp(state.expires_at, tz=datetime.UTC)
|
|
74
74
|
log(f" Expires: {expires_dt.strftime('%Y-%m-%d %H:%M:%S UTC')}")
|
|
75
75
|
except Exception as e:
|
|
@@ -90,7 +90,7 @@ def login_command(
|
|
|
90
90
|
if not typer.confirm("Do you want to re-login?"):
|
|
91
91
|
return
|
|
92
92
|
|
|
93
|
-
log("Starting Claude OAuth login flow
|
|
93
|
+
log("Starting Claude OAuth login flow…")
|
|
94
94
|
log("A browser window will open for authentication.")
|
|
95
95
|
log("After login, paste the authorization code in the terminal.")
|
|
96
96
|
|
klaude_code/cli/list_model.py
CHANGED
|
@@ -50,7 +50,7 @@ def _get_codex_status_rows() -> list[tuple[Text, Text]]:
|
|
|
50
50
|
Text.assemble(
|
|
51
51
|
("Logged in", ThemeKey.CONFIG_STATUS_OK),
|
|
52
52
|
(
|
|
53
|
-
f" (account: {state.account_id[:8]}
|
|
53
|
+
f" (account: {state.account_id[:8]}…, expires: {expires_dt.strftime('%Y-%m-%d %H:%M UTC')})",
|
|
54
54
|
"dim",
|
|
55
55
|
),
|
|
56
56
|
),
|
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=[
|
|
@@ -10,7 +10,7 @@ from rich.console import Console
|
|
|
10
10
|
from rich.text import Text
|
|
11
11
|
|
|
12
12
|
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
13
|
-
from klaude_code.protocol import commands, events, model
|
|
13
|
+
from klaude_code.protocol import commands, events, message, model
|
|
14
14
|
from klaude_code.session.export import build_export_html
|
|
15
15
|
|
|
16
16
|
|
|
@@ -33,15 +33,15 @@ class ExportOnlineCommand(CommandABC):
|
|
|
33
33
|
def is_interactive(self) -> bool:
|
|
34
34
|
return False
|
|
35
35
|
|
|
36
|
-
async def run(self, agent: Agent, user_input:
|
|
36
|
+
async def run(self, agent: Agent, user_input: message.UserInputPayload) -> CommandResult:
|
|
37
37
|
del user_input # unused
|
|
38
38
|
# Check if npx or surge is available
|
|
39
39
|
surge_cmd = self._get_surge_command()
|
|
40
40
|
if not surge_cmd:
|
|
41
41
|
event = events.DeveloperMessageEvent(
|
|
42
42
|
session_id=agent.session.id,
|
|
43
|
-
item=
|
|
44
|
-
|
|
43
|
+
item=message.DeveloperMessage(
|
|
44
|
+
parts=message.text_parts_from_str("surge.sh CLI not found. Install with: npm install -g surge"),
|
|
45
45
|
command_output=model.CommandOutput(command_name=self.name, is_error=True),
|
|
46
46
|
),
|
|
47
47
|
)
|
|
@@ -50,29 +50,29 @@ class ExportOnlineCommand(CommandABC):
|
|
|
50
50
|
try:
|
|
51
51
|
console = Console()
|
|
52
52
|
# Check login status inside status context since npx surge whoami can be slow
|
|
53
|
-
with console.status(Text("Checking surge.sh login status
|
|
53
|
+
with console.status(Text("Checking surge.sh login status…", style="dim"), spinner_style="dim"):
|
|
54
54
|
logged_in = self._is_surge_logged_in(surge_cmd)
|
|
55
55
|
|
|
56
56
|
if not logged_in:
|
|
57
57
|
login_cmd = " ".join([*surge_cmd, "login"])
|
|
58
58
|
event = events.DeveloperMessageEvent(
|
|
59
59
|
session_id=agent.session.id,
|
|
60
|
-
item=
|
|
61
|
-
|
|
60
|
+
item=message.DeveloperMessage(
|
|
61
|
+
parts=message.text_parts_from_str(f"Not logged in to surge.sh. Please run: {login_cmd}"),
|
|
62
62
|
command_output=model.CommandOutput(command_name=self.name, is_error=True),
|
|
63
63
|
),
|
|
64
64
|
)
|
|
65
65
|
return CommandResult(events=[event])
|
|
66
66
|
|
|
67
|
-
with console.status(Text("Deploying to surge.sh
|
|
67
|
+
with console.status(Text("Deploying to surge.sh…", style="dim"), spinner_style="dim"):
|
|
68
68
|
html_doc = self._build_html(agent)
|
|
69
69
|
domain = self._generate_domain()
|
|
70
70
|
url = self._deploy_to_surge(surge_cmd, html_doc, domain)
|
|
71
71
|
|
|
72
72
|
event = events.DeveloperMessageEvent(
|
|
73
73
|
session_id=agent.session.id,
|
|
74
|
-
item=
|
|
75
|
-
|
|
74
|
+
item=message.DeveloperMessage(
|
|
75
|
+
parts=message.text_parts_from_str(f"Session deployed to: {url}"),
|
|
76
76
|
command_output=model.CommandOutput(command_name=self.name),
|
|
77
77
|
),
|
|
78
78
|
)
|
|
@@ -82,8 +82,8 @@ class ExportOnlineCommand(CommandABC):
|
|
|
82
82
|
|
|
83
83
|
event = events.DeveloperMessageEvent(
|
|
84
84
|
session_id=agent.session.id,
|
|
85
|
-
item=
|
|
86
|
-
|
|
85
|
+
item=message.DeveloperMessage(
|
|
86
|
+
parts=message.text_parts_from_str(f"Failed to deploy session: {exc}\n{traceback.format_exc()}"),
|
|
87
87
|
command_output=model.CommandOutput(command_name=self.name, is_error=True),
|
|
88
88
|
),
|
|
89
89
|
)
|
|
@@ -6,7 +6,7 @@ from typing import Literal
|
|
|
6
6
|
from prompt_toolkit.styles import Style
|
|
7
7
|
|
|
8
8
|
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
9
|
-
from klaude_code.protocol import commands, events, model
|
|
9
|
+
from klaude_code.protocol import commands, events, message, model
|
|
10
10
|
from klaude_code.ui.modes.repl.clipboard import copy_to_clipboard
|
|
11
11
|
from klaude_code.ui.terminal.selector import SelectItem, select_one
|
|
12
12
|
|
|
@@ -41,29 +41,29 @@ def _truncate(text: str, max_len: int = 60) -> str:
|
|
|
41
41
|
text = text.replace("\n", " ").strip()
|
|
42
42
|
if len(text) <= max_len:
|
|
43
43
|
return text
|
|
44
|
-
return text[: max_len -
|
|
44
|
+
return text[: max_len - 1] + "…"
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def _build_fork_points(conversation_history: list[
|
|
47
|
+
def _build_fork_points(conversation_history: list[message.HistoryEvent]) -> list[ForkPoint]:
|
|
48
48
|
"""Build list of fork points from conversation history.
|
|
49
49
|
|
|
50
50
|
Fork points are:
|
|
51
|
-
- Each
|
|
51
|
+
- Each UserMessage position (for UI display, including first which would be empty session)
|
|
52
52
|
- The end of the conversation (fork entire conversation)
|
|
53
53
|
"""
|
|
54
54
|
fork_points: list[ForkPoint] = []
|
|
55
55
|
user_indices: list[int] = []
|
|
56
56
|
|
|
57
57
|
for i, item in enumerate(conversation_history):
|
|
58
|
-
if isinstance(item,
|
|
58
|
+
if isinstance(item, message.UserMessage):
|
|
59
59
|
user_indices.append(i)
|
|
60
60
|
|
|
61
|
-
# For each
|
|
61
|
+
# For each UserMessage, create a fork point at that position
|
|
62
62
|
for i, user_idx in enumerate(user_indices):
|
|
63
63
|
user_item = conversation_history[user_idx]
|
|
64
|
-
assert isinstance(user_item,
|
|
64
|
+
assert isinstance(user_item, message.UserMessage)
|
|
65
65
|
|
|
66
|
-
# Find the end of this "task" (next
|
|
66
|
+
# Find the end of this "task" (next UserMessage or end of history)
|
|
67
67
|
next_user_idx = user_indices[i + 1] if i + 1 < len(user_indices) else len(conversation_history)
|
|
68
68
|
|
|
69
69
|
# Count tool calls by name and find last assistant message in this segment
|
|
@@ -71,15 +71,19 @@ def _build_fork_points(conversation_history: list[model.ConversationItem]) -> li
|
|
|
71
71
|
last_assistant_content = ""
|
|
72
72
|
for j in range(user_idx, next_user_idx):
|
|
73
73
|
item = conversation_history[j]
|
|
74
|
-
if isinstance(item,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
if isinstance(item, message.AssistantMessage):
|
|
75
|
+
for part in item.parts:
|
|
76
|
+
if isinstance(part, message.ToolCallPart):
|
|
77
|
+
tool_stats[part.tool_name] = tool_stats.get(part.tool_name, 0) + 1
|
|
78
|
+
text = message.join_text_parts(item.parts)
|
|
79
|
+
if text:
|
|
80
|
+
last_assistant_content = text
|
|
81
|
+
|
|
82
|
+
user_text = message.join_text_parts(user_item.parts)
|
|
79
83
|
fork_points.append(
|
|
80
84
|
ForkPoint(
|
|
81
85
|
history_index=user_idx,
|
|
82
|
-
user_message=
|
|
86
|
+
user_message=user_text or "(empty)",
|
|
83
87
|
tool_call_stats=tool_stats,
|
|
84
88
|
last_assistant_summary=_truncate(last_assistant_content) if last_assistant_content else "",
|
|
85
89
|
)
|
|
@@ -195,14 +199,14 @@ class ForkSessionCommand(CommandABC):
|
|
|
195
199
|
def is_interactive(self) -> bool:
|
|
196
200
|
return True
|
|
197
201
|
|
|
198
|
-
async def run(self, agent: Agent, user_input:
|
|
202
|
+
async def run(self, agent: Agent, user_input: message.UserInputPayload) -> CommandResult:
|
|
199
203
|
del user_input # unused
|
|
200
204
|
|
|
201
205
|
if agent.session.messages_count == 0:
|
|
202
206
|
event = events.DeveloperMessageEvent(
|
|
203
207
|
session_id=agent.session.id,
|
|
204
|
-
item=
|
|
205
|
-
|
|
208
|
+
item=message.DeveloperMessage(
|
|
209
|
+
parts=message.text_parts_from_str("(no messages to fork)"),
|
|
206
210
|
command_output=model.CommandOutput(command_name=self.name),
|
|
207
211
|
),
|
|
208
212
|
)
|
|
@@ -221,8 +225,8 @@ class ForkSessionCommand(CommandABC):
|
|
|
221
225
|
|
|
222
226
|
event = events.DeveloperMessageEvent(
|
|
223
227
|
session_id=agent.session.id,
|
|
224
|
-
item=
|
|
225
|
-
|
|
228
|
+
item=message.DeveloperMessage(
|
|
229
|
+
parts=message.text_parts_from_str(f"Session forked successfully. New session id: {new_session.id}"),
|
|
226
230
|
command_output=model.CommandOutput(
|
|
227
231
|
command_name=self.name,
|
|
228
232
|
ui_extra=model.SessionIdUIExtra(session_id=new_session.id),
|
|
@@ -237,8 +241,8 @@ class ForkSessionCommand(CommandABC):
|
|
|
237
241
|
if selected == "cancelled":
|
|
238
242
|
event = events.DeveloperMessageEvent(
|
|
239
243
|
session_id=agent.session.id,
|
|
240
|
-
item=
|
|
241
|
-
|
|
244
|
+
item=message.DeveloperMessage(
|
|
245
|
+
parts=message.text_parts_from_str("(fork cancelled)"),
|
|
242
246
|
command_output=model.CommandOutput(command_name=self.name),
|
|
243
247
|
),
|
|
244
248
|
)
|
|
@@ -256,8 +260,10 @@ class ForkSessionCommand(CommandABC):
|
|
|
256
260
|
|
|
257
261
|
event = events.DeveloperMessageEvent(
|
|
258
262
|
session_id=agent.session.id,
|
|
259
|
-
item=
|
|
260
|
-
|
|
263
|
+
item=message.DeveloperMessage(
|
|
264
|
+
parts=message.text_parts_from_str(
|
|
265
|
+
f"Session forked ({fork_description}). New session id: {new_session.id}"
|
|
266
|
+
),
|
|
261
267
|
command_output=model.CommandOutput(
|
|
262
268
|
command_name=self.name,
|
|
263
269
|
ui_extra=model.SessionIdUIExtra(session_id=new_session.id),
|
klaude_code/command/help_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
|
|
|
4
4
|
|
|
5
5
|
class HelpCommand(CommandABC):
|
|
@@ -13,7 +13,7 @@ class HelpCommand(CommandABC):
|
|
|
13
13
|
def summary(self) -> str:
|
|
14
14
|
return "Show help and available commands"
|
|
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
18
|
lines: list[str] = [
|
|
19
19
|
"""
|
|
@@ -41,8 +41,8 @@ Available slash commands:"""
|
|
|
41
41
|
|
|
42
42
|
event = events.DeveloperMessageEvent(
|
|
43
43
|
session_id=agent.session.id,
|
|
44
|
-
item=
|
|
45
|
-
|
|
44
|
+
item=message.DeveloperMessage(
|
|
45
|
+
parts=message.text_parts_from_str("\n".join(lines)),
|
|
46
46
|
command_output=model.CommandOutput(command_name=self.name),
|
|
47
47
|
),
|
|
48
48
|
)
|
klaude_code/command/model_cmd.py
CHANGED
|
@@ -4,7 +4,7 @@ from prompt_toolkit.styles import Style
|
|
|
4
4
|
|
|
5
5
|
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
6
6
|
from klaude_code.command.model_select import select_model_interactive
|
|
7
|
-
from klaude_code.protocol import commands, events, model, op
|
|
7
|
+
from klaude_code.protocol import commands, events, message, model, op
|
|
8
8
|
from klaude_code.ui.terminal.selector import SelectItem, select_one
|
|
9
9
|
|
|
10
10
|
SELECT_STYLE = Style(
|
|
@@ -65,7 +65,7 @@ class ModelCommand(CommandABC):
|
|
|
65
65
|
def placeholder(self) -> str:
|
|
66
66
|
return "model name"
|
|
67
67
|
|
|
68
|
-
async def run(self, agent: Agent, user_input:
|
|
68
|
+
async def run(self, agent: Agent, user_input: message.UserInputPayload) -> CommandResult:
|
|
69
69
|
selected_model = await asyncio.to_thread(select_model_interactive, preferred=user_input.text)
|
|
70
70
|
|
|
71
71
|
current_model = agent.profile.llm_client.model_name if agent.profile else None
|
|
@@ -74,8 +74,8 @@ class ModelCommand(CommandABC):
|
|
|
74
74
|
events=[
|
|
75
75
|
events.DeveloperMessageEvent(
|
|
76
76
|
session_id=agent.session.id,
|
|
77
|
-
item=
|
|
78
|
-
|
|
77
|
+
item=message.DeveloperMessage(
|
|
78
|
+
parts=message.text_parts_from_str("(no change)"),
|
|
79
79
|
command_output=model.CommandOutput(command_name=self.name),
|
|
80
80
|
),
|
|
81
81
|
)
|
|
@@ -6,9 +6,18 @@ description: Commit current git changes
|
|
|
6
6
|
|
|
7
7
|
### Step 1: Detect version control system
|
|
8
8
|
|
|
9
|
-
Check if `jj` is available in the current environment.
|
|
9
|
+
Check if `jj` is available in the current environment. (check in your <env> tag)
|
|
10
10
|
|
|
11
|
-
### Step
|
|
11
|
+
### Step 2: Run pre-commit checks
|
|
12
|
+
|
|
13
|
+
Before creating a commit, run the following checks:
|
|
14
|
+
|
|
15
|
+
1. Run the project's linter to check and fix code style issues
|
|
16
|
+
2. Run the project's test suite to ensure all tests pass
|
|
17
|
+
3. If either check fails, stop the commit process and report the errors to the user
|
|
18
|
+
4. If both checks pass, proceed to the next step
|
|
19
|
+
|
|
20
|
+
### Step 3A: If jj is available
|
|
12
21
|
|
|
13
22
|
1. Run `jj status` and `jj log -r 'ancestors(@, 10)'` to see working copy changes and the last 10 changes
|
|
14
23
|
2. For each change that has no description (shows as "(no description set)"):
|
|
@@ -3,7 +3,7 @@ from importlib.resources import files
|
|
|
3
3
|
import yaml
|
|
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
|
from klaude_code.trace import log_debug
|
|
8
8
|
|
|
9
9
|
|
|
@@ -55,7 +55,7 @@ class PromptCommand(CommandABC):
|
|
|
55
55
|
def support_addition_params(self) -> bool:
|
|
56
56
|
return True
|
|
57
57
|
|
|
58
|
-
async def run(self, agent: Agent, user_input:
|
|
58
|
+
async def run(self, agent: Agent, user_input: message.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>"
|
|
@@ -71,7 +71,7 @@ class PromptCommand(CommandABC):
|
|
|
71
71
|
operations=[
|
|
72
72
|
op.RunAgentOperation(
|
|
73
73
|
session_id=agent.session.id,
|
|
74
|
-
input=
|
|
74
|
+
input=message.UserInputPayload(text=final_prompt, images=user_input.images),
|
|
75
75
|
)
|
|
76
76
|
]
|
|
77
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, message
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class RefreshTerminalCommand(CommandABC):
|
|
@@ -17,7 +17,7 @@ class RefreshTerminalCommand(CommandABC):
|
|
|
17
17
|
def is_interactive(self) -> bool:
|
|
18
18
|
return True
|
|
19
19
|
|
|
20
|
-
async def run(self, agent: Agent, user_input:
|
|
20
|
+
async def run(self, agent: Agent, user_input: message.UserInputPayload) -> CommandResult:
|
|
21
21
|
del user_input # unused
|
|
22
22
|
import os
|
|
23
23
|
|
klaude_code/command/registry.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|
|
3
3
|
|
|
4
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, op
|
|
6
|
+
from klaude_code.protocol import commands, events, message, model, op
|
|
7
7
|
from klaude_code.trace import log_debug
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
@@ -133,7 +133,7 @@ def is_slash_command_name(name: str) -> bool:
|
|
|
133
133
|
return _resolve_command_key(name) is not None
|
|
134
134
|
|
|
135
135
|
|
|
136
|
-
async def dispatch_command(user_input:
|
|
136
|
+
async def dispatch_command(user_input: message.UserInputPayload, agent: Agent, *, submission_id: str) -> CommandResult:
|
|
137
137
|
_ensure_commands_loaded()
|
|
138
138
|
# Detect command name
|
|
139
139
|
raw = user_input.text
|
|
@@ -168,7 +168,7 @@ async def dispatch_command(user_input: model.UserInputPayload, agent: Agent, *,
|
|
|
168
168
|
command_identifier: commands.CommandName | str = command.name
|
|
169
169
|
|
|
170
170
|
try:
|
|
171
|
-
user_input_for_command =
|
|
171
|
+
user_input_for_command = message.UserInputPayload(text=rest, images=user_input.images)
|
|
172
172
|
result = await command.run(agent, user_input_for_command)
|
|
173
173
|
ops = list(result.operations or [])
|
|
174
174
|
for operation in ops:
|
|
@@ -187,8 +187,10 @@ async def dispatch_command(user_input: model.UserInputPayload, agent: Agent, *,
|
|
|
187
187
|
events=[
|
|
188
188
|
events.DeveloperMessageEvent(
|
|
189
189
|
session_id=agent.session.id,
|
|
190
|
-
item=
|
|
191
|
-
|
|
190
|
+
item=message.DeveloperMessage(
|
|
191
|
+
parts=message.text_parts_from_str(
|
|
192
|
+
f"Command {command_identifier} error: [{e.__class__.__name__}] {e!s}"
|
|
193
|
+
),
|
|
192
194
|
command_output=command_output,
|
|
193
195
|
),
|
|
194
196
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
3
|
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
4
|
-
from klaude_code.protocol import commands, events, model
|
|
4
|
+
from klaude_code.protocol import commands, events, message, model
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def _read_changelog() -> str:
|
|
@@ -68,15 +68,15 @@ class ReleaseNotesCommand(CommandABC):
|
|
|
68
68
|
def summary(self) -> str:
|
|
69
69
|
return "Show the latest release notes"
|
|
70
70
|
|
|
71
|
-
async def run(self, agent: Agent, user_input:
|
|
71
|
+
async def run(self, agent: Agent, user_input: message.UserInputPayload) -> CommandResult:
|
|
72
72
|
del user_input # unused
|
|
73
73
|
changelog = _read_changelog()
|
|
74
74
|
content = _extract_releases(changelog, count=10)
|
|
75
75
|
|
|
76
76
|
event = events.DeveloperMessageEvent(
|
|
77
77
|
session_id=agent.session.id,
|
|
78
|
-
item=
|
|
79
|
-
|
|
78
|
+
item=message.DeveloperMessage(
|
|
79
|
+
parts=message.text_parts_from_str(content),
|
|
80
80
|
command_output=model.CommandOutput(command_name=self.name),
|
|
81
81
|
),
|
|
82
82
|
)
|