klaude-code 1.2.6__py3-none-any.whl → 1.8.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/__init__.py +24 -0
- klaude_code/auth/codex/__init__.py +20 -0
- klaude_code/auth/codex/exceptions.py +17 -0
- klaude_code/auth/codex/jwt_utils.py +45 -0
- klaude_code/auth/codex/oauth.py +229 -0
- klaude_code/auth/codex/token_manager.py +84 -0
- klaude_code/cli/auth_cmd.py +73 -0
- klaude_code/cli/config_cmd.py +91 -0
- klaude_code/cli/cost_cmd.py +338 -0
- klaude_code/cli/debug.py +78 -0
- klaude_code/cli/list_model.py +307 -0
- klaude_code/cli/main.py +233 -134
- klaude_code/cli/runtime.py +309 -117
- klaude_code/{version.py → cli/self_update.py} +114 -5
- klaude_code/cli/session_cmd.py +37 -21
- klaude_code/command/__init__.py +88 -27
- klaude_code/command/clear_cmd.py +8 -7
- klaude_code/command/command_abc.py +31 -31
- klaude_code/command/debug_cmd.py +79 -0
- klaude_code/command/export_cmd.py +19 -53
- klaude_code/command/export_online_cmd.py +154 -0
- klaude_code/command/fork_session_cmd.py +267 -0
- klaude_code/command/help_cmd.py +7 -8
- klaude_code/command/model_cmd.py +60 -10
- klaude_code/command/model_select.py +84 -0
- klaude_code/command/prompt-jj-describe.md +32 -0
- klaude_code/command/prompt_command.py +19 -11
- klaude_code/command/refresh_cmd.py +8 -10
- klaude_code/command/registry.py +139 -40
- klaude_code/command/release_notes_cmd.py +84 -0
- klaude_code/command/resume_cmd.py +111 -0
- klaude_code/command/status_cmd.py +104 -60
- klaude_code/command/terminal_setup_cmd.py +7 -9
- klaude_code/command/thinking_cmd.py +98 -0
- klaude_code/config/__init__.py +14 -6
- klaude_code/config/assets/__init__.py +1 -0
- klaude_code/config/assets/builtin_config.yaml +303 -0
- klaude_code/config/builtin_config.py +38 -0
- klaude_code/config/config.py +378 -109
- klaude_code/config/select_model.py +117 -53
- klaude_code/config/thinking.py +269 -0
- klaude_code/{const/__init__.py → const.py} +50 -19
- klaude_code/core/agent.py +20 -28
- klaude_code/core/executor.py +327 -112
- klaude_code/core/manager/__init__.py +2 -4
- klaude_code/core/manager/llm_clients.py +1 -15
- klaude_code/core/manager/llm_clients_builder.py +10 -11
- klaude_code/core/manager/sub_agent_manager.py +37 -6
- klaude_code/core/prompt.py +63 -44
- klaude_code/core/prompts/prompt-claude-code.md +2 -13
- klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +117 -0
- klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +117 -0
- klaude_code/core/prompts/prompt-codex.md +9 -42
- klaude_code/core/prompts/prompt-minimal.md +12 -0
- klaude_code/core/prompts/{prompt-subagent-explore.md → prompt-sub-agent-explore.md} +16 -3
- klaude_code/core/prompts/{prompt-subagent-oracle.md → prompt-sub-agent-oracle.md} +1 -2
- klaude_code/core/prompts/prompt-sub-agent-web.md +51 -0
- klaude_code/core/reminders.py +283 -95
- klaude_code/core/task.py +113 -75
- klaude_code/core/tool/__init__.py +24 -31
- klaude_code/core/tool/file/_utils.py +36 -0
- klaude_code/core/tool/file/apply_patch.py +17 -25
- klaude_code/core/tool/file/apply_patch_tool.py +57 -77
- klaude_code/core/tool/file/diff_builder.py +151 -0
- klaude_code/core/tool/file/edit_tool.py +50 -63
- klaude_code/core/tool/file/move_tool.md +41 -0
- klaude_code/core/tool/file/move_tool.py +435 -0
- klaude_code/core/tool/file/read_tool.md +1 -1
- klaude_code/core/tool/file/read_tool.py +86 -86
- klaude_code/core/tool/file/write_tool.py +59 -69
- klaude_code/core/tool/report_back_tool.py +84 -0
- klaude_code/core/tool/shell/bash_tool.py +265 -22
- klaude_code/core/tool/shell/command_safety.py +3 -6
- klaude_code/core/tool/{memory → skill}/skill_tool.py +16 -26
- klaude_code/core/tool/sub_agent_tool.py +13 -2
- klaude_code/core/tool/todo/todo_write_tool.md +0 -157
- klaude_code/core/tool/todo/todo_write_tool.py +1 -1
- klaude_code/core/tool/todo/todo_write_tool_raw.md +182 -0
- klaude_code/core/tool/todo/update_plan_tool.py +1 -1
- klaude_code/core/tool/tool_abc.py +18 -0
- klaude_code/core/tool/tool_context.py +27 -12
- klaude_code/core/tool/tool_registry.py +7 -7
- klaude_code/core/tool/tool_runner.py +44 -36
- klaude_code/core/tool/truncation.py +29 -14
- klaude_code/core/tool/web/mermaid_tool.md +43 -0
- klaude_code/core/tool/web/mermaid_tool.py +2 -5
- klaude_code/core/tool/web/web_fetch_tool.md +1 -1
- klaude_code/core/tool/web/web_fetch_tool.py +112 -22
- klaude_code/core/tool/web/web_search_tool.md +23 -0
- klaude_code/core/tool/web/web_search_tool.py +130 -0
- klaude_code/core/turn.py +168 -66
- klaude_code/llm/__init__.py +2 -10
- klaude_code/llm/anthropic/client.py +190 -178
- klaude_code/llm/anthropic/input.py +39 -15
- klaude_code/llm/bedrock/__init__.py +3 -0
- klaude_code/llm/bedrock/client.py +60 -0
- klaude_code/llm/client.py +7 -21
- klaude_code/llm/codex/__init__.py +5 -0
- klaude_code/llm/codex/client.py +149 -0
- klaude_code/llm/google/__init__.py +3 -0
- klaude_code/llm/google/client.py +309 -0
- klaude_code/llm/google/input.py +215 -0
- klaude_code/llm/input_common.py +3 -9
- klaude_code/llm/openai_compatible/client.py +72 -164
- klaude_code/llm/openai_compatible/input.py +6 -4
- klaude_code/llm/openai_compatible/stream.py +273 -0
- klaude_code/llm/openai_compatible/tool_call_accumulator.py +17 -1
- klaude_code/llm/openrouter/client.py +89 -160
- klaude_code/llm/openrouter/input.py +18 -30
- klaude_code/llm/openrouter/reasoning.py +118 -0
- klaude_code/llm/registry.py +39 -7
- klaude_code/llm/responses/client.py +184 -171
- klaude_code/llm/responses/input.py +20 -1
- klaude_code/llm/usage.py +17 -12
- klaude_code/protocol/commands.py +17 -1
- klaude_code/protocol/events.py +31 -4
- klaude_code/protocol/llm_param.py +13 -10
- klaude_code/protocol/model.py +232 -29
- klaude_code/protocol/op.py +90 -1
- klaude_code/protocol/op_handler.py +35 -1
- klaude_code/protocol/sub_agent/__init__.py +117 -0
- klaude_code/protocol/sub_agent/explore.py +63 -0
- klaude_code/protocol/sub_agent/oracle.py +91 -0
- klaude_code/protocol/sub_agent/task.py +61 -0
- klaude_code/protocol/sub_agent/web.py +79 -0
- klaude_code/protocol/tools.py +4 -2
- klaude_code/session/__init__.py +2 -2
- klaude_code/session/codec.py +71 -0
- klaude_code/session/export.py +293 -86
- klaude_code/session/selector.py +89 -67
- klaude_code/session/session.py +320 -309
- klaude_code/session/store.py +220 -0
- klaude_code/session/templates/export_session.html +595 -83
- klaude_code/session/templates/mermaid_viewer.html +926 -0
- klaude_code/skill/__init__.py +27 -0
- klaude_code/skill/assets/deslop/SKILL.md +17 -0
- klaude_code/skill/assets/dev-docs/SKILL.md +108 -0
- klaude_code/skill/assets/handoff/SKILL.md +39 -0
- klaude_code/skill/assets/jj-workspace/SKILL.md +20 -0
- klaude_code/skill/assets/skill-creator/SKILL.md +139 -0
- klaude_code/{core/tool/memory/skill_loader.py → skill/loader.py} +55 -15
- klaude_code/skill/manager.py +70 -0
- klaude_code/skill/system_skills.py +192 -0
- klaude_code/trace/__init__.py +20 -2
- klaude_code/trace/log.py +150 -5
- klaude_code/ui/__init__.py +4 -9
- klaude_code/ui/core/input.py +1 -1
- klaude_code/ui/core/stage_manager.py +7 -7
- klaude_code/ui/modes/debug/display.py +2 -1
- klaude_code/ui/modes/repl/__init__.py +3 -48
- klaude_code/ui/modes/repl/clipboard.py +5 -5
- klaude_code/ui/modes/repl/completers.py +487 -123
- klaude_code/ui/modes/repl/display.py +5 -4
- klaude_code/ui/modes/repl/event_handler.py +370 -117
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +552 -105
- klaude_code/ui/modes/repl/key_bindings.py +146 -23
- klaude_code/ui/modes/repl/renderer.py +189 -99
- klaude_code/ui/renderers/assistant.py +9 -2
- klaude_code/ui/renderers/bash_syntax.py +178 -0
- klaude_code/ui/renderers/common.py +78 -0
- klaude_code/ui/renderers/developer.py +104 -48
- klaude_code/ui/renderers/diffs.py +87 -6
- klaude_code/ui/renderers/errors.py +11 -6
- klaude_code/ui/renderers/mermaid_viewer.py +57 -0
- klaude_code/ui/renderers/metadata.py +112 -76
- klaude_code/ui/renderers/sub_agent.py +92 -7
- klaude_code/ui/renderers/thinking.py +40 -18
- klaude_code/ui/renderers/tools.py +405 -227
- klaude_code/ui/renderers/user_input.py +73 -13
- klaude_code/ui/rich/__init__.py +10 -1
- klaude_code/ui/rich/cjk_wrap.py +228 -0
- klaude_code/ui/rich/code_panel.py +131 -0
- klaude_code/ui/rich/live.py +17 -0
- klaude_code/ui/rich/markdown.py +305 -170
- klaude_code/ui/rich/searchable_text.py +10 -13
- klaude_code/ui/rich/status.py +190 -49
- klaude_code/ui/rich/theme.py +135 -39
- klaude_code/ui/terminal/__init__.py +55 -0
- klaude_code/ui/terminal/color.py +1 -1
- klaude_code/ui/terminal/control.py +13 -22
- klaude_code/ui/terminal/notifier.py +44 -4
- klaude_code/ui/terminal/selector.py +658 -0
- klaude_code/ui/utils/common.py +0 -18
- klaude_code-1.8.0.dist-info/METADATA +377 -0
- klaude_code-1.8.0.dist-info/RECORD +219 -0
- {klaude_code-1.2.6.dist-info → klaude_code-1.8.0.dist-info}/entry_points.txt +1 -0
- klaude_code/command/diff_cmd.py +0 -138
- klaude_code/command/prompt-dev-docs-update.md +0 -56
- klaude_code/command/prompt-dev-docs.md +0 -46
- klaude_code/config/list_model.py +0 -162
- klaude_code/core/manager/agent_manager.py +0 -127
- klaude_code/core/prompts/prompt-subagent-webfetch.md +0 -46
- klaude_code/core/tool/file/multi_edit_tool.md +0 -42
- klaude_code/core/tool/file/multi_edit_tool.py +0 -199
- klaude_code/core/tool/memory/memory_tool.md +0 -16
- klaude_code/core/tool/memory/memory_tool.py +0 -462
- klaude_code/llm/openrouter/reasoning_handler.py +0 -209
- klaude_code/protocol/sub_agent.py +0 -348
- klaude_code/ui/utils/debouncer.py +0 -42
- klaude_code-1.2.6.dist-info/METADATA +0 -178
- klaude_code-1.2.6.dist-info/RECORD +0 -167
- /klaude_code/core/prompts/{prompt-subagent.md → prompt-sub-agent.md} +0 -0
- /klaude_code/core/tool/{memory → skill}/__init__.py +0 -0
- /klaude_code/core/tool/{memory → skill}/skill_tool.md +0 -0
- {klaude_code-1.2.6.dist-info → klaude_code-1.8.0.dist-info}/WHEEL +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Self-update and version utilities for klaude-code."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -8,8 +8,14 @@ import subprocess
|
|
|
8
8
|
import threading
|
|
9
9
|
import time
|
|
10
10
|
import urllib.request
|
|
11
|
+
from importlib.metadata import PackageNotFoundError
|
|
12
|
+
from importlib.metadata import version as pkg_version
|
|
11
13
|
from typing import NamedTuple
|
|
12
14
|
|
|
15
|
+
import typer
|
|
16
|
+
|
|
17
|
+
from klaude_code.trace import log
|
|
18
|
+
|
|
13
19
|
PACKAGE_NAME = "klaude-code"
|
|
14
20
|
PYPI_URL = f"https://pypi.org/pypi/{PACKAGE_NAME}/json"
|
|
15
21
|
CHECK_INTERVAL_SECONDS = 3600 # Check at most once per hour
|
|
@@ -57,7 +63,7 @@ def _get_installed_version() -> str | None:
|
|
|
57
63
|
ver = ver[1:]
|
|
58
64
|
return ver
|
|
59
65
|
return None
|
|
60
|
-
except
|
|
66
|
+
except (OSError, subprocess.SubprocessError):
|
|
61
67
|
return None
|
|
62
68
|
|
|
63
69
|
|
|
@@ -67,7 +73,7 @@ def _get_latest_version() -> str | None:
|
|
|
67
73
|
with urllib.request.urlopen(PYPI_URL, timeout=5) as response:
|
|
68
74
|
data = json.loads(response.read().decode())
|
|
69
75
|
return data.get("info", {}).get("version")
|
|
70
|
-
except
|
|
76
|
+
except (OSError, json.JSONDecodeError, ValueError):
|
|
71
77
|
return None
|
|
72
78
|
|
|
73
79
|
|
|
@@ -93,7 +99,7 @@ def _compare_versions(installed: str, latest: str) -> bool:
|
|
|
93
99
|
installed_tuple = _parse_version(installed)
|
|
94
100
|
latest_tuple = _parse_version(latest)
|
|
95
101
|
return latest_tuple > installed_tuple
|
|
96
|
-
except
|
|
102
|
+
except ValueError:
|
|
97
103
|
return False
|
|
98
104
|
|
|
99
105
|
|
|
@@ -160,4 +166,107 @@ def get_update_message() -> str | None:
|
|
|
160
166
|
info = check_for_updates()
|
|
161
167
|
if info is None or not info.update_available:
|
|
162
168
|
return None
|
|
163
|
-
return f"New version available: {info.latest}. Please run `
|
|
169
|
+
return f"New version available: {info.latest}. Please run `klaude upgrade` to upgrade."
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _print_version() -> None:
|
|
173
|
+
try:
|
|
174
|
+
ver = pkg_version(PACKAGE_NAME)
|
|
175
|
+
except PackageNotFoundError:
|
|
176
|
+
ver = "unknown"
|
|
177
|
+
except (ValueError, TypeError):
|
|
178
|
+
# Catch invalid package name format or type errors
|
|
179
|
+
ver = "unknown"
|
|
180
|
+
print(f"{PACKAGE_NAME} {ver}")
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def version_option_callback(value: bool) -> None:
|
|
184
|
+
"""Show version and exit."""
|
|
185
|
+
if value:
|
|
186
|
+
_print_version()
|
|
187
|
+
raise typer.Exit(0)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def version_command() -> None:
|
|
191
|
+
"""Show version and exit."""
|
|
192
|
+
|
|
193
|
+
_print_version()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def update_command(
|
|
197
|
+
check: bool = typer.Option(
|
|
198
|
+
False,
|
|
199
|
+
"--check",
|
|
200
|
+
help="Check for updates and exit without upgrading",
|
|
201
|
+
),
|
|
202
|
+
) -> None:
|
|
203
|
+
"""Upgrade klaude-code when installed via `uv tool`."""
|
|
204
|
+
|
|
205
|
+
info = check_for_updates_blocking()
|
|
206
|
+
|
|
207
|
+
if check:
|
|
208
|
+
if info is None:
|
|
209
|
+
log(("Error: `uv` is not available; cannot check for updates.", "red"))
|
|
210
|
+
log(f"Install uv, then run `uv tool upgrade {PACKAGE_NAME}`.")
|
|
211
|
+
raise typer.Exit(1)
|
|
212
|
+
|
|
213
|
+
installed_display = info.installed or "unknown"
|
|
214
|
+
latest_display = info.latest or "unknown"
|
|
215
|
+
status = "update available" if info.update_available else "up to date"
|
|
216
|
+
|
|
217
|
+
log(f"{PACKAGE_NAME} installed: {installed_display}")
|
|
218
|
+
log(f"{PACKAGE_NAME} latest: {latest_display}")
|
|
219
|
+
log(f"Status: {status}")
|
|
220
|
+
|
|
221
|
+
if info.update_available:
|
|
222
|
+
log("Run `klaude upgrade` to upgrade.")
|
|
223
|
+
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
if shutil.which("uv") is None:
|
|
227
|
+
log(("Error: `uv` not found in PATH.", "red"))
|
|
228
|
+
log(f"To update, install uv and run `uv tool upgrade {PACKAGE_NAME}`.")
|
|
229
|
+
raise typer.Exit(1)
|
|
230
|
+
|
|
231
|
+
log(f"Running `uv tool upgrade {PACKAGE_NAME}`...")
|
|
232
|
+
result = subprocess.run(["uv", "tool", "upgrade", PACKAGE_NAME], check=False)
|
|
233
|
+
if result.returncode != 0:
|
|
234
|
+
log((f"Error: update failed (exit code {result.returncode}).", "red"))
|
|
235
|
+
raise typer.Exit(result.returncode or 1)
|
|
236
|
+
|
|
237
|
+
log("Update complete. Please re-run `klaude` to use the new version.")
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def register_self_update_commands(app: typer.Typer) -> None:
|
|
241
|
+
"""Register self-update and version subcommands to the given Typer app."""
|
|
242
|
+
|
|
243
|
+
app.command("update")(update_command)
|
|
244
|
+
app.command("upgrade", help="Alias for `klaude update`.")(update_command)
|
|
245
|
+
app.command("version", help="Alias for `klaude --version`.")(version_command)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def check_for_updates_blocking() -> VersionInfo | None:
|
|
249
|
+
"""Check for updates to klaude-code synchronously.
|
|
250
|
+
|
|
251
|
+
This is intended for CLI commands (e.g. `klaude update --check`) that need
|
|
252
|
+
a deterministic result instead of the async cached behavior.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
VersionInfo if uv is available, otherwise None.
|
|
256
|
+
"""
|
|
257
|
+
|
|
258
|
+
if not _has_uv():
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
installed = _get_installed_version()
|
|
262
|
+
latest = _get_latest_version()
|
|
263
|
+
|
|
264
|
+
update_available = False
|
|
265
|
+
if installed and latest:
|
|
266
|
+
update_available = _compare_versions(installed, latest)
|
|
267
|
+
|
|
268
|
+
return VersionInfo(
|
|
269
|
+
installed=installed,
|
|
270
|
+
latest=latest,
|
|
271
|
+
update_available=update_available,
|
|
272
|
+
)
|
klaude_code/cli/session_cmd.py
CHANGED
|
@@ -7,31 +7,47 @@ from klaude_code.trace import log
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def _session_confirm(sessions: list[Session.SessionMetaBrief], message: str) -> bool:
|
|
10
|
-
"""Show session list and confirm deletion using
|
|
11
|
-
|
|
10
|
+
"""Show session list and confirm deletion using prompt_toolkit."""
|
|
11
|
+
|
|
12
|
+
from prompt_toolkit.styles import Style
|
|
13
|
+
|
|
14
|
+
from klaude_code.ui.terminal.selector import SelectItem, select_one
|
|
12
15
|
|
|
13
16
|
def _fmt(ts: float) -> str:
|
|
14
17
|
try:
|
|
15
18
|
return time.strftime("%m-%d %H:%M:%S", time.localtime(ts))
|
|
16
|
-
except
|
|
19
|
+
except (OSError, OverflowError, ValueError):
|
|
17
20
|
return str(ts)
|
|
18
21
|
|
|
19
22
|
log(f"Sessions to delete ({len(sessions)}):")
|
|
20
23
|
for s in sessions:
|
|
21
24
|
msg_count_display = "N/A" if s.messages_count == -1 else str(s.messages_count)
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
first_msg_text = s.user_messages[0] if s.user_messages else ""
|
|
26
|
+
first_msg = first_msg_text.strip().replace("\n", " ")[:50]
|
|
27
|
+
if len(first_msg_text) > 50:
|
|
24
28
|
first_msg += "..."
|
|
25
29
|
log(f" {_fmt(s.updated_at)} {msg_count_display:>3} msgs {first_msg}")
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
items: list[SelectItem[bool]] = [
|
|
32
|
+
SelectItem(title=[("class:text", "No\n")], value=False, search_text="No"),
|
|
33
|
+
SelectItem(title=[("class:text", "Yes\n")], value=True, search_text="Yes"),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
result = select_one(
|
|
37
|
+
message=message,
|
|
38
|
+
items=items,
|
|
39
|
+
pointer="→",
|
|
40
|
+
style=Style(
|
|
41
|
+
[
|
|
42
|
+
("question", "bold"),
|
|
43
|
+
("pointer", "ansigreen"),
|
|
44
|
+
("highlighted", "ansigreen"),
|
|
45
|
+
("text", ""),
|
|
46
|
+
]
|
|
47
|
+
),
|
|
48
|
+
use_search_filter=False,
|
|
34
49
|
)
|
|
50
|
+
return bool(result)
|
|
35
51
|
|
|
36
52
|
|
|
37
53
|
def session_clean(
|
|
@@ -46,10 +62,9 @@ def session_clean(
|
|
|
46
62
|
log(f"No sessions with fewer than {min_messages} messages found.")
|
|
47
63
|
return
|
|
48
64
|
|
|
49
|
-
if not yes:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return
|
|
65
|
+
if not yes and not _session_confirm(to_delete, "Delete these sessions?"):
|
|
66
|
+
log("Aborted.")
|
|
67
|
+
return
|
|
53
68
|
|
|
54
69
|
deleted = Session.clean_small_sessions(min_messages)
|
|
55
70
|
log(f"Deleted {deleted} session(s).")
|
|
@@ -65,16 +80,17 @@ def session_clean_all(
|
|
|
65
80
|
log("No sessions found.")
|
|
66
81
|
return
|
|
67
82
|
|
|
68
|
-
if not yes:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return
|
|
83
|
+
if not yes and not _session_confirm(sessions, "Delete ALL sessions? This cannot be undone."):
|
|
84
|
+
log("Aborted.")
|
|
85
|
+
return
|
|
72
86
|
|
|
73
87
|
deleted = Session.clean_all_sessions()
|
|
74
88
|
log(f"Deleted {deleted} session(s).")
|
|
75
89
|
|
|
76
90
|
|
|
77
|
-
def register_session_commands(
|
|
91
|
+
def register_session_commands(app: typer.Typer) -> None:
|
|
78
92
|
"""Register session subcommands to the given Typer app."""
|
|
93
|
+
session_app = typer.Typer(help="Manage sessions for the current project")
|
|
79
94
|
session_app.command("clean")(session_clean)
|
|
80
95
|
session_app.command("clean-all")(session_clean_all)
|
|
96
|
+
app.add_typer(session_app, name="session")
|
klaude_code/command/__init__.py
CHANGED
|
@@ -1,43 +1,104 @@
|
|
|
1
|
-
from .
|
|
2
|
-
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
|
|
1
|
+
from .command_abc import CommandABC, CommandResult
|
|
11
2
|
from .registry import (
|
|
12
3
|
dispatch_command,
|
|
4
|
+
get_command_info_list,
|
|
5
|
+
get_command_names,
|
|
13
6
|
get_commands,
|
|
14
7
|
has_interactive_command,
|
|
15
8
|
is_slash_command_name,
|
|
16
9
|
load_prompt_commands,
|
|
17
|
-
|
|
10
|
+
register,
|
|
18
11
|
)
|
|
19
|
-
from .status_cmd import StatusCommand
|
|
20
|
-
from .terminal_setup_cmd import TerminalSetupCommand
|
|
21
12
|
|
|
22
|
-
#
|
|
23
|
-
|
|
13
|
+
# Lazy load commands to avoid heavy imports at module load time
|
|
14
|
+
_commands_loaded = False
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def ensure_commands_loaded() -> None:
|
|
18
|
+
"""Ensure all commands are loaded (lazy initialization).
|
|
19
|
+
|
|
20
|
+
This function is called internally by registry functions like get_commands(),
|
|
21
|
+
dispatch_command(), etc. It can also be called explicitly if early loading is desired.
|
|
22
|
+
|
|
23
|
+
Commands are registered in display order - the order here determines
|
|
24
|
+
the order shown in slash command completion.
|
|
25
|
+
"""
|
|
26
|
+
global _commands_loaded
|
|
27
|
+
if _commands_loaded:
|
|
28
|
+
return
|
|
29
|
+
_commands_loaded = True
|
|
30
|
+
|
|
31
|
+
# Import and register commands in display order
|
|
32
|
+
from .clear_cmd import ClearCommand
|
|
33
|
+
from .debug_cmd import DebugCommand
|
|
34
|
+
from .export_cmd import ExportCommand
|
|
35
|
+
from .export_online_cmd import ExportOnlineCommand
|
|
36
|
+
from .fork_session_cmd import ForkSessionCommand
|
|
37
|
+
from .help_cmd import HelpCommand
|
|
38
|
+
from .model_cmd import ModelCommand
|
|
39
|
+
from .refresh_cmd import RefreshTerminalCommand
|
|
40
|
+
from .release_notes_cmd import ReleaseNotesCommand
|
|
41
|
+
from .resume_cmd import ResumeCommand
|
|
42
|
+
from .status_cmd import StatusCommand
|
|
43
|
+
from .terminal_setup_cmd import TerminalSetupCommand
|
|
44
|
+
from .thinking_cmd import ThinkingCommand
|
|
45
|
+
|
|
46
|
+
# Register in desired display order
|
|
47
|
+
register(ExportCommand())
|
|
48
|
+
register(ExportOnlineCommand())
|
|
49
|
+
register(RefreshTerminalCommand())
|
|
50
|
+
register(ThinkingCommand())
|
|
51
|
+
register(ModelCommand())
|
|
52
|
+
register(ForkSessionCommand())
|
|
53
|
+
register(ResumeCommand())
|
|
54
|
+
load_prompt_commands()
|
|
55
|
+
register(StatusCommand())
|
|
56
|
+
register(HelpCommand())
|
|
57
|
+
register(ReleaseNotesCommand())
|
|
58
|
+
register(TerminalSetupCommand())
|
|
59
|
+
register(DebugCommand())
|
|
60
|
+
register(ClearCommand())
|
|
61
|
+
|
|
62
|
+
# Load prompt-based commands (appended after built-in commands)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Lazy accessors for command classes
|
|
66
|
+
def __getattr__(name: str) -> object:
|
|
67
|
+
_commands_map = {
|
|
68
|
+
"ClearCommand": "clear_cmd",
|
|
69
|
+
"DebugCommand": "debug_cmd",
|
|
70
|
+
"ExportCommand": "export_cmd",
|
|
71
|
+
"ExportOnlineCommand": "export_online_cmd",
|
|
72
|
+
"ForkSessionCommand": "fork_session_cmd",
|
|
73
|
+
"HelpCommand": "help_cmd",
|
|
74
|
+
"ModelCommand": "model_cmd",
|
|
75
|
+
"RefreshTerminalCommand": "refresh_cmd",
|
|
76
|
+
"ReleaseNotesCommand": "release_notes_cmd",
|
|
77
|
+
"ResumeCommand": "resume_cmd",
|
|
78
|
+
"StatusCommand": "status_cmd",
|
|
79
|
+
"TerminalSetupCommand": "terminal_setup_cmd",
|
|
80
|
+
"ThinkingCommand": "thinking_cmd",
|
|
81
|
+
}
|
|
82
|
+
if name in _commands_map:
|
|
83
|
+
import importlib
|
|
84
|
+
|
|
85
|
+
module = importlib.import_module(f".{_commands_map[name]}", __package__)
|
|
86
|
+
return getattr(module, name)
|
|
87
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
88
|
+
|
|
24
89
|
|
|
25
90
|
__all__ = [
|
|
26
|
-
|
|
27
|
-
"DiffCommand",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"ExportCommand",
|
|
31
|
-
"RefreshTerminalCommand",
|
|
32
|
-
"StatusCommand",
|
|
33
|
-
"TerminalSetupCommand",
|
|
34
|
-
"register_command",
|
|
91
|
+
# Command classes are lazily loaded via __getattr__
|
|
92
|
+
# "ClearCommand", "DiffCommand", "HelpCommand", "ModelCommand",
|
|
93
|
+
# "ExportCommand", "RefreshTerminalCommand", "ReleaseNotesCommand",
|
|
94
|
+
# "StatusCommand", "TerminalSetupCommand",
|
|
35
95
|
"CommandABC",
|
|
36
96
|
"CommandResult",
|
|
37
|
-
"InputAction",
|
|
38
|
-
"InputActionType",
|
|
39
97
|
"dispatch_command",
|
|
98
|
+
"ensure_commands_loaded",
|
|
99
|
+
"get_command_info_list",
|
|
100
|
+
"get_command_names",
|
|
40
101
|
"get_commands",
|
|
41
|
-
"is_slash_command_name",
|
|
42
102
|
"has_interactive_command",
|
|
103
|
+
"is_slash_command_name",
|
|
43
104
|
]
|
klaude_code/command/clear_cmd.py
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
2
|
-
from klaude_code.
|
|
3
|
-
from klaude_code.core.agent import Agent
|
|
4
|
-
from klaude_code.protocol import commands
|
|
1
|
+
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
2
|
+
from klaude_code.protocol import commands, model, op
|
|
5
3
|
|
|
6
4
|
|
|
7
|
-
@register_command
|
|
8
5
|
class ClearCommand(CommandABC):
|
|
9
6
|
"""Clear current session and start a new conversation"""
|
|
10
7
|
|
|
@@ -16,5 +13,9 @@ class ClearCommand(CommandABC):
|
|
|
16
13
|
def summary(self) -> str:
|
|
17
14
|
return "Clear conversation history and free up context"
|
|
18
15
|
|
|
19
|
-
async def run(self,
|
|
20
|
-
|
|
16
|
+
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
17
|
+
del user_input # unused
|
|
18
|
+
return CommandResult(
|
|
19
|
+
operations=[op.ClearSessionOperation(session_id=agent.session.id)],
|
|
20
|
+
persist_user_input=False,
|
|
21
|
+
)
|
|
@@ -1,45 +1,36 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from
|
|
2
|
+
from typing import Protocol
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel
|
|
5
5
|
|
|
6
|
-
from klaude_code.
|
|
7
|
-
from klaude_code.protocol import commands
|
|
6
|
+
from klaude_code.llm import LLMClientABC
|
|
7
|
+
from klaude_code.protocol import commands, llm_param, model, op
|
|
8
8
|
from klaude_code.protocol import events as protocol_events
|
|
9
|
+
from klaude_code.session.session import Session
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
class
|
|
12
|
-
"""
|
|
12
|
+
class AgentProfile(Protocol):
|
|
13
|
+
"""Protocol for the agent's active model profile."""
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
CLEAR = "clear"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class InputAction(BaseModel):
|
|
20
|
-
"""Structured executor action derived from a user input."""
|
|
15
|
+
@property
|
|
16
|
+
def llm_client(self) -> LLMClientABC: ...
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
model_name: str | None = None
|
|
18
|
+
@property
|
|
19
|
+
def system_prompt(self) -> str | None: ...
|
|
25
20
|
|
|
26
|
-
@
|
|
27
|
-
def
|
|
28
|
-
"""Create a RunAgent action preserving the provided text."""
|
|
21
|
+
@property
|
|
22
|
+
def tools(self) -> list[llm_param.ToolSchema]: ...
|
|
29
23
|
|
|
30
|
-
return cls(type=InputActionType.RUN_AGENT, text=text)
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"""Create a ChangeModel action for the provided model name."""
|
|
25
|
+
class Agent(Protocol):
|
|
26
|
+
"""Protocol for Agent objects passed to commands."""
|
|
35
27
|
|
|
36
|
-
|
|
28
|
+
session: Session
|
|
37
29
|
|
|
38
|
-
@
|
|
39
|
-
def
|
|
40
|
-
"""Create a Clear action to reset the session."""
|
|
30
|
+
@property
|
|
31
|
+
def profile(self) -> AgentProfile | None: ...
|
|
41
32
|
|
|
42
|
-
|
|
33
|
+
def get_llm_client(self) -> LLMClientABC: ...
|
|
43
34
|
|
|
44
35
|
|
|
45
36
|
class CommandResult(BaseModel):
|
|
@@ -49,7 +40,11 @@ class CommandResult(BaseModel):
|
|
|
49
40
|
list[protocol_events.DeveloperMessageEvent | protocol_events.WelcomeEvent | protocol_events.ReplayHistoryEvent]
|
|
50
41
|
| None
|
|
51
42
|
) = None # List of UI events to display immediately
|
|
52
|
-
|
|
43
|
+
operations: list[op.Operation] | None = None
|
|
44
|
+
|
|
45
|
+
# Persistence controls: some slash commands are UI/control actions and should not be written to session history.
|
|
46
|
+
persist_user_input: bool = True
|
|
47
|
+
persist_events: bool = True
|
|
53
48
|
|
|
54
49
|
|
|
55
50
|
class CommandABC(ABC):
|
|
@@ -77,14 +72,19 @@ class CommandABC(ABC):
|
|
|
77
72
|
"""Whether this command support additional parameters."""
|
|
78
73
|
return False
|
|
79
74
|
|
|
75
|
+
@property
|
|
76
|
+
def placeholder(self) -> str:
|
|
77
|
+
"""Placeholder text for additional parameters in help display."""
|
|
78
|
+
return "additional instructions"
|
|
79
|
+
|
|
80
80
|
@abstractmethod
|
|
81
|
-
async def run(self,
|
|
81
|
+
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
82
82
|
"""
|
|
83
83
|
Execute the command.
|
|
84
84
|
|
|
85
85
|
Args:
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
agent: The agent instance
|
|
87
|
+
user_input: User input with text containing command arguments (without command name)
|
|
88
88
|
|
|
89
89
|
Returns:
|
|
90
90
|
CommandResult: Result of the command execution
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
|
|
2
|
+
from klaude_code.protocol import commands, events, model
|
|
3
|
+
from klaude_code.trace import DebugType, get_current_log_file, is_debug_enabled, set_debug_logging
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _format_status() -> str:
|
|
7
|
+
"""Format the current debug status for display."""
|
|
8
|
+
if not is_debug_enabled():
|
|
9
|
+
return "Debug: OFF"
|
|
10
|
+
|
|
11
|
+
log_file = get_current_log_file()
|
|
12
|
+
log_path_str = str(log_file) if log_file else "(console)"
|
|
13
|
+
return f"Debug: ON\nLog file: {log_path_str}"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _parse_debug_filters(raw: str) -> set[DebugType] | None:
|
|
17
|
+
filters: set[DebugType] = set()
|
|
18
|
+
for chunk in raw.split(","):
|
|
19
|
+
normalized = chunk.strip().lower().replace("-", "_")
|
|
20
|
+
if not normalized:
|
|
21
|
+
continue
|
|
22
|
+
try:
|
|
23
|
+
filters.add(DebugType(normalized))
|
|
24
|
+
except ValueError as exc:
|
|
25
|
+
raise ValueError(normalized) from exc
|
|
26
|
+
return filters or None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DebugCommand(CommandABC):
|
|
30
|
+
"""Toggle debug mode and configure debug filters."""
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def name(self) -> commands.CommandName:
|
|
34
|
+
return commands.CommandName.DEBUG
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def summary(self) -> str:
|
|
38
|
+
return "Toggle debug mode (optional: filter types)"
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def support_addition_params(self) -> bool:
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def placeholder(self) -> str:
|
|
46
|
+
return "filter types"
|
|
47
|
+
|
|
48
|
+
async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
|
|
49
|
+
raw = user_input.text.strip()
|
|
50
|
+
|
|
51
|
+
# /debug (no args) - enable debug
|
|
52
|
+
if not raw:
|
|
53
|
+
set_debug_logging(True, write_to_file=True)
|
|
54
|
+
return self._message_result(agent, _format_status())
|
|
55
|
+
|
|
56
|
+
# /debug <filters> - enable with filters
|
|
57
|
+
try:
|
|
58
|
+
filters = _parse_debug_filters(raw)
|
|
59
|
+
if filters:
|
|
60
|
+
set_debug_logging(True, write_to_file=True, filters=filters)
|
|
61
|
+
filter_names = ", ".join(sorted(dt.value for dt in filters))
|
|
62
|
+
return self._message_result(agent, f"Filters: {filter_names}\n{_format_status()}")
|
|
63
|
+
except ValueError:
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
return self._message_result(agent, f"Invalid filter: {raw}\nValid: {', '.join(dt.value for dt in DebugType)}")
|
|
67
|
+
|
|
68
|
+
def _message_result(self, agent: "Agent", content: str) -> CommandResult:
|
|
69
|
+
return CommandResult(
|
|
70
|
+
events=[
|
|
71
|
+
events.DeveloperMessageEvent(
|
|
72
|
+
session_id=agent.session.id,
|
|
73
|
+
item=model.DeveloperMessageItem(
|
|
74
|
+
content=content,
|
|
75
|
+
command_output=model.CommandOutput(command_name=self.name),
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
]
|
|
79
|
+
)
|