klaude-code 1.2.8__py3-none-any.whl → 1.2.10__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/codex/__init__.py +1 -1
- klaude_code/cli/main.py +12 -1
- klaude_code/cli/runtime.py +7 -11
- klaude_code/command/__init__.py +68 -21
- klaude_code/command/clear_cmd.py +6 -2
- klaude_code/command/command_abc.py +5 -2
- klaude_code/command/diff_cmd.py +5 -2
- klaude_code/command/export_cmd.py +7 -4
- klaude_code/command/help_cmd.py +6 -2
- klaude_code/command/model_cmd.py +5 -2
- klaude_code/command/prompt-deslop.md +14 -0
- klaude_code/command/prompt_command.py +8 -3
- klaude_code/command/refresh_cmd.py +6 -2
- klaude_code/command/registry.py +17 -5
- klaude_code/command/release_notes_cmd.py +89 -0
- klaude_code/command/status_cmd.py +98 -56
- klaude_code/command/terminal_setup_cmd.py +7 -4
- klaude_code/const/__init__.py +1 -1
- klaude_code/core/agent.py +66 -26
- klaude_code/core/executor.py +2 -2
- klaude_code/core/manager/agent_manager.py +6 -7
- klaude_code/core/manager/llm_clients.py +47 -22
- klaude_code/core/manager/llm_clients_builder.py +19 -7
- klaude_code/core/manager/sub_agent_manager.py +6 -2
- klaude_code/core/prompt.py +38 -28
- klaude_code/core/reminders.py +4 -7
- klaude_code/core/task.py +59 -40
- klaude_code/core/tool/__init__.py +2 -0
- klaude_code/core/tool/file/_utils.py +30 -0
- klaude_code/core/tool/file/apply_patch_tool.py +1 -1
- klaude_code/core/tool/file/edit_tool.py +6 -31
- klaude_code/core/tool/file/multi_edit_tool.py +7 -32
- klaude_code/core/tool/file/read_tool.py +6 -18
- klaude_code/core/tool/file/write_tool.py +6 -31
- klaude_code/core/tool/memory/__init__.py +5 -0
- klaude_code/core/tool/memory/memory_tool.py +2 -2
- klaude_code/core/tool/memory/skill_loader.py +2 -1
- klaude_code/core/tool/memory/skill_tool.py +13 -0
- klaude_code/core/tool/sub_agent_tool.py +2 -1
- klaude_code/core/tool/todo/todo_write_tool.py +1 -1
- klaude_code/core/tool/todo/update_plan_tool.py +1 -1
- klaude_code/core/tool/tool_context.py +21 -4
- klaude_code/core/tool/tool_runner.py +5 -8
- klaude_code/core/tool/web/mermaid_tool.py +1 -4
- klaude_code/core/turn.py +40 -37
- klaude_code/llm/__init__.py +2 -12
- klaude_code/llm/anthropic/client.py +14 -44
- klaude_code/llm/client.py +2 -2
- klaude_code/llm/codex/client.py +4 -3
- klaude_code/llm/input_common.py +0 -6
- klaude_code/llm/openai_compatible/client.py +31 -74
- klaude_code/llm/openai_compatible/input.py +6 -4
- klaude_code/llm/openai_compatible/stream_processor.py +82 -0
- klaude_code/llm/openrouter/client.py +32 -62
- klaude_code/llm/openrouter/input.py +4 -27
- klaude_code/llm/registry.py +33 -7
- klaude_code/llm/responses/client.py +16 -48
- klaude_code/llm/responses/input.py +1 -1
- klaude_code/llm/usage.py +61 -11
- klaude_code/protocol/commands.py +1 -0
- klaude_code/protocol/events.py +11 -2
- klaude_code/protocol/model.py +147 -24
- klaude_code/protocol/op.py +1 -0
- klaude_code/protocol/sub_agent.py +5 -1
- klaude_code/session/export.py +56 -32
- klaude_code/session/session.py +43 -21
- klaude_code/session/templates/export_session.html +4 -1
- klaude_code/ui/core/input.py +1 -1
- klaude_code/ui/modes/repl/__init__.py +1 -5
- klaude_code/ui/modes/repl/clipboard.py +5 -5
- klaude_code/ui/modes/repl/event_handler.py +153 -54
- klaude_code/ui/modes/repl/renderer.py +4 -4
- klaude_code/ui/renderers/developer.py +35 -25
- klaude_code/ui/renderers/metadata.py +68 -30
- klaude_code/ui/renderers/tools.py +53 -87
- klaude_code/ui/rich/markdown.py +5 -5
- klaude_code/ui/terminal/control.py +2 -2
- klaude_code/version.py +3 -3
- {klaude_code-1.2.8.dist-info → klaude_code-1.2.10.dist-info}/METADATA +1 -1
- {klaude_code-1.2.8.dist-info → klaude_code-1.2.10.dist-info}/RECORD +82 -78
- {klaude_code-1.2.8.dist-info → klaude_code-1.2.10.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.8.dist-info → klaude_code-1.2.10.dist-info}/entry_points.txt +0 -0
klaude_code/cli/main.py
CHANGED
|
@@ -4,6 +4,7 @@ import os
|
|
|
4
4
|
import subprocess
|
|
5
5
|
import sys
|
|
6
6
|
import uuid
|
|
7
|
+
from importlib.metadata import PackageNotFoundError
|
|
7
8
|
from importlib.metadata import version as pkg_version
|
|
8
9
|
|
|
9
10
|
import typer
|
|
@@ -27,6 +28,9 @@ def _version_callback(value: bool) -> None:
|
|
|
27
28
|
if value:
|
|
28
29
|
try:
|
|
29
30
|
ver = pkg_version("klaude-code")
|
|
31
|
+
except PackageNotFoundError:
|
|
32
|
+
# Package is not installed or has no metadata; show a generic version string.
|
|
33
|
+
ver = "unknown"
|
|
30
34
|
except Exception:
|
|
31
35
|
ver = "unknown"
|
|
32
36
|
print(f"klaude-code {ver}")
|
|
@@ -232,8 +236,12 @@ def exec_command(
|
|
|
232
236
|
stdin = sys.stdin.read().rstrip("\n")
|
|
233
237
|
if stdin:
|
|
234
238
|
parts.append(stdin)
|
|
235
|
-
except
|
|
239
|
+
except (OSError, ValueError) as e:
|
|
240
|
+
# Expected I/O-related errors when reading from stdin (e.g. broken pipe, closed stream).
|
|
236
241
|
log((f"Error reading from stdin: {e}", "red"))
|
|
242
|
+
except Exception as e:
|
|
243
|
+
# Unexpected errors are still reported but kept from crashing the CLI.
|
|
244
|
+
log((f"Unexpected error reading from stdin: {e}", "red"))
|
|
237
245
|
|
|
238
246
|
if input_content:
|
|
239
247
|
parts.append(input_content)
|
|
@@ -333,6 +341,7 @@ def main_callback(
|
|
|
333
341
|
|
|
334
342
|
# Resolve session id before entering asyncio loop
|
|
335
343
|
session_id: str | None = None
|
|
344
|
+
is_new_session = False
|
|
336
345
|
if resume:
|
|
337
346
|
session_id = resume_select_session()
|
|
338
347
|
if session_id is None:
|
|
@@ -343,6 +352,7 @@ def main_callback(
|
|
|
343
352
|
# If still no session_id, generate a new one for a new session
|
|
344
353
|
if session_id is None:
|
|
345
354
|
session_id = uuid.uuid4().hex
|
|
355
|
+
is_new_session = True
|
|
346
356
|
|
|
347
357
|
debug_enabled, debug_filters = resolve_debug_settings(debug, debug_filter)
|
|
348
358
|
|
|
@@ -357,5 +367,6 @@ def main_callback(
|
|
|
357
367
|
run_interactive(
|
|
358
368
|
init_config=init_config,
|
|
359
369
|
session_id=session_id,
|
|
370
|
+
is_new_session=is_new_session,
|
|
360
371
|
)
|
|
361
372
|
)
|
klaude_code/cli/runtime.py
CHANGED
|
@@ -13,7 +13,6 @@ from klaude_code.config import Config, load_config
|
|
|
13
13
|
from klaude_code.core.agent import Agent, DefaultModelProfileProvider, VanillaModelProfileProvider
|
|
14
14
|
from klaude_code.core.executor import Executor
|
|
15
15
|
from klaude_code.core.manager import build_llm_clients
|
|
16
|
-
from klaude_code.core.tool import SkillLoader, SkillTool
|
|
17
16
|
from klaude_code.protocol import events, op
|
|
18
17
|
from klaude_code.protocol.model import UserInputPayload
|
|
19
18
|
from klaude_code.protocol.sub_agent import iter_sub_agent_profiles
|
|
@@ -96,11 +95,6 @@ async def initialize_app_components(init_config: AppInitConfig) -> AppComponents
|
|
|
96
95
|
if config is None:
|
|
97
96
|
raise typer.Exit(1)
|
|
98
97
|
|
|
99
|
-
# Initialize skills
|
|
100
|
-
skill_loader = SkillLoader()
|
|
101
|
-
skill_loader.discover_skills()
|
|
102
|
-
SkillTool.set_skill_loader(skill_loader)
|
|
103
|
-
|
|
104
98
|
# Initialize LLM clients
|
|
105
99
|
try:
|
|
106
100
|
enabled_sub_agents = [p.name for p in iter_sub_agent_profiles()]
|
|
@@ -216,8 +210,7 @@ async def run_exec(init_config: AppInitConfig, input_content: str) -> None:
|
|
|
216
210
|
# Generate a new session ID for exec mode
|
|
217
211
|
session_id = uuid.uuid4().hex
|
|
218
212
|
|
|
219
|
-
|
|
220
|
-
await components.executor.submit_and_wait(op.InitAgentOperation(session_id=session_id))
|
|
213
|
+
await components.executor.submit_and_wait(op.InitAgentOperation(session_id=session_id, is_new_session=True))
|
|
221
214
|
await components.event_queue.join()
|
|
222
215
|
|
|
223
216
|
# Submit the input content directly
|
|
@@ -231,7 +224,9 @@ async def run_exec(init_config: AppInitConfig, input_content: str) -> None:
|
|
|
231
224
|
await cleanup_app_components(components)
|
|
232
225
|
|
|
233
226
|
|
|
234
|
-
async def run_interactive(
|
|
227
|
+
async def run_interactive(
|
|
228
|
+
init_config: AppInitConfig, session_id: str | None = None, *, is_new_session: bool = False
|
|
229
|
+
) -> None:
|
|
235
230
|
"""Run the interactive REPL using the provided configuration."""
|
|
236
231
|
|
|
237
232
|
components = await initialize_app_components(init_config)
|
|
@@ -284,8 +279,9 @@ async def run_interactive(init_config: AppInitConfig, session_id: str | None = N
|
|
|
284
279
|
restore_sigint = install_sigint_double_press_exit(_show_toast_once, _hide_progress)
|
|
285
280
|
|
|
286
281
|
try:
|
|
287
|
-
|
|
288
|
-
|
|
282
|
+
await components.executor.submit_and_wait(
|
|
283
|
+
op.InitAgentOperation(session_id=session_id, is_new_session=is_new_session)
|
|
284
|
+
)
|
|
289
285
|
await components.event_queue.join()
|
|
290
286
|
# Input
|
|
291
287
|
await input_provider.start()
|
klaude_code/command/__init__.py
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
from .clear_cmd import ClearCommand
|
|
2
1
|
from .command_abc import CommandABC, CommandResult, InputAction, InputActionType
|
|
3
|
-
from .diff_cmd import DiffCommand
|
|
4
|
-
from .export_cmd import ExportCommand
|
|
5
|
-
from .help_cmd import HelpCommand
|
|
6
|
-
|
|
7
|
-
# InitCommand is now dynamically loaded via prompt_init.md
|
|
8
|
-
# from .init_cmd import InitCommand
|
|
9
|
-
from .model_cmd import ModelCommand
|
|
10
|
-
from .refresh_cmd import RefreshTerminalCommand
|
|
11
2
|
from .registry import (
|
|
12
3
|
dispatch_command,
|
|
13
4
|
get_commands,
|
|
@@ -16,21 +7,76 @@ from .registry import (
|
|
|
16
7
|
load_prompt_commands,
|
|
17
8
|
register_command,
|
|
18
9
|
)
|
|
19
|
-
from .status_cmd import StatusCommand
|
|
20
|
-
from .terminal_setup_cmd import TerminalSetupCommand
|
|
21
10
|
|
|
22
|
-
#
|
|
23
|
-
|
|
11
|
+
# Lazy load commands to avoid heavy imports at module load time
|
|
12
|
+
_commands_loaded = False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def ensure_commands_loaded() -> None:
|
|
16
|
+
"""Ensure all commands are loaded (lazy initialization).
|
|
17
|
+
|
|
18
|
+
This function is called internally by registry functions like get_commands(),
|
|
19
|
+
dispatch_command(), etc. It can also be called explicitly if early loading is desired.
|
|
20
|
+
"""
|
|
21
|
+
global _commands_loaded
|
|
22
|
+
if _commands_loaded:
|
|
23
|
+
return
|
|
24
|
+
_commands_loaded = True
|
|
25
|
+
|
|
26
|
+
# Import command modules to trigger @register_command decorators
|
|
27
|
+
from . import clear_cmd as _clear_cmd # noqa: F401
|
|
28
|
+
from . import diff_cmd as _diff_cmd # noqa: F401
|
|
29
|
+
from . import export_cmd as _export_cmd # noqa: F401
|
|
30
|
+
from . import help_cmd as _help_cmd # noqa: F401
|
|
31
|
+
from . import model_cmd as _model_cmd # noqa: F401
|
|
32
|
+
from . import refresh_cmd as _refresh_cmd # noqa: F401
|
|
33
|
+
from . import release_notes_cmd as _release_notes_cmd # noqa: F401
|
|
34
|
+
from . import status_cmd as _status_cmd # noqa: F401
|
|
35
|
+
from . import terminal_setup_cmd as _terminal_setup_cmd # noqa: F401
|
|
36
|
+
|
|
37
|
+
# Suppress unused variable warnings
|
|
38
|
+
_ = (
|
|
39
|
+
_clear_cmd,
|
|
40
|
+
_diff_cmd,
|
|
41
|
+
_export_cmd,
|
|
42
|
+
_help_cmd,
|
|
43
|
+
_model_cmd,
|
|
44
|
+
_refresh_cmd,
|
|
45
|
+
_release_notes_cmd,
|
|
46
|
+
_status_cmd,
|
|
47
|
+
_terminal_setup_cmd,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Load prompt-based commands
|
|
51
|
+
load_prompt_commands()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Lazy accessors for command classes
|
|
55
|
+
def __getattr__(name: str) -> object:
|
|
56
|
+
_commands_map = {
|
|
57
|
+
"ClearCommand": "clear_cmd",
|
|
58
|
+
"DiffCommand": "diff_cmd",
|
|
59
|
+
"ExportCommand": "export_cmd",
|
|
60
|
+
"HelpCommand": "help_cmd",
|
|
61
|
+
"ModelCommand": "model_cmd",
|
|
62
|
+
"RefreshTerminalCommand": "refresh_cmd",
|
|
63
|
+
"ReleaseNotesCommand": "release_notes_cmd",
|
|
64
|
+
"StatusCommand": "status_cmd",
|
|
65
|
+
"TerminalSetupCommand": "terminal_setup_cmd",
|
|
66
|
+
}
|
|
67
|
+
if name in _commands_map:
|
|
68
|
+
import importlib
|
|
69
|
+
|
|
70
|
+
module = importlib.import_module(f".{_commands_map[name]}", __package__)
|
|
71
|
+
return getattr(module, name)
|
|
72
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
73
|
+
|
|
24
74
|
|
|
25
75
|
__all__ = [
|
|
26
|
-
|
|
27
|
-
"DiffCommand",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"ExportCommand",
|
|
31
|
-
"RefreshTerminalCommand",
|
|
32
|
-
"StatusCommand",
|
|
33
|
-
"TerminalSetupCommand",
|
|
76
|
+
# Command classes are lazily loaded via __getattr__
|
|
77
|
+
# "ClearCommand", "DiffCommand", "HelpCommand", "ModelCommand",
|
|
78
|
+
# "ExportCommand", "RefreshTerminalCommand", "ReleaseNotesCommand",
|
|
79
|
+
# "StatusCommand", "TerminalSetupCommand",
|
|
34
80
|
"register_command",
|
|
35
81
|
"CommandABC",
|
|
36
82
|
"CommandResult",
|
|
@@ -40,4 +86,5 @@ __all__ = [
|
|
|
40
86
|
"get_commands",
|
|
41
87
|
"is_slash_command_name",
|
|
42
88
|
"has_interactive_command",
|
|
89
|
+
"ensure_commands_loaded",
|
|
43
90
|
]
|
klaude_code/command/clear_cmd.py
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
1
3
|
from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
|
|
2
4
|
from klaude_code.command.registry import register_command
|
|
3
|
-
from klaude_code.core.agent import Agent
|
|
4
5
|
from klaude_code.protocol import commands
|
|
5
6
|
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from klaude_code.core.agent import Agent
|
|
9
|
+
|
|
6
10
|
|
|
7
11
|
@register_command
|
|
8
12
|
class ClearCommand(CommandABC):
|
|
@@ -16,5 +20,5 @@ class ClearCommand(CommandABC):
|
|
|
16
20
|
def summary(self) -> str:
|
|
17
21
|
return "Clear conversation history and free up context"
|
|
18
22
|
|
|
19
|
-
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
23
|
+
async def run(self, raw: str, agent: "Agent") -> CommandResult:
|
|
20
24
|
return CommandResult(actions=[InputAction.clear()])
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from enum import Enum
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
3
4
|
|
|
4
5
|
from pydantic import BaseModel
|
|
5
6
|
|
|
6
|
-
from klaude_code.core.agent import Agent
|
|
7
7
|
from klaude_code.protocol import commands
|
|
8
8
|
from klaude_code.protocol import events as protocol_events
|
|
9
9
|
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from klaude_code.core.agent import Agent
|
|
12
|
+
|
|
10
13
|
|
|
11
14
|
class InputActionType(str, Enum):
|
|
12
15
|
"""Supported input action kinds."""
|
|
@@ -78,7 +81,7 @@ class CommandABC(ABC):
|
|
|
78
81
|
return False
|
|
79
82
|
|
|
80
83
|
@abstractmethod
|
|
81
|
-
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
84
|
+
async def run(self, raw: str, agent: "Agent") -> CommandResult:
|
|
82
85
|
"""
|
|
83
86
|
Execute the command.
|
|
84
87
|
|
klaude_code/command/diff_cmd.py
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import subprocess
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
3
4
|
|
|
4
5
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
5
6
|
from klaude_code.command.registry import register_command
|
|
6
|
-
from klaude_code.core.agent import Agent
|
|
7
7
|
from klaude_code.protocol import commands, events, model
|
|
8
8
|
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from klaude_code.core.agent import Agent
|
|
11
|
+
|
|
9
12
|
|
|
10
13
|
@register_command
|
|
11
14
|
class DiffCommand(CommandABC):
|
|
@@ -19,7 +22,7 @@ class DiffCommand(CommandABC):
|
|
|
19
22
|
def summary(self) -> str:
|
|
20
23
|
return "Show git diff"
|
|
21
24
|
|
|
22
|
-
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
25
|
+
async def run(self, raw: str, agent: "Agent") -> CommandResult:
|
|
23
26
|
try:
|
|
24
27
|
# Check if current directory is in a git repository
|
|
25
28
|
git_check = subprocess.run(
|
|
@@ -2,13 +2,16 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import subprocess
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
5
6
|
|
|
6
7
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
7
8
|
from klaude_code.command.registry import register_command
|
|
8
|
-
from klaude_code.core.agent import Agent
|
|
9
9
|
from klaude_code.protocol import commands, events, model
|
|
10
10
|
from klaude_code.session.export import build_export_html, get_default_export_path
|
|
11
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from klaude_code.core.agent import Agent
|
|
14
|
+
|
|
12
15
|
|
|
13
16
|
@register_command
|
|
14
17
|
class ExportCommand(CommandABC):
|
|
@@ -30,7 +33,7 @@ class ExportCommand(CommandABC):
|
|
|
30
33
|
def is_interactive(self) -> bool:
|
|
31
34
|
return False
|
|
32
35
|
|
|
33
|
-
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
36
|
+
async def run(self, raw: str, agent: "Agent") -> CommandResult:
|
|
34
37
|
try:
|
|
35
38
|
output_path = self._resolve_output_path(raw, agent)
|
|
36
39
|
html_doc = self._build_html(agent)
|
|
@@ -57,7 +60,7 @@ class ExportCommand(CommandABC):
|
|
|
57
60
|
)
|
|
58
61
|
return CommandResult(events=[event])
|
|
59
62
|
|
|
60
|
-
def _resolve_output_path(self, raw: str, agent: Agent) -> Path:
|
|
63
|
+
def _resolve_output_path(self, raw: str, agent: "Agent") -> Path:
|
|
61
64
|
trimmed = raw.strip()
|
|
62
65
|
if trimmed:
|
|
63
66
|
candidate = Path(trimmed).expanduser()
|
|
@@ -78,7 +81,7 @@ class ExportCommand(CommandABC):
|
|
|
78
81
|
msg = f"Failed to open HTML with `open`: {exc}"
|
|
79
82
|
raise RuntimeError(msg) from exc
|
|
80
83
|
|
|
81
|
-
def _build_html(self, agent: Agent) -> str:
|
|
84
|
+
def _build_html(self, agent: "Agent") -> str:
|
|
82
85
|
profile = agent.profile
|
|
83
86
|
system_prompt = (profile.system_prompt if profile else "") or ""
|
|
84
87
|
tools = profile.tools if profile else []
|
klaude_code/command/help_cmd.py
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
1
3
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
2
4
|
from klaude_code.command.registry import register_command
|
|
3
|
-
from klaude_code.core.agent import Agent
|
|
4
5
|
from klaude_code.protocol import commands, events, model
|
|
5
6
|
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from klaude_code.core.agent import Agent
|
|
9
|
+
|
|
6
10
|
|
|
7
11
|
@register_command
|
|
8
12
|
class HelpCommand(CommandABC):
|
|
@@ -16,7 +20,7 @@ class HelpCommand(CommandABC):
|
|
|
16
20
|
def summary(self) -> str:
|
|
17
21
|
return "Show help and available commands"
|
|
18
22
|
|
|
19
|
-
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
23
|
+
async def run(self, raw: str, agent: "Agent") -> CommandResult:
|
|
20
24
|
lines: list[str] = [
|
|
21
25
|
"""
|
|
22
26
|
Usage:
|
klaude_code/command/model_cmd.py
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
2
3
|
|
|
3
4
|
from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
|
|
4
5
|
from klaude_code.command.registry import register_command
|
|
5
6
|
from klaude_code.config import select_model_from_config
|
|
6
|
-
from klaude_code.core.agent import Agent
|
|
7
7
|
from klaude_code.protocol import commands, events, model
|
|
8
8
|
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from klaude_code.core.agent import Agent
|
|
11
|
+
|
|
9
12
|
|
|
10
13
|
@register_command
|
|
11
14
|
class ModelCommand(CommandABC):
|
|
@@ -23,7 +26,7 @@ class ModelCommand(CommandABC):
|
|
|
23
26
|
def is_interactive(self) -> bool:
|
|
24
27
|
return True
|
|
25
28
|
|
|
26
|
-
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
29
|
+
async def run(self, raw: str, agent: "Agent") -> CommandResult:
|
|
27
30
|
selected_model = await asyncio.to_thread(select_model_from_config, preferred=raw)
|
|
28
31
|
|
|
29
32
|
current_model = agent.profile.llm_client.model_name if agent.profile else None
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Remove AI code slop
|
|
3
|
+
from: https://cursor.com/cn/link/command?name=deslop&text=%23%20Remove%20AI%20code%20slop%0A%0ACheck%20the%20diff%20against%20main%2C%20and%20remove%20all%20AI%20generated%20slop%20introduced%20in%20this%20branch.%0A%0AThis%20includes%3A%0A-%20Extra%20comments%20that%20a%20human%20wouldn%27t%20add%20or%20is%20inconsistent%20with%20the%20rest%20of%20the%20file%0A-%20Extra%20defensive%20checks%20or%20try%2Fcatch%20blocks%20that%20are%20abnormal%20for%20that%20area%20of%20the%20codebase%20(especially%20if%20called%20by%20trusted%20%2F%20validated%20codepaths)%0A-%20Casts%20to%20any%20to%20get%20around%20type%20issues%0A-%20Any%20other%20style%20that%20is%20inconsistent%20with%20the%20file%0A%0AReport%20at%20the%20end%20with%20only%20a%201-3%20sentence%20summary%20of%20what%20you%20changed
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Check the diff against main, and remove all AI generated slop introduced in this branch.
|
|
7
|
+
|
|
8
|
+
This includes:
|
|
9
|
+
- Extra comments that a human wouldn't add or is inconsistent with the rest of the file
|
|
10
|
+
- Extra defensive checks or try/catch blocks that are abnormal for that area of the codebase (especially if called by trusted / validated codepaths)
|
|
11
|
+
- Casts to any to get around type issues
|
|
12
|
+
- Any other style that is inconsistent with the file
|
|
13
|
+
|
|
14
|
+
Report at the end with only a 1-3 sentence summary of what you changed
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
from importlib.resources import files
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
2
3
|
|
|
3
4
|
import yaml
|
|
4
5
|
|
|
5
6
|
from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
|
|
6
|
-
from klaude_code.core.agent import Agent
|
|
7
7
|
from klaude_code.protocol import commands
|
|
8
|
+
from klaude_code.trace import log_debug
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from klaude_code.core.agent import Agent
|
|
8
12
|
|
|
9
13
|
|
|
10
14
|
class PromptCommand(CommandABC):
|
|
@@ -41,7 +45,8 @@ class PromptCommand(CommandABC):
|
|
|
41
45
|
|
|
42
46
|
self._metadata = {}
|
|
43
47
|
self._content = raw_text
|
|
44
|
-
except
|
|
48
|
+
except (OSError, yaml.YAMLError) as e:
|
|
49
|
+
log_debug(f"Failed to load prompt template {self.template_name}: {e}")
|
|
45
50
|
self._metadata = {"description": "Error loading template"}
|
|
46
51
|
self._content = f"Error loading template: {self.template_name}"
|
|
47
52
|
|
|
@@ -54,7 +59,7 @@ class PromptCommand(CommandABC):
|
|
|
54
59
|
def support_addition_params(self) -> bool:
|
|
55
60
|
return True
|
|
56
61
|
|
|
57
|
-
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
62
|
+
async def run(self, raw: str, agent: "Agent") -> CommandResult:
|
|
58
63
|
self._ensure_loaded()
|
|
59
64
|
template_content = self._content or ""
|
|
60
65
|
user_input = raw.strip() or "<none>"
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
1
3
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
2
4
|
from klaude_code.command.registry import register_command
|
|
3
|
-
from klaude_code.core.agent import Agent
|
|
4
5
|
from klaude_code.protocol import commands, events
|
|
5
6
|
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from klaude_code.core.agent import Agent
|
|
9
|
+
|
|
6
10
|
|
|
7
11
|
@register_command
|
|
8
12
|
class RefreshTerminalCommand(CommandABC):
|
|
@@ -20,7 +24,7 @@ class RefreshTerminalCommand(CommandABC):
|
|
|
20
24
|
def is_interactive(self) -> bool:
|
|
21
25
|
return True
|
|
22
26
|
|
|
23
|
-
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
27
|
+
async def run(self, raw: str, agent: "Agent") -> CommandResult:
|
|
24
28
|
import os
|
|
25
29
|
|
|
26
30
|
os.system("cls" if os.name == "nt" else "clear")
|
klaude_code/command/registry.py
CHANGED
|
@@ -3,10 +3,12 @@ from typing import TYPE_CHECKING, TypeVar
|
|
|
3
3
|
|
|
4
4
|
from klaude_code.command.command_abc import CommandResult, InputAction
|
|
5
5
|
from klaude_code.command.prompt_command import PromptCommand
|
|
6
|
-
from klaude_code.core.agent import Agent
|
|
7
6
|
from klaude_code.protocol import commands, events, model
|
|
7
|
+
from klaude_code.trace import log_debug
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
|
+
from klaude_code.core.agent import Agent
|
|
11
|
+
|
|
10
12
|
from .command_abc import CommandABC
|
|
11
13
|
|
|
12
14
|
_COMMANDS: dict[commands.CommandName | str, "CommandABC"] = {}
|
|
@@ -30,21 +32,30 @@ def load_prompt_commands():
|
|
|
30
32
|
if (name.startswith("prompt_") or name.startswith("prompt-")) and name.endswith(".md"):
|
|
31
33
|
cmd = PromptCommand(name)
|
|
32
34
|
_COMMANDS[cmd.name] = cmd
|
|
33
|
-
except
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
except OSError as e:
|
|
36
|
+
log_debug(f"Failed to load prompt commands: {e}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _ensure_commands_loaded() -> None:
|
|
40
|
+
"""Ensure all commands are loaded (lazy initialization)."""
|
|
41
|
+
from klaude_code.command import ensure_commands_loaded
|
|
42
|
+
|
|
43
|
+
ensure_commands_loaded()
|
|
36
44
|
|
|
37
45
|
|
|
38
46
|
def get_commands() -> dict[commands.CommandName | str, "CommandABC"]:
|
|
39
47
|
"""Get all registered commands."""
|
|
48
|
+
_ensure_commands_loaded()
|
|
40
49
|
return _COMMANDS.copy()
|
|
41
50
|
|
|
42
51
|
|
|
43
52
|
def is_slash_command_name(name: str) -> bool:
|
|
53
|
+
_ensure_commands_loaded()
|
|
44
54
|
return name in _COMMANDS
|
|
45
55
|
|
|
46
56
|
|
|
47
|
-
async def dispatch_command(raw: str, agent: Agent) -> CommandResult:
|
|
57
|
+
async def dispatch_command(raw: str, agent: "Agent") -> CommandResult:
|
|
58
|
+
_ensure_commands_loaded()
|
|
48
59
|
# Detect command name
|
|
49
60
|
if not raw.startswith("/"):
|
|
50
61
|
return CommandResult(actions=[InputAction.run_agent(raw)])
|
|
@@ -96,6 +107,7 @@ async def dispatch_command(raw: str, agent: Agent) -> CommandResult:
|
|
|
96
107
|
|
|
97
108
|
|
|
98
109
|
def has_interactive_command(raw: str) -> bool:
|
|
110
|
+
_ensure_commands_loaded()
|
|
99
111
|
if not raw.startswith("/"):
|
|
100
112
|
return False
|
|
101
113
|
splits = raw.split(" ", maxsplit=1)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
5
|
+
from klaude_code.command.registry import register_command
|
|
6
|
+
from klaude_code.protocol import commands, events, model
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from klaude_code.core.agent import Agent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _read_changelog() -> str:
|
|
13
|
+
"""Read CHANGELOG.md from project root."""
|
|
14
|
+
changelog_path = Path(__file__).parent.parent.parent.parent / "CHANGELOG.md"
|
|
15
|
+
if not changelog_path.exists():
|
|
16
|
+
return "CHANGELOG.md not found"
|
|
17
|
+
return changelog_path.read_text(encoding="utf-8")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _extract_releases(changelog: str, count: int = 1) -> str:
|
|
21
|
+
"""Extract release sections from changelog in reverse order (oldest first).
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
changelog: The full changelog content.
|
|
25
|
+
count: Number of releases to extract (default 1).
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
The content of the specified number of releases, with newest at bottom.
|
|
29
|
+
"""
|
|
30
|
+
lines = changelog.split("\n")
|
|
31
|
+
releases: list[list[str]] = []
|
|
32
|
+
current_release: list[str] = []
|
|
33
|
+
version_count = 0
|
|
34
|
+
|
|
35
|
+
for line in lines:
|
|
36
|
+
# Skip [Unreleased] section header
|
|
37
|
+
if line.startswith("## [Unreleased]"):
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
# Check for version header (e.g., ## [1.2.8] - 2025-12-01)
|
|
41
|
+
if line.startswith("## [") and "]" in line:
|
|
42
|
+
if current_release:
|
|
43
|
+
releases.append(current_release)
|
|
44
|
+
version_count += 1
|
|
45
|
+
if version_count > count:
|
|
46
|
+
break
|
|
47
|
+
current_release = [line]
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
if version_count > 0:
|
|
51
|
+
current_release.append(line)
|
|
52
|
+
|
|
53
|
+
# Append the last release if exists
|
|
54
|
+
if current_release and version_count <= count:
|
|
55
|
+
releases.append(current_release)
|
|
56
|
+
|
|
57
|
+
if not releases:
|
|
58
|
+
return "No release notes found"
|
|
59
|
+
|
|
60
|
+
# Reverse to show oldest first, newest last
|
|
61
|
+
releases.reverse()
|
|
62
|
+
return "\n".join("\n".join(release) for release in releases).strip()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@register_command
|
|
66
|
+
class ReleaseNotesCommand(CommandABC):
|
|
67
|
+
"""Display the latest release notes from CHANGELOG.md."""
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def name(self) -> commands.CommandName:
|
|
71
|
+
return commands.CommandName.RELEASE_NOTES
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def summary(self) -> str:
|
|
75
|
+
return "Show the latest release notes"
|
|
76
|
+
|
|
77
|
+
async def run(self, raw: str, agent: "Agent") -> CommandResult:
|
|
78
|
+
changelog = _read_changelog()
|
|
79
|
+
content = _extract_releases(changelog, count=10)
|
|
80
|
+
|
|
81
|
+
event = events.DeveloperMessageEvent(
|
|
82
|
+
session_id=agent.session.id,
|
|
83
|
+
item=model.DeveloperMessageItem(
|
|
84
|
+
content=content,
|
|
85
|
+
command_output=model.CommandOutput(command_name=self.name),
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return CommandResult(events=[event])
|