klaude-code 1.2.17__py3-none-any.whl → 1.2.19__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- klaude_code/cli/config_cmd.py +1 -1
- klaude_code/cli/debug.py +1 -1
- klaude_code/cli/main.py +45 -31
- klaude_code/cli/runtime.py +49 -13
- klaude_code/{version.py → cli/self_update.py} +110 -2
- klaude_code/command/__init__.py +4 -1
- klaude_code/command/clear_cmd.py +2 -7
- klaude_code/command/command_abc.py +33 -5
- klaude_code/command/debug_cmd.py +79 -0
- klaude_code/command/diff_cmd.py +2 -6
- klaude_code/command/export_cmd.py +7 -7
- klaude_code/command/export_online_cmd.py +9 -8
- klaude_code/command/help_cmd.py +4 -9
- klaude_code/command/model_cmd.py +10 -6
- klaude_code/command/prompt_command.py +2 -6
- klaude_code/command/refresh_cmd.py +2 -7
- klaude_code/command/registry.py +69 -26
- klaude_code/command/release_notes_cmd.py +2 -6
- klaude_code/command/status_cmd.py +2 -7
- klaude_code/command/terminal_setup_cmd.py +2 -6
- klaude_code/command/thinking_cmd.py +16 -10
- klaude_code/config/select_model.py +81 -5
- klaude_code/const/__init__.py +1 -1
- klaude_code/core/executor.py +257 -110
- klaude_code/core/manager/__init__.py +2 -4
- klaude_code/core/prompts/prompt-claude-code.md +1 -1
- klaude_code/core/prompts/prompt-sub-agent-explore.md +14 -2
- klaude_code/core/prompts/prompt-sub-agent-web.md +8 -5
- klaude_code/core/reminders.py +9 -35
- klaude_code/core/task.py +9 -7
- klaude_code/core/tool/file/read_tool.md +1 -1
- klaude_code/core/tool/file/read_tool.py +41 -12
- klaude_code/core/tool/memory/skill_loader.py +12 -10
- klaude_code/core/tool/shell/bash_tool.py +22 -2
- klaude_code/core/tool/tool_registry.py +1 -1
- klaude_code/core/tool/tool_runner.py +26 -23
- klaude_code/core/tool/truncation.py +23 -9
- klaude_code/core/tool/web/web_fetch_tool.md +1 -1
- klaude_code/core/tool/web/web_fetch_tool.py +36 -1
- klaude_code/core/turn.py +28 -0
- klaude_code/llm/anthropic/client.py +25 -9
- klaude_code/llm/openai_compatible/client.py +5 -2
- klaude_code/llm/openrouter/client.py +7 -3
- klaude_code/llm/responses/client.py +6 -1
- klaude_code/protocol/commands.py +1 -0
- klaude_code/protocol/sub_agent/web.py +3 -2
- klaude_code/session/session.py +35 -15
- klaude_code/session/templates/export_session.html +45 -32
- klaude_code/trace/__init__.py +20 -2
- klaude_code/ui/modes/repl/completers.py +231 -73
- klaude_code/ui/modes/repl/event_handler.py +8 -6
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +1 -1
- klaude_code/ui/modes/repl/renderer.py +2 -2
- klaude_code/ui/renderers/common.py +54 -0
- klaude_code/ui/renderers/developer.py +2 -3
- klaude_code/ui/renderers/errors.py +1 -1
- klaude_code/ui/renderers/metadata.py +12 -5
- klaude_code/ui/renderers/thinking.py +24 -8
- klaude_code/ui/renderers/tools.py +82 -14
- klaude_code/ui/rich/code_panel.py +112 -0
- klaude_code/ui/rich/markdown.py +3 -4
- klaude_code/ui/rich/status.py +0 -2
- klaude_code/ui/rich/theme.py +10 -1
- klaude_code/ui/utils/common.py +0 -18
- {klaude_code-1.2.17.dist-info → klaude_code-1.2.19.dist-info}/METADATA +32 -7
- {klaude_code-1.2.17.dist-info → klaude_code-1.2.19.dist-info}/RECORD +69 -68
- klaude_code/core/manager/agent_manager.py +0 -132
- /klaude_code/{config → cli}/list_model.py +0 -0
- {klaude_code-1.2.17.dist-info → klaude_code-1.2.19.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.17.dist-info → klaude_code-1.2.19.dist-info}/entry_points.txt +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
1
3
|
from rich.console import RenderableType
|
|
2
4
|
from rich.padding import Padding
|
|
3
5
|
from rich.text import Text
|
|
@@ -10,14 +12,28 @@ def thinking_prefix() -> Text:
|
|
|
10
12
|
return Text.from_markup("[not italic]⸫[/not italic] Thinking …", style=ThemeKey.THINKING)
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
def
|
|
15
|
+
def normalize_thinking_content(content: str) -> str:
|
|
14
16
|
"""Normalize thinking content for display."""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
text = content.rstrip()
|
|
18
|
+
|
|
19
|
+
# Weird case of Gemini 3
|
|
20
|
+
text = text.replace("\\n\\n\n\n", "")
|
|
21
|
+
|
|
22
|
+
# Fix OpenRouter OpenAI reasoning formatting where segments like
|
|
23
|
+
# "text**Title**\n\n" lose the blank line between segments.
|
|
24
|
+
# We want: "text\n**Title**\n" so that each bold title starts on
|
|
25
|
+
# its own line and uses a single trailing newline.
|
|
26
|
+
text = re.sub(r"([^\n])(\*\*[^*]+?\*\*)\n\n", r"\1 \n\n\2 \n", text)
|
|
27
|
+
|
|
28
|
+
# Remove extra newlines between back-to-back bold titles, eg
|
|
29
|
+
# "**Title1****Title2**" -> "**Title1**\n\n**Title2**".
|
|
30
|
+
text = text.replace("****", "**\n\n**")
|
|
31
|
+
|
|
32
|
+
# Compact double-newline after bold so the body text follows
|
|
33
|
+
# directly after the title line, using a markdown line break.
|
|
34
|
+
text = text.replace("**\n\n", "** \n")
|
|
35
|
+
|
|
36
|
+
return text
|
|
21
37
|
|
|
22
38
|
|
|
23
39
|
def render_thinking(content: str, *, code_theme: str, style: str) -> RenderableType | None:
|
|
@@ -31,7 +47,7 @@ def render_thinking(content: str, *, code_theme: str, style: str) -> RenderableT
|
|
|
31
47
|
|
|
32
48
|
return Padding.indent(
|
|
33
49
|
ThinkingMarkdown(
|
|
34
|
-
|
|
50
|
+
normalize_thinking_content(content),
|
|
35
51
|
code_theme=code_theme,
|
|
36
52
|
style=style,
|
|
37
53
|
),
|
|
@@ -2,7 +2,7 @@ import json
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Any, cast
|
|
4
4
|
|
|
5
|
-
from rich.console import RenderableType
|
|
5
|
+
from rich.console import Group, RenderableType
|
|
6
6
|
from rich.padding import Padding
|
|
7
7
|
from rich.text import Text
|
|
8
8
|
|
|
@@ -10,9 +10,8 @@ from klaude_code import const
|
|
|
10
10
|
from klaude_code.protocol import events, model, tools
|
|
11
11
|
from klaude_code.protocol.sub_agent import is_sub_agent_tool as _is_sub_agent_tool
|
|
12
12
|
from klaude_code.ui.renderers import diffs as r_diffs
|
|
13
|
-
from klaude_code.ui.renderers.common import create_grid
|
|
13
|
+
from klaude_code.ui.renderers.common import create_grid, truncate_display
|
|
14
14
|
from klaude_code.ui.rich.theme import ThemeKey
|
|
15
|
-
from klaude_code.ui.utils.common import truncate_display
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
def is_sub_agent_tool(tool_name: str) -> bool:
|
|
@@ -290,7 +289,7 @@ def render_todo(tr: events.ToolResultEvent) -> RenderableType:
|
|
|
290
289
|
def render_generic_tool_result(result: str, *, is_error: bool = False) -> RenderableType:
|
|
291
290
|
"""Render a generic tool result as indented, truncated text."""
|
|
292
291
|
style = ThemeKey.ERROR if is_error else ThemeKey.TOOL_RESULT
|
|
293
|
-
return Padding.indent(
|
|
292
|
+
return Padding.indent(truncate_display(result, base_style=style), level=2)
|
|
294
293
|
|
|
295
294
|
|
|
296
295
|
def _extract_mermaid_link(
|
|
@@ -379,6 +378,75 @@ def render_mermaid_tool_call(arguments: str) -> RenderableType:
|
|
|
379
378
|
return grid
|
|
380
379
|
|
|
381
380
|
|
|
381
|
+
def _truncate_url(url: str, max_length: int = 400) -> str:
|
|
382
|
+
"""Truncate URL for display, preserving domain and path structure."""
|
|
383
|
+
if len(url) <= max_length:
|
|
384
|
+
return url
|
|
385
|
+
# Remove protocol for display
|
|
386
|
+
display_url = url
|
|
387
|
+
for prefix in ("https://", "http://"):
|
|
388
|
+
if display_url.startswith(prefix):
|
|
389
|
+
display_url = display_url[len(prefix) :]
|
|
390
|
+
break
|
|
391
|
+
if len(display_url) <= max_length:
|
|
392
|
+
return display_url
|
|
393
|
+
# Truncate with ellipsis
|
|
394
|
+
return display_url[: max_length - 3] + "..."
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def render_web_fetch_tool_call(arguments: str) -> RenderableType:
|
|
398
|
+
grid = create_grid()
|
|
399
|
+
tool_name_column = Text.assemble(("↓", ThemeKey.TOOL_MARK), " ", ("Fetch", ThemeKey.TOOL_NAME))
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
payload: dict[str, str] = json.loads(arguments)
|
|
403
|
+
except json.JSONDecodeError:
|
|
404
|
+
summary = Text(
|
|
405
|
+
arguments.strip()[: const.INVALID_TOOL_CALL_MAX_LENGTH],
|
|
406
|
+
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
407
|
+
)
|
|
408
|
+
grid.add_row(tool_name_column, summary)
|
|
409
|
+
return grid
|
|
410
|
+
|
|
411
|
+
url = payload.get("url", "")
|
|
412
|
+
summary = Text(_truncate_url(url), ThemeKey.TOOL_PARAM_FILE_PATH) if url else Text("(no url)", ThemeKey.TOOL_PARAM)
|
|
413
|
+
|
|
414
|
+
grid.add_row(tool_name_column, summary)
|
|
415
|
+
return grid
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def render_web_search_tool_call(arguments: str) -> RenderableType:
|
|
419
|
+
grid = create_grid()
|
|
420
|
+
tool_name_column = Text.assemble(("◉", ThemeKey.TOOL_MARK), " ", ("Search", ThemeKey.TOOL_NAME))
|
|
421
|
+
|
|
422
|
+
try:
|
|
423
|
+
payload: dict[str, Any] = json.loads(arguments)
|
|
424
|
+
except json.JSONDecodeError:
|
|
425
|
+
summary = Text(
|
|
426
|
+
arguments.strip()[: const.INVALID_TOOL_CALL_MAX_LENGTH],
|
|
427
|
+
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
428
|
+
)
|
|
429
|
+
grid.add_row(tool_name_column, summary)
|
|
430
|
+
return grid
|
|
431
|
+
|
|
432
|
+
query = payload.get("query", "")
|
|
433
|
+
max_results = payload.get("max_results")
|
|
434
|
+
|
|
435
|
+
summary = Text("", ThemeKey.TOOL_PARAM)
|
|
436
|
+
if query:
|
|
437
|
+
# Truncate long queries
|
|
438
|
+
display_query = query if len(query) <= 80 else query[:77] + "..."
|
|
439
|
+
summary.append(display_query, ThemeKey.TOOL_PARAM)
|
|
440
|
+
else:
|
|
441
|
+
summary.append("(no query)", ThemeKey.TOOL_PARAM)
|
|
442
|
+
|
|
443
|
+
if isinstance(max_results, int) and max_results != 10:
|
|
444
|
+
summary.append(f" (max {max_results})", ThemeKey.TOOL_TIMEOUT)
|
|
445
|
+
|
|
446
|
+
grid.add_row(tool_name_column, summary)
|
|
447
|
+
return grid
|
|
448
|
+
|
|
449
|
+
|
|
382
450
|
def render_mermaid_tool_result(tr: events.ToolResultEvent) -> RenderableType:
|
|
383
451
|
from klaude_code.ui.terminal import supports_osc8_hyperlinks
|
|
384
452
|
|
|
@@ -409,16 +477,12 @@ def _extract_truncation(
|
|
|
409
477
|
|
|
410
478
|
def render_truncation_info(ui_extra: model.TruncationUIExtra) -> RenderableType:
|
|
411
479
|
"""Render truncation info for the user."""
|
|
412
|
-
original_kb = ui_extra.original_length / 1024
|
|
413
480
|
truncated_kb = ui_extra.truncated_length / 1024
|
|
481
|
+
|
|
414
482
|
text = Text.assemble(
|
|
415
|
-
("
|
|
416
|
-
(
|
|
417
|
-
("
|
|
418
|
-
(f"{truncated_kb:.1f}KB", ThemeKey.TOOL_RESULT_BOLD),
|
|
419
|
-
(" hidden\nFull output saved to ", ThemeKey.TOOL_RESULT),
|
|
420
|
-
(ui_extra.saved_file_path, ThemeKey.TOOL_RESULT),
|
|
421
|
-
("\nUse Read with limit+offset or rg/grep to inspect", ThemeKey.TOOL_RESULT),
|
|
483
|
+
("Offload context to ", ThemeKey.TOOL_RESULT_TRUNCATED),
|
|
484
|
+
(ui_extra.saved_file_path, ThemeKey.TOOL_RESULT_TRUNCATED),
|
|
485
|
+
(f", {truncated_kb:.1f}KB truncated", ThemeKey.TOOL_RESULT_TRUNCATED),
|
|
422
486
|
)
|
|
423
487
|
return Padding.indent(text, level=2)
|
|
424
488
|
|
|
@@ -506,6 +570,10 @@ def render_tool_call(e: events.ToolCallEvent) -> RenderableType | None:
|
|
|
506
570
|
return render_generic_tool_call(e.tool_name, e.arguments, "◈")
|
|
507
571
|
case tools.REPORT_BACK:
|
|
508
572
|
return render_report_back_tool_call()
|
|
573
|
+
case tools.WEB_FETCH:
|
|
574
|
+
return render_web_fetch_tool_call(e.arguments)
|
|
575
|
+
case tools.WEB_SEARCH:
|
|
576
|
+
return render_web_search_tool_call(e.arguments)
|
|
509
577
|
case _:
|
|
510
578
|
return render_generic_tool_call(e.tool_name, e.arguments)
|
|
511
579
|
|
|
@@ -528,13 +596,13 @@ def render_tool_result(e: events.ToolResultEvent) -> RenderableType | None:
|
|
|
528
596
|
|
|
529
597
|
# Handle error case
|
|
530
598
|
if e.status == "error" and e.ui_extra is None:
|
|
531
|
-
error_msg =
|
|
599
|
+
error_msg = truncate_display(e.result)
|
|
532
600
|
return r_errors.render_error(error_msg)
|
|
533
601
|
|
|
534
602
|
# Show truncation info if output was truncated and saved to file
|
|
535
603
|
truncation_info = get_truncation_info(e)
|
|
536
604
|
if truncation_info:
|
|
537
|
-
return render_truncation_info(truncation_info)
|
|
605
|
+
return Group(render_truncation_info(truncation_info), render_generic_tool_result(e.result))
|
|
538
606
|
|
|
539
607
|
diff_text = _extract_diff_text(e.ui_extra)
|
|
540
608
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""A panel that only has top and bottom borders, no left/right borders or padding."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from rich.console import ConsoleRenderable, RichCast
|
|
8
|
+
from rich.jupyter import JupyterMixin
|
|
9
|
+
from rich.measure import Measurement, measure_renderables
|
|
10
|
+
from rich.segment import Segment
|
|
11
|
+
from rich.style import StyleType
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from rich.console import Console, ConsoleOptions, RenderResult
|
|
15
|
+
|
|
16
|
+
# Box drawing characters
|
|
17
|
+
TOP_LEFT = "┌" # ┌
|
|
18
|
+
TOP_RIGHT = "┐" # ┐
|
|
19
|
+
BOTTOM_LEFT = "└" # └
|
|
20
|
+
BOTTOM_RIGHT = "┘" # ┘
|
|
21
|
+
HORIZONTAL = "─" # ─
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CodePanel(JupyterMixin):
|
|
25
|
+
"""A panel with only top and bottom borders, no left/right borders.
|
|
26
|
+
|
|
27
|
+
This is designed for code blocks where you want easy copy-paste without
|
|
28
|
+
picking up border characters on the sides.
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> console.print(CodePanel(Syntax(code, "python")))
|
|
32
|
+
|
|
33
|
+
Renders as:
|
|
34
|
+
┌──────────────────────────┐
|
|
35
|
+
code line 1
|
|
36
|
+
code line 2
|
|
37
|
+
└──────────────────────────┘
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
renderable: ConsoleRenderable | RichCast | str,
|
|
43
|
+
*,
|
|
44
|
+
border_style: StyleType = "none",
|
|
45
|
+
expand: bool = False,
|
|
46
|
+
padding: int = 1,
|
|
47
|
+
) -> None:
|
|
48
|
+
"""Initialize the CodePanel.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
renderable: A console renderable object.
|
|
52
|
+
border_style: The style of the border. Defaults to "none".
|
|
53
|
+
expand: If True, expand to fill available width. Defaults to False.
|
|
54
|
+
padding: Left/right padding for content. Defaults to 1.
|
|
55
|
+
"""
|
|
56
|
+
self.renderable = renderable
|
|
57
|
+
self.border_style = border_style
|
|
58
|
+
self.expand = expand
|
|
59
|
+
self.padding = padding
|
|
60
|
+
|
|
61
|
+
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
|
|
62
|
+
border_style = console.get_style(self.border_style)
|
|
63
|
+
max_width = options.max_width
|
|
64
|
+
pad = self.padding
|
|
65
|
+
|
|
66
|
+
# Measure the content width (account for padding)
|
|
67
|
+
if self.expand:
|
|
68
|
+
content_width = max_width - pad * 2
|
|
69
|
+
else:
|
|
70
|
+
content_width = console.measure(self.renderable, options=options.update(width=max_width - pad * 2)).maximum
|
|
71
|
+
content_width = min(content_width, max_width - pad * 2)
|
|
72
|
+
|
|
73
|
+
# Render content lines
|
|
74
|
+
child_options = options.update(width=content_width)
|
|
75
|
+
lines = console.render_lines(self.renderable, child_options)
|
|
76
|
+
|
|
77
|
+
# Calculate border width based on content width + padding
|
|
78
|
+
border_width = content_width + pad * 2
|
|
79
|
+
|
|
80
|
+
new_line = Segment.line()
|
|
81
|
+
pad_segment = Segment(" " * pad) if pad > 0 else None
|
|
82
|
+
|
|
83
|
+
# Top border: ┌───...───┐
|
|
84
|
+
top_border = (
|
|
85
|
+
TOP_LEFT + (HORIZONTAL * (border_width - 2)) + TOP_RIGHT if border_width >= 2 else HORIZONTAL * border_width
|
|
86
|
+
)
|
|
87
|
+
yield Segment(top_border, border_style)
|
|
88
|
+
yield new_line
|
|
89
|
+
|
|
90
|
+
# Content lines with padding
|
|
91
|
+
for line in lines:
|
|
92
|
+
if pad_segment:
|
|
93
|
+
yield pad_segment
|
|
94
|
+
yield from line
|
|
95
|
+
if pad_segment:
|
|
96
|
+
yield pad_segment
|
|
97
|
+
yield new_line
|
|
98
|
+
|
|
99
|
+
# Bottom border: └───...───┘
|
|
100
|
+
bottom_border = (
|
|
101
|
+
BOTTOM_LEFT + (HORIZONTAL * (border_width - 2)) + BOTTOM_RIGHT
|
|
102
|
+
if border_width >= 2
|
|
103
|
+
else HORIZONTAL * border_width
|
|
104
|
+
)
|
|
105
|
+
yield Segment(bottom_border, border_style)
|
|
106
|
+
yield new_line
|
|
107
|
+
|
|
108
|
+
def __rich_measure__(self, console: Console, options: ConsoleOptions) -> Measurement:
|
|
109
|
+
if self.expand:
|
|
110
|
+
return Measurement(options.max_width, options.max_width)
|
|
111
|
+
width = measure_renderables(console, options, [self.renderable]).maximum + self.padding * 2
|
|
112
|
+
return Measurement(width, width)
|
klaude_code/ui/rich/markdown.py
CHANGED
|
@@ -7,11 +7,9 @@ import time
|
|
|
7
7
|
from collections.abc import Callable
|
|
8
8
|
from typing import Any, ClassVar
|
|
9
9
|
|
|
10
|
-
from rich import box
|
|
11
10
|
from rich.console import Console, ConsoleOptions, Group, RenderableType, RenderResult
|
|
12
11
|
from rich.live import Live
|
|
13
12
|
from rich.markdown import CodeBlock, Heading, Markdown
|
|
14
|
-
from rich.panel import Panel
|
|
15
13
|
from rich.rule import Rule
|
|
16
14
|
from rich.spinner import Spinner
|
|
17
15
|
from rich.style import Style
|
|
@@ -20,6 +18,7 @@ from rich.text import Text
|
|
|
20
18
|
from rich.theme import Theme
|
|
21
19
|
|
|
22
20
|
from klaude_code import const
|
|
21
|
+
from klaude_code.ui.rich.code_panel import CodePanel
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
class NoInsetCodeBlock(CodeBlock):
|
|
@@ -34,7 +33,7 @@ class NoInsetCodeBlock(CodeBlock):
|
|
|
34
33
|
word_wrap=True,
|
|
35
34
|
padding=(0, 0),
|
|
36
35
|
)
|
|
37
|
-
yield
|
|
36
|
+
yield CodePanel(syntax, border_style="markdown.code.border")
|
|
38
37
|
|
|
39
38
|
|
|
40
39
|
class ThinkingCodeBlock(CodeBlock):
|
|
@@ -43,7 +42,7 @@ class ThinkingCodeBlock(CodeBlock):
|
|
|
43
42
|
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
|
|
44
43
|
code = str(self.text).rstrip()
|
|
45
44
|
text = Text(code, "markdown.code.block")
|
|
46
|
-
yield text
|
|
45
|
+
yield CodePanel(text, border_style="markdown.code.border")
|
|
47
46
|
|
|
48
47
|
|
|
49
48
|
class LeftHeading(Heading):
|
klaude_code/ui/rich/status.py
CHANGED
klaude_code/ui/rich/theme.py
CHANGED
|
@@ -86,6 +86,7 @@ class ThemeKey(str, Enum):
|
|
|
86
86
|
# SPINNER_STATUS
|
|
87
87
|
SPINNER_STATUS = "spinner.status"
|
|
88
88
|
SPINNER_STATUS_TEXT = "spinner.status.text"
|
|
89
|
+
SPINNER_STATUS_TEXT_BOLD = "spinner.status.text.bold"
|
|
89
90
|
# STATUS
|
|
90
91
|
STATUS_HINT = "status.hint"
|
|
91
92
|
# USER_INPUT
|
|
@@ -103,6 +104,7 @@ class ThemeKey(str, Enum):
|
|
|
103
104
|
TOOL_PARAM = "tool.param"
|
|
104
105
|
TOOL_PARAM_BOLD = "tool.param.bold"
|
|
105
106
|
TOOL_RESULT = "tool.result"
|
|
107
|
+
TOOL_RESULT_TRUNCATED = "tool.result.truncated"
|
|
106
108
|
TOOL_RESULT_BOLD = "tool.result.bold"
|
|
107
109
|
TOOL_MARK = "tool.mark"
|
|
108
110
|
TOOL_APPROVED = "tool.approved"
|
|
@@ -181,6 +183,7 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
181
183
|
# SPINNER_STATUS
|
|
182
184
|
ThemeKey.SPINNER_STATUS.value: palette.blue,
|
|
183
185
|
ThemeKey.SPINNER_STATUS_TEXT.value: palette.blue,
|
|
186
|
+
ThemeKey.SPINNER_STATUS_TEXT_BOLD.value: "bold " + palette.blue,
|
|
184
187
|
# STATUS
|
|
185
188
|
ThemeKey.STATUS_HINT.value: palette.grey2,
|
|
186
189
|
# REMINDER
|
|
@@ -194,6 +197,7 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
194
197
|
ThemeKey.TOOL_PARAM_BOLD.value: "bold " + palette.green,
|
|
195
198
|
ThemeKey.TOOL_RESULT.value: palette.grey_green,
|
|
196
199
|
ThemeKey.TOOL_RESULT_BOLD.value: "bold " + palette.grey_green,
|
|
200
|
+
ThemeKey.TOOL_RESULT_TRUNCATED.value: palette.yellow,
|
|
197
201
|
ThemeKey.TOOL_MARK.value: "bold",
|
|
198
202
|
ThemeKey.TOOL_APPROVED.value: palette.green + " bold reverse",
|
|
199
203
|
ThemeKey.TOOL_REJECTED.value: palette.red + " bold reverse",
|
|
@@ -243,11 +247,15 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
243
247
|
"markdown.hr": palette.grey3,
|
|
244
248
|
"markdown.item.bullet": palette.grey2,
|
|
245
249
|
"markdown.item.number": palette.grey2,
|
|
250
|
+
"markdown.link": "underline " + palette.blue,
|
|
251
|
+
"markdown.link_url": "underline " + palette.blue,
|
|
246
252
|
}
|
|
247
253
|
),
|
|
248
254
|
thinking_markdown_theme=Theme(
|
|
249
255
|
styles={
|
|
250
256
|
"markdown.code": palette.grey1 + " italic on " + palette.text_background,
|
|
257
|
+
"markdown.code.block": palette.grey1,
|
|
258
|
+
"markdown.code.border": palette.grey3,
|
|
251
259
|
"markdown.h1": "bold reverse",
|
|
252
260
|
"markdown.h1.border": palette.grey3,
|
|
253
261
|
"markdown.h2.border": palette.grey3,
|
|
@@ -256,7 +264,8 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
256
264
|
"markdown.hr": palette.grey3,
|
|
257
265
|
"markdown.item.bullet": palette.grey2,
|
|
258
266
|
"markdown.item.number": palette.grey2,
|
|
259
|
-
"markdown.
|
|
267
|
+
"markdown.link": "underline " + palette.blue,
|
|
268
|
+
"markdown.link_url": "underline " + palette.blue,
|
|
260
269
|
"markdown.strong": "bold italic " + palette.grey1,
|
|
261
270
|
}
|
|
262
271
|
),
|
klaude_code/ui/utils/common.py
CHANGED
|
@@ -2,8 +2,6 @@ import re
|
|
|
2
2
|
import subprocess
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from klaude_code import const
|
|
6
|
-
|
|
7
5
|
LEADING_NEWLINES_REGEX = re.compile(r"^\n{2,}")
|
|
8
6
|
|
|
9
7
|
|
|
@@ -90,19 +88,3 @@ def show_path_with_tilde(path: Path | None = None):
|
|
|
90
88
|
return f"~/{relative_path}"
|
|
91
89
|
except ValueError:
|
|
92
90
|
return str(path)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def truncate_display(
|
|
96
|
-
text: str,
|
|
97
|
-
max_lines: int = const.TRUNCATE_DISPLAY_MAX_LINES,
|
|
98
|
-
max_line_length: int = const.TRUNCATE_DISPLAY_MAX_LINE_LENGTH,
|
|
99
|
-
) -> str:
|
|
100
|
-
lines = text.split("\n")
|
|
101
|
-
if len(lines) > max_lines:
|
|
102
|
-
lines = [*lines[:max_lines], "… (more " + str(len(lines) - max_lines) + " lines)"]
|
|
103
|
-
for i, line in enumerate(lines):
|
|
104
|
-
if len(line) > max_line_length:
|
|
105
|
-
lines[i] = (
|
|
106
|
-
line[:max_line_length] + "… (more " + str(len(line) - max_line_length) + " characters in this line)"
|
|
107
|
-
)
|
|
108
|
-
return "\n".join(lines)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: klaude-code
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.19
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Dist: anthropic>=0.66.0
|
|
6
6
|
Requires-Dist: ddgs>=9.9.3
|
|
@@ -41,6 +41,21 @@ To update:
|
|
|
41
41
|
uv tool upgrade klaude-code
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
Or use the built-in alias command:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
klaude update
|
|
48
|
+
klaude upgrade
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
To show version:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
klaude --version
|
|
55
|
+
klaude -v
|
|
56
|
+
klaude version
|
|
57
|
+
```
|
|
58
|
+
|
|
44
59
|
## Usage
|
|
45
60
|
|
|
46
61
|
### Interactive Mode
|
|
@@ -50,13 +65,18 @@ klaude [--model <name>] [--select-model]
|
|
|
50
65
|
```
|
|
51
66
|
|
|
52
67
|
**Options:**
|
|
53
|
-
- `--version`/`-V`: Show version and exit.
|
|
54
|
-
- `--model`/`-m`:
|
|
55
|
-
- `--select-model`/`-s`:
|
|
68
|
+
- `--version`/`-V`/`-v`: Show version and exit.
|
|
69
|
+
- `--model`/`-m`: Preferred model name (exact match picks immediately; otherwise opens the interactive selector filtered by this value).
|
|
70
|
+
- `--select-model`/`-s`: Open the interactive model selector at startup (shows all models unless `--model` is also provided).
|
|
56
71
|
- `--continue`/`-c`: Resume the most recent session.
|
|
57
72
|
- `--resume`/`-r`: Select a session to resume for this project.
|
|
58
73
|
- `--vanilla`: Minimal mode with only basic tools (Bash, Read, Edit) and no system prompts.
|
|
59
74
|
|
|
75
|
+
**Model selection behavior:**
|
|
76
|
+
- Default: uses `main_model` from config.
|
|
77
|
+
- `--select-model`: always prompts you to pick.
|
|
78
|
+
- `--model <value>`: tries to resolve `<value>` to a single model; if it can't, it prompts with a filtered list (and falls back to showing all models if there are no matches).
|
|
79
|
+
|
|
60
80
|
**Debug Options:**
|
|
61
81
|
- `--debug`/`-d`: Enable debug mode with verbose logging and LLM trace.
|
|
62
82
|
- `--debug-filter`: Filter debug output by type (comma-separated).
|
|
@@ -64,7 +84,7 @@ klaude [--model <name>] [--select-model]
|
|
|
64
84
|
|
|
65
85
|
### Configuration
|
|
66
86
|
|
|
67
|
-
An example config will be created in `~/.klaude/config.yaml` when first run.
|
|
87
|
+
An example config will be created in `~/.klaude/klaude-config.yaml` when first run.
|
|
68
88
|
|
|
69
89
|
Open the configuration file in editor:
|
|
70
90
|
|
|
@@ -201,7 +221,7 @@ sub_agent_models:
|
|
|
201
221
|
oracle: gpt-5.1
|
|
202
222
|
explore: haiku
|
|
203
223
|
task: opus
|
|
204
|
-
|
|
224
|
+
webagent: haiku
|
|
205
225
|
|
|
206
226
|
```
|
|
207
227
|
|
|
@@ -263,5 +283,10 @@ klaude exec "what is 2+2?"
|
|
|
263
283
|
echo "hello world" | klaude exec
|
|
264
284
|
|
|
265
285
|
# With model selection
|
|
286
|
+
|
|
287
|
+
# Exact model name (non-interactive)
|
|
266
288
|
echo "generate quicksort in python" | klaude exec --model gpt-5.1
|
|
267
|
-
|
|
289
|
+
|
|
290
|
+
# Partial/ambiguous name opens the interactive selector (filtered)
|
|
291
|
+
echo "generate quicksort in python" | klaude exec --model gpt
|
|
292
|
+
```
|