klaude-code 1.2.9__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.
Files changed (55) hide show
  1. klaude_code/cli/main.py +12 -1
  2. klaude_code/cli/runtime.py +7 -11
  3. klaude_code/command/__init__.py +68 -23
  4. klaude_code/command/clear_cmd.py +6 -2
  5. klaude_code/command/command_abc.py +5 -2
  6. klaude_code/command/diff_cmd.py +5 -2
  7. klaude_code/command/export_cmd.py +7 -4
  8. klaude_code/command/help_cmd.py +6 -2
  9. klaude_code/command/model_cmd.py +5 -2
  10. klaude_code/command/prompt_command.py +8 -3
  11. klaude_code/command/refresh_cmd.py +6 -2
  12. klaude_code/command/registry.py +17 -5
  13. klaude_code/command/release_notes_cmd.py +5 -2
  14. klaude_code/command/status_cmd.py +8 -4
  15. klaude_code/command/terminal_setup_cmd.py +7 -4
  16. klaude_code/const/__init__.py +1 -1
  17. klaude_code/core/agent.py +55 -9
  18. klaude_code/core/executor.py +2 -2
  19. klaude_code/core/manager/agent_manager.py +6 -7
  20. klaude_code/core/manager/llm_clients.py +47 -22
  21. klaude_code/core/manager/llm_clients_builder.py +19 -7
  22. klaude_code/core/manager/sub_agent_manager.py +1 -1
  23. klaude_code/core/reminders.py +0 -3
  24. klaude_code/core/task.py +2 -2
  25. klaude_code/core/tool/file/_utils.py +30 -0
  26. klaude_code/core/tool/file/edit_tool.py +5 -30
  27. klaude_code/core/tool/file/multi_edit_tool.py +6 -31
  28. klaude_code/core/tool/file/read_tool.py +6 -18
  29. klaude_code/core/tool/file/write_tool.py +5 -30
  30. klaude_code/core/tool/memory/__init__.py +5 -0
  31. klaude_code/core/tool/memory/skill_loader.py +2 -1
  32. klaude_code/core/tool/memory/skill_tool.py +13 -0
  33. klaude_code/llm/__init__.py +2 -12
  34. klaude_code/llm/anthropic/client.py +2 -1
  35. klaude_code/llm/client.py +1 -1
  36. klaude_code/llm/codex/client.py +1 -1
  37. klaude_code/llm/openai_compatible/client.py +3 -2
  38. klaude_code/llm/openrouter/client.py +3 -3
  39. klaude_code/llm/registry.py +33 -7
  40. klaude_code/llm/responses/client.py +2 -1
  41. klaude_code/llm/responses/input.py +1 -1
  42. klaude_code/llm/usage.py +17 -8
  43. klaude_code/protocol/model.py +12 -7
  44. klaude_code/protocol/op.py +1 -0
  45. klaude_code/session/export.py +5 -5
  46. klaude_code/session/session.py +15 -5
  47. klaude_code/ui/core/input.py +1 -1
  48. klaude_code/ui/modes/repl/clipboard.py +5 -5
  49. klaude_code/ui/renderers/metadata.py +1 -1
  50. klaude_code/ui/terminal/control.py +2 -2
  51. klaude_code/version.py +3 -3
  52. {klaude_code-1.2.9.dist-info → klaude_code-1.2.10.dist-info}/METADATA +1 -1
  53. {klaude_code-1.2.9.dist-info → klaude_code-1.2.10.dist-info}/RECORD +55 -54
  54. {klaude_code-1.2.9.dist-info → klaude_code-1.2.10.dist-info}/WHEEL +0 -0
  55. {klaude_code-1.2.9.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 Exception as e:
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
  )
@@ -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
- # Init Agent
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(init_config: AppInitConfig, session_id: str | None = None) -> None:
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
- # Init Agent
288
- await components.executor.submit_and_wait(op.InitAgentOperation(session_id=session_id))
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()
@@ -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,23 +7,76 @@ from .registry import (
16
7
  load_prompt_commands,
17
8
  register_command,
18
9
  )
19
- from .release_notes_cmd import ReleaseNotesCommand
20
- from .status_cmd import StatusCommand
21
- from .terminal_setup_cmd import TerminalSetupCommand
22
10
 
23
- # Dynamically load prompt commands
24
- load_prompt_commands()
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
+
25
74
 
26
75
  __all__ = [
27
- "ClearCommand",
28
- "DiffCommand",
29
- "HelpCommand",
30
- "ModelCommand",
31
- "ExportCommand",
32
- "RefreshTerminalCommand",
33
- "ReleaseNotesCommand",
34
- "StatusCommand",
35
- "TerminalSetupCommand",
76
+ # Command classes are lazily loaded via __getattr__
77
+ # "ClearCommand", "DiffCommand", "HelpCommand", "ModelCommand",
78
+ # "ExportCommand", "RefreshTerminalCommand", "ReleaseNotesCommand",
79
+ # "StatusCommand", "TerminalSetupCommand",
36
80
  "register_command",
37
81
  "CommandABC",
38
82
  "CommandResult",
@@ -42,4 +86,5 @@ __all__ = [
42
86
  "get_commands",
43
87
  "is_slash_command_name",
44
88
  "has_interactive_command",
89
+ "ensure_commands_loaded",
45
90
  ]
@@ -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
 
@@ -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 []
@@ -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:
@@ -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
@@ -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 Exception:
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")
@@ -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 Exception:
34
- # If resource loading fails, just ignore
35
- pass
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)
@@ -1,10 +1,13 @@
1
1
  from pathlib import Path
2
+ from typing import TYPE_CHECKING
2
3
 
3
4
  from klaude_code.command.command_abc import CommandABC, CommandResult
4
5
  from klaude_code.command.registry import register_command
5
- from klaude_code.core.agent import Agent
6
6
  from klaude_code.protocol import commands, events, model
7
7
 
8
+ if TYPE_CHECKING:
9
+ from klaude_code.core.agent import Agent
10
+
8
11
 
9
12
  def _read_changelog() -> str:
10
13
  """Read CHANGELOG.md from project root."""
@@ -71,7 +74,7 @@ class ReleaseNotesCommand(CommandABC):
71
74
  def summary(self) -> str:
72
75
  return "Show the latest release notes"
73
76
 
74
- async def run(self, raw: str, agent: Agent) -> CommandResult:
77
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
75
78
  changelog = _read_changelog()
76
79
  content = _extract_releases(changelog, count=10)
77
80
 
@@ -1,9 +1,13 @@
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
  from klaude_code.session.session import Session
6
7
 
8
+ if TYPE_CHECKING:
9
+ from klaude_code.core.agent import Agent
10
+
7
11
 
8
12
  class AggregatedUsage(model.BaseModel):
9
13
  """Aggregated usage statistics including per-model breakdown."""
@@ -56,8 +60,8 @@ def accumulate_session_usage(session: Session) -> AggregatedUsage:
56
60
  total.cache_read_cost = (total.cache_read_cost or 0.0) + usage.cache_read_cost
57
61
 
58
62
  # Track peak context window size (max across all tasks)
59
- if usage.context_window_size is not None:
60
- total.context_window_size = usage.context_window_size
63
+ if usage.context_token is not None:
64
+ total.context_token = usage.context_token
61
65
 
62
66
  # Keep the latest context_limit for computed context_usage_percent
63
67
  if usage.context_limit is not None:
@@ -135,7 +139,7 @@ class StatusCommand(CommandABC):
135
139
  def summary(self) -> str:
136
140
  return "Show session usage statistics"
137
141
 
138
- async def run(self, raw: str, agent: Agent) -> CommandResult:
142
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
139
143
  session = agent.session
140
144
  aggregated = accumulate_session_usage(session)
141
145
 
@@ -1,12 +1,15 @@
1
1
  import os
2
2
  import subprocess
3
3
  from pathlib import Path
4
+ from typing import TYPE_CHECKING
4
5
 
5
6
  from klaude_code.command.command_abc import CommandABC, CommandResult
6
7
  from klaude_code.command.registry import register_command
7
- from klaude_code.core.agent import Agent
8
8
  from klaude_code.protocol import commands, events, model
9
9
 
10
+ if TYPE_CHECKING:
11
+ from klaude_code.core.agent import Agent
12
+
10
13
 
11
14
  @register_command
12
15
  class TerminalSetupCommand(CommandABC):
@@ -24,7 +27,7 @@ class TerminalSetupCommand(CommandABC):
24
27
  def is_interactive(self) -> bool:
25
28
  return False
26
29
 
27
- async def run(self, raw: str, agent: Agent) -> CommandResult:
30
+ async def run(self, raw: str, agent: "Agent") -> CommandResult:
28
31
  term_program = os.environ.get("TERM_PROGRAM", "").lower()
29
32
 
30
33
  try:
@@ -223,7 +226,7 @@ class TerminalSetupCommand(CommandABC):
223
226
 
224
227
  return message
225
228
 
226
- def _create_success_result(self, agent: Agent, message: str) -> CommandResult:
229
+ def _create_success_result(self, agent: "Agent", message: str) -> CommandResult:
227
230
  """Create success result"""
228
231
  return CommandResult(
229
232
  events=[
@@ -237,7 +240,7 @@ class TerminalSetupCommand(CommandABC):
237
240
  ]
238
241
  )
239
242
 
240
- def _create_error_result(self, agent: Agent, message: str) -> CommandResult:
243
+ def _create_error_result(self, agent: "Agent", message: str) -> CommandResult:
241
244
  """Create error result"""
242
245
  return CommandResult(
243
246
  events=[
@@ -91,7 +91,7 @@ INVALID_TOOL_CALL_MAX_LENGTH = 500
91
91
  TRUNCATE_DISPLAY_MAX_LINE_LENGTH = 1000
92
92
 
93
93
  # Maximum lines for truncated display output
94
- TRUNCATE_DISPLAY_MAX_LINES = 10
94
+ TRUNCATE_DISPLAY_MAX_LINES = 20
95
95
 
96
96
  # Maximum lines for sub-agent result display
97
97
  SUB_AGENT_RESULT_MAX_LINES = 12