klaude-code 1.2.14__py3-none-any.whl → 1.2.16__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/main.py +66 -42
- klaude_code/cli/runtime.py +24 -13
- klaude_code/command/export_cmd.py +2 -2
- klaude_code/command/prompt-handoff.md +33 -0
- klaude_code/command/thinking_cmd.py +6 -2
- klaude_code/config/config.py +5 -5
- klaude_code/config/list_model.py +1 -1
- klaude_code/const/__init__.py +3 -0
- klaude_code/core/executor.py +2 -2
- klaude_code/core/manager/llm_clients_builder.py +1 -1
- klaude_code/core/manager/sub_agent_manager.py +30 -6
- klaude_code/core/prompt.py +15 -13
- klaude_code/core/prompts/{prompt-subagent-explore.md → prompt-sub-agent-explore.md} +0 -1
- klaude_code/core/prompts/{prompt-subagent-oracle.md → prompt-sub-agent-oracle.md} +1 -1
- klaude_code/core/reminders.py +75 -32
- klaude_code/core/task.py +10 -22
- klaude_code/core/tool/__init__.py +2 -0
- klaude_code/core/tool/report_back_tool.py +58 -0
- klaude_code/core/tool/sub_agent_tool.py +6 -0
- klaude_code/core/tool/tool_runner.py +9 -1
- klaude_code/core/turn.py +45 -4
- klaude_code/llm/anthropic/input.py +14 -5
- klaude_code/llm/input_common.py +1 -1
- klaude_code/llm/openrouter/input.py +14 -3
- klaude_code/llm/responses/input.py +19 -0
- klaude_code/protocol/events.py +1 -0
- klaude_code/protocol/model.py +24 -14
- 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_fetch.py +74 -0
- klaude_code/protocol/tools.py +1 -0
- klaude_code/session/export.py +12 -6
- klaude_code/session/session.py +12 -2
- klaude_code/session/templates/export_session.html +20 -24
- klaude_code/ui/modes/repl/completers.py +1 -1
- klaude_code/ui/modes/repl/event_handler.py +34 -3
- klaude_code/ui/modes/repl/renderer.py +9 -9
- klaude_code/ui/renderers/developer.py +18 -7
- klaude_code/ui/renderers/metadata.py +57 -84
- klaude_code/ui/renderers/sub_agent.py +59 -3
- klaude_code/ui/renderers/thinking.py +3 -3
- klaude_code/ui/renderers/tools.py +67 -30
- klaude_code/ui/rich/markdown.py +45 -57
- klaude_code/ui/rich/status.py +32 -14
- klaude_code/ui/rich/theme.py +18 -17
- {klaude_code-1.2.14.dist-info → klaude_code-1.2.16.dist-info}/METADATA +3 -2
- {klaude_code-1.2.14.dist-info → klaude_code-1.2.16.dist-info}/RECORD +53 -47
- klaude_code/protocol/sub_agent.py +0 -354
- /klaude_code/core/prompts/{prompt-subagent-webfetch.md → prompt-sub-agent-webfetch.md} +0 -0
- /klaude_code/core/prompts/{prompt-subagent.md → prompt-sub-agent.md} +0 -0
- {klaude_code-1.2.14.dist-info → klaude_code-1.2.16.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.14.dist-info → klaude_code-1.2.16.dist-info}/entry_points.txt +0 -0
klaude_code/ui/rich/markdown.py
CHANGED
|
@@ -4,8 +4,10 @@ from __future__ import annotations
|
|
|
4
4
|
import contextlib
|
|
5
5
|
import io
|
|
6
6
|
import time
|
|
7
|
+
from collections.abc import Callable
|
|
7
8
|
from typing import Any, ClassVar
|
|
8
9
|
|
|
10
|
+
from rich import box
|
|
9
11
|
from rich.console import Console, ConsoleOptions, Group, RenderableType, RenderResult
|
|
10
12
|
from rich.live import Live
|
|
11
13
|
from rich.markdown import CodeBlock, Heading, Markdown
|
|
@@ -30,9 +32,18 @@ class NoInsetCodeBlock(CodeBlock):
|
|
|
30
32
|
self.lexer_name,
|
|
31
33
|
theme=self.theme,
|
|
32
34
|
word_wrap=True,
|
|
33
|
-
padding=(0,
|
|
35
|
+
padding=(0, 0),
|
|
34
36
|
)
|
|
35
|
-
yield Panel.fit(syntax, padding=0, border_style="markdown.code.
|
|
37
|
+
yield Panel.fit(syntax, padding=(0, 0), box=box.HORIZONTALS, border_style="markdown.code.border")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ThinkingCodeBlock(CodeBlock):
|
|
41
|
+
"""A code block for thinking content that uses grey styling instead of syntax highlighting."""
|
|
42
|
+
|
|
43
|
+
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
|
|
44
|
+
code = str(self.text).rstrip()
|
|
45
|
+
text = Text(code, "markdown.code.block")
|
|
46
|
+
yield text
|
|
36
47
|
|
|
37
48
|
|
|
38
49
|
class LeftHeading(Heading):
|
|
@@ -41,16 +52,10 @@ class LeftHeading(Heading):
|
|
|
41
52
|
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
|
|
42
53
|
text = self.text
|
|
43
54
|
text.justify = "left" # Override justification
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# yield Panel(
|
|
49
|
-
# text,
|
|
50
|
-
# box=box.SQUARE,
|
|
51
|
-
# style="markdown.h1.border",
|
|
52
|
-
# )
|
|
53
|
-
if self.tag == "h2":
|
|
55
|
+
if self.tag == "h1":
|
|
56
|
+
h1_text = text.assemble((" ", "markdown.h1"), text, (" ", "markdown.h1"))
|
|
57
|
+
yield h1_text
|
|
58
|
+
elif self.tag == "h2":
|
|
54
59
|
text.stylize(Style(bold=True, underline=False))
|
|
55
60
|
yield Rule(title=text, characters="-", style="markdown.h2.border", align="left")
|
|
56
61
|
else:
|
|
@@ -68,6 +73,17 @@ class NoInsetMarkdown(Markdown):
|
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
|
|
76
|
+
class ThinkingMarkdown(Markdown):
|
|
77
|
+
"""Markdown for thinking content with grey-styled code blocks and left-justified headings."""
|
|
78
|
+
|
|
79
|
+
elements: ClassVar[dict[str, type[Any]]] = {
|
|
80
|
+
**Markdown.elements,
|
|
81
|
+
"fence": ThinkingCodeBlock,
|
|
82
|
+
"code_block": ThinkingCodeBlock,
|
|
83
|
+
"heading_open": LeftHeading,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
71
87
|
class MarkdownStream:
|
|
72
88
|
"""Streaming markdown renderer that progressively displays content with a live updating window.
|
|
73
89
|
|
|
@@ -84,6 +100,7 @@ class MarkdownStream:
|
|
|
84
100
|
spinner: Spinner | None = None,
|
|
85
101
|
mark: str | None = None,
|
|
86
102
|
indent: int = 0,
|
|
103
|
+
markdown_class: Callable[..., Markdown] | None = None,
|
|
87
104
|
) -> None:
|
|
88
105
|
"""Initialize the markdown stream.
|
|
89
106
|
|
|
@@ -93,6 +110,7 @@ class MarkdownStream:
|
|
|
93
110
|
console (Console, optional): External console to use for rendering
|
|
94
111
|
mark (str | None, optional): Marker shown before the first non-empty line when indent >= 2
|
|
95
112
|
indent (int, optional): Number of spaces to indent all rendered lines on the left
|
|
113
|
+
markdown_class: Markdown class to use for rendering (defaults to NoInsetMarkdown)
|
|
96
114
|
"""
|
|
97
115
|
self.printed: list[str] = [] # Stores lines that have already been printed
|
|
98
116
|
|
|
@@ -108,16 +126,13 @@ class MarkdownStream:
|
|
|
108
126
|
self.when: float = 0.0 # Timestamp of last update
|
|
109
127
|
self.min_delay: float = 1.0 / 20 # Minimum time between updates (20fps)
|
|
110
128
|
self.live_window: int = const.MARKDOWN_STREAM_LIVE_WINDOW
|
|
111
|
-
# Track the maximum height the live window has ever reached
|
|
112
|
-
# so we only pad when it shrinks from a previous height,
|
|
113
|
-
# instead of always padding to live_window from the start.
|
|
114
|
-
self._live_window_seen_height: int = 0
|
|
115
129
|
|
|
116
130
|
self.theme = theme
|
|
117
131
|
self.console = console
|
|
118
132
|
self.spinner: Spinner | None = spinner
|
|
119
133
|
self.mark: str | None = mark
|
|
120
134
|
self.indent: int = max(indent, 0)
|
|
135
|
+
self.markdown_class: Callable[..., Markdown] = markdown_class or NoInsetMarkdown
|
|
121
136
|
|
|
122
137
|
@property
|
|
123
138
|
def _live_started(self) -> bool:
|
|
@@ -154,7 +169,7 @@ class MarkdownStream:
|
|
|
154
169
|
width=effective_width,
|
|
155
170
|
)
|
|
156
171
|
|
|
157
|
-
markdown =
|
|
172
|
+
markdown = self.markdown_class(text, **self.mdargs)
|
|
158
173
|
temp_console.print(markdown)
|
|
159
174
|
output = string_io.getvalue()
|
|
160
175
|
|
|
@@ -205,18 +220,16 @@ class MarkdownStream:
|
|
|
205
220
|
Markdown going to the console works better in terminal scrollback buffers.
|
|
206
221
|
The live window doesn't play nice with terminal scrollback.
|
|
207
222
|
"""
|
|
208
|
-
# On the first call, start the Live renderer
|
|
209
223
|
if not self._live_started:
|
|
210
224
|
initial_content = self._live_renderable(Text(""), final=False)
|
|
225
|
+
# transient=False keeps final frame on screen after stop()
|
|
211
226
|
self.live = Live(
|
|
212
227
|
initial_content,
|
|
213
228
|
refresh_per_second=1.0 / self.min_delay,
|
|
214
229
|
console=self.console,
|
|
215
230
|
)
|
|
216
231
|
self.live.start()
|
|
217
|
-
# Note: self._live_started is now a property derived from self.live
|
|
218
232
|
|
|
219
|
-
# If live rendering isn't available (e.g., after a final update), stop.
|
|
220
233
|
if self.live is None:
|
|
221
234
|
return
|
|
222
235
|
|
|
@@ -236,61 +249,36 @@ class MarkdownStream:
|
|
|
236
249
|
|
|
237
250
|
num_lines = len(lines)
|
|
238
251
|
|
|
239
|
-
#
|
|
240
|
-
|
|
241
|
-
if not final:
|
|
242
|
-
num_lines = max(num_lines - self.live_window, 0)
|
|
252
|
+
# Reserve last live_window lines for Live area to keep height stable
|
|
253
|
+
num_lines = max(num_lines - self.live_window, 0)
|
|
243
254
|
|
|
244
|
-
#
|
|
245
|
-
|
|
246
|
-
if final or num_lines > 0:
|
|
247
|
-
# Lines to append to stable area
|
|
255
|
+
# Print new stable lines above Live window
|
|
256
|
+
if num_lines > 0:
|
|
248
257
|
num_printed = len(self.printed)
|
|
249
258
|
to_append_count = num_lines - num_printed
|
|
250
259
|
|
|
251
260
|
if to_append_count > 0:
|
|
252
|
-
# Print new stable lines above Live window
|
|
253
261
|
append_chunk = lines[num_printed:num_lines]
|
|
254
262
|
append_chunk_text = Text.from_ansi("".join(append_chunk))
|
|
255
263
|
live = self.live
|
|
256
264
|
assert live is not None
|
|
257
|
-
live.console.print(append_chunk_text)
|
|
258
|
-
|
|
259
|
-
# Track printed stable lines
|
|
265
|
+
live.console.print(append_chunk_text)
|
|
260
266
|
self.printed = lines[:num_lines]
|
|
261
267
|
|
|
262
|
-
|
|
268
|
+
rest_lines = lines[num_lines:]
|
|
269
|
+
|
|
270
|
+
# Final: render remaining lines without spinner, then stop Live
|
|
263
271
|
if final:
|
|
264
272
|
live = self.live
|
|
265
273
|
assert live is not None
|
|
266
|
-
|
|
274
|
+
rest = "".join(rest_lines)
|
|
275
|
+
rest_text = Text.from_ansi(rest)
|
|
276
|
+
final_renderable = self._live_renderable(rest_text, final=True)
|
|
277
|
+
live.update(final_renderable)
|
|
267
278
|
live.stop()
|
|
268
279
|
self.live = None
|
|
269
280
|
return
|
|
270
281
|
|
|
271
|
-
# Update Live window to prevent timing issues
|
|
272
|
-
# with console.print above. We pad the live region
|
|
273
|
-
# so that its height stays stable when it shrinks
|
|
274
|
-
# from a previously reached height, avoiding spinner jitter.
|
|
275
|
-
rest_lines = lines[num_lines:]
|
|
276
|
-
|
|
277
|
-
if not final:
|
|
278
|
-
current_height = len(rest_lines)
|
|
279
|
-
|
|
280
|
-
# Update the maximum height we've seen so far for this live window.
|
|
281
|
-
if current_height > self._live_window_seen_height:
|
|
282
|
-
# Never exceed configured live_window, even if logic changes later.
|
|
283
|
-
self._live_window_seen_height = min(current_height, self.live_window)
|
|
284
|
-
|
|
285
|
-
target_height = min(self._live_window_seen_height, self.live_window)
|
|
286
|
-
if target_height > 0 and current_height < target_height:
|
|
287
|
-
# Pad only up to the maximum height we've seen so far.
|
|
288
|
-
# This keeps the Live region height stable without overshooting,
|
|
289
|
-
# which can cause the spinner to jump by a line.
|
|
290
|
-
pad_count = target_height - current_height
|
|
291
|
-
# Pad after the existing lines so spinner visually stays at the bottom.
|
|
292
|
-
rest_lines = rest_lines + ["\n"] * pad_count
|
|
293
|
-
|
|
294
282
|
rest = "".join(rest_lines)
|
|
295
283
|
rest = Text.from_ansi(rest)
|
|
296
284
|
live = self.live
|
klaude_code/ui/rich/status.py
CHANGED
|
@@ -2,10 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
4
|
import math
|
|
5
|
+
import random
|
|
5
6
|
import time
|
|
6
7
|
|
|
7
8
|
import rich.status as rich_status
|
|
8
|
-
from rich._spinners import SPINNERS
|
|
9
9
|
from rich.color import Color
|
|
10
10
|
from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
|
|
11
11
|
from rich.spinner import Spinner as RichSpinner
|
|
@@ -17,18 +17,25 @@ from klaude_code import const
|
|
|
17
17
|
from klaude_code.ui.rich.theme import ThemeKey
|
|
18
18
|
from klaude_code.ui.terminal.color import get_last_terminal_background_rgb
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
# Use an existing Rich spinner name; BreathingSpinner overrides its rendering
|
|
21
|
+
BREATHING_SPINNER_NAME = "dots"
|
|
22
|
+
|
|
23
|
+
# Alternating glyphs for the breathing spinner - switches at each "transparent" point
|
|
24
|
+
# All glyphs are center-symmetric (point-symmetric)
|
|
25
|
+
_BREATHING_SPINNER_GLYPHS_BASE = [
|
|
26
|
+
# Stars
|
|
27
|
+
"✦",
|
|
28
|
+
"✶",
|
|
29
|
+
"✲",
|
|
30
|
+
"⏺",
|
|
31
|
+
"◆",
|
|
32
|
+
"❖",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
# Shuffle glyphs on module load for variety across sessions
|
|
36
|
+
BREATHING_SPINNER_GLYPHS = _BREATHING_SPINNER_GLYPHS_BASE.copy()
|
|
37
|
+
random.shuffle(BREATHING_SPINNER_GLYPHS)
|
|
21
38
|
|
|
22
|
-
SPINNERS.update(
|
|
23
|
-
{
|
|
24
|
-
BREATHING_SPINNER_NAME: {
|
|
25
|
-
"interval": 100,
|
|
26
|
-
# Frames content is ignored by the custom breathing spinner implementation,
|
|
27
|
-
# but we keep a single-frame list for correct width measurement.
|
|
28
|
-
"frames": ["⏺"],
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
)
|
|
32
39
|
|
|
33
40
|
_process_start: float | None = None
|
|
34
41
|
|
|
@@ -126,6 +133,17 @@ def _breathing_intensity() -> float:
|
|
|
126
133
|
return 0.5 * (1.0 - math.cos(2.0 * math.pi * phase))
|
|
127
134
|
|
|
128
135
|
|
|
136
|
+
def _breathing_glyph() -> str:
|
|
137
|
+
"""Get the current glyph for the breathing spinner.
|
|
138
|
+
|
|
139
|
+
Alternates between glyphs at each breath cycle (when intensity reaches 0).
|
|
140
|
+
"""
|
|
141
|
+
period = max(const.SPINNER_BREATH_PERIOD_SECONDS, 0.1)
|
|
142
|
+
elapsed = _elapsed_since_start()
|
|
143
|
+
cycle = int(elapsed / period)
|
|
144
|
+
return BREATHING_SPINNER_GLYPHS[cycle % len(BREATHING_SPINNER_GLYPHS)]
|
|
145
|
+
|
|
146
|
+
|
|
129
147
|
def _breathing_style(console: Console, base_style: Style, intensity: float) -> Style:
|
|
130
148
|
"""Blend a base style's foreground color toward terminal background.
|
|
131
149
|
|
|
@@ -159,7 +177,7 @@ class ShimmerStatusText:
|
|
|
159
177
|
def __init__(self, main_text: str | Text, main_style: ThemeKey) -> None:
|
|
160
178
|
self._main_text = main_text if isinstance(main_text, Text) else Text(main_text)
|
|
161
179
|
self._main_style = main_style
|
|
162
|
-
self._hint_text = Text(
|
|
180
|
+
self._hint_text = Text(const.STATUS_HINT_TEXT)
|
|
163
181
|
self._hint_style = ThemeKey.STATUS_HINT
|
|
164
182
|
|
|
165
183
|
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
|
|
@@ -219,7 +237,7 @@ class BreathingSpinner(RichSpinner):
|
|
|
219
237
|
intensity = _breathing_intensity()
|
|
220
238
|
style = _breathing_style(console, base_style, intensity)
|
|
221
239
|
|
|
222
|
-
glyph =
|
|
240
|
+
glyph = _breathing_glyph()
|
|
223
241
|
frame = Text(glyph, style=style)
|
|
224
242
|
|
|
225
243
|
if not self.text:
|
klaude_code/ui/rich/theme.py
CHANGED
|
@@ -14,12 +14,12 @@ class Palette:
|
|
|
14
14
|
blue: str
|
|
15
15
|
orange: str
|
|
16
16
|
magenta: str
|
|
17
|
-
grey_blue: str
|
|
18
17
|
grey1: str
|
|
19
18
|
grey2: str
|
|
20
19
|
grey3: str
|
|
21
20
|
grey_green: str
|
|
22
21
|
purple: str
|
|
22
|
+
lavender: str
|
|
23
23
|
diff_add: str
|
|
24
24
|
diff_remove: str
|
|
25
25
|
code_theme: str
|
|
@@ -29,17 +29,17 @@ class Palette:
|
|
|
29
29
|
LIGHT_PALETTE = Palette(
|
|
30
30
|
red="red",
|
|
31
31
|
yellow="yellow",
|
|
32
|
-
green="
|
|
32
|
+
green="#00875f",
|
|
33
33
|
cyan="cyan",
|
|
34
|
-
blue="#
|
|
34
|
+
blue="#3078C5",
|
|
35
35
|
orange="#d77757",
|
|
36
36
|
magenta="magenta",
|
|
37
|
-
grey_blue="steel_blue",
|
|
38
37
|
grey1="#667e90",
|
|
39
38
|
grey2="#93a4b1",
|
|
40
39
|
grey3="#c4ced4",
|
|
41
|
-
grey_green="#
|
|
42
|
-
purple="
|
|
40
|
+
grey_green="#96a096",
|
|
41
|
+
purple="#5f5fd7",
|
|
42
|
+
lavender="#5f87af",
|
|
43
43
|
diff_add="#2e5a32 on #e8f5e9",
|
|
44
44
|
diff_remove="#5a2e32 on #ffebee",
|
|
45
45
|
code_theme="ansi_light",
|
|
@@ -47,19 +47,19 @@ LIGHT_PALETTE = Palette(
|
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
DARK_PALETTE = Palette(
|
|
50
|
-
red="
|
|
50
|
+
red="#d75f5f",
|
|
51
51
|
yellow="yellow",
|
|
52
|
-
green="
|
|
52
|
+
green="#5fd787",
|
|
53
53
|
cyan="cyan",
|
|
54
|
-
blue="
|
|
54
|
+
blue="#00afff",
|
|
55
55
|
orange="#e6704e",
|
|
56
56
|
magenta="magenta",
|
|
57
|
-
grey_blue="steel_blue",
|
|
58
57
|
grey1="#99aabb",
|
|
59
58
|
grey2="#778899",
|
|
60
59
|
grey3="#646464",
|
|
61
60
|
grey_green="#6d8672",
|
|
62
61
|
purple="#afbafe",
|
|
62
|
+
lavender="#9898b8",
|
|
63
63
|
diff_add="#c8e6c9 on #2e4a32",
|
|
64
64
|
diff_remove="#ffcdd2 on #4a2e32",
|
|
65
65
|
code_theme="ansi_dark",
|
|
@@ -107,6 +107,7 @@ class ThemeKey(str, Enum):
|
|
|
107
107
|
TOOL_MARK = "tool.mark"
|
|
108
108
|
TOOL_APPROVED = "tool.approved"
|
|
109
109
|
TOOL_REJECTED = "tool.rejected"
|
|
110
|
+
TOOL_TIMEOUT = "tool.timeout"
|
|
110
111
|
# THINKING
|
|
111
112
|
THINKING = "thinking"
|
|
112
113
|
THINKING_BOLD = "thinking.bold"
|
|
@@ -174,9 +175,9 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
174
175
|
ThemeKey.USER_INPUT_AT_PATTERN.value: palette.purple,
|
|
175
176
|
ThemeKey.USER_INPUT_SLASH_COMMAND.value: "bold reverse " + palette.blue,
|
|
176
177
|
# METADATA
|
|
177
|
-
ThemeKey.METADATA.value: palette.
|
|
178
|
-
ThemeKey.METADATA_DIM.value: "dim " + palette.
|
|
179
|
-
ThemeKey.METADATA_BOLD.value: "bold " + palette.
|
|
178
|
+
ThemeKey.METADATA.value: palette.lavender,
|
|
179
|
+
ThemeKey.METADATA_DIM.value: "dim " + palette.lavender,
|
|
180
|
+
ThemeKey.METADATA_BOLD.value: "bold " + palette.lavender,
|
|
180
181
|
# SPINNER_STATUS
|
|
181
182
|
ThemeKey.SPINNER_STATUS.value: palette.blue,
|
|
182
183
|
ThemeKey.SPINNER_STATUS_TEXT.value: palette.blue,
|
|
@@ -196,6 +197,7 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
196
197
|
ThemeKey.TOOL_MARK.value: "bold",
|
|
197
198
|
ThemeKey.TOOL_APPROVED.value: palette.green + " bold reverse",
|
|
198
199
|
ThemeKey.TOOL_REJECTED.value: palette.red + " bold reverse",
|
|
200
|
+
ThemeKey.TOOL_TIMEOUT.value: palette.yellow,
|
|
199
201
|
# THINKING
|
|
200
202
|
ThemeKey.THINKING.value: "italic " + palette.grey2,
|
|
201
203
|
ThemeKey.THINKING_BOLD.value: "bold italic " + palette.grey1,
|
|
@@ -232,7 +234,7 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
232
234
|
markdown_theme=Theme(
|
|
233
235
|
styles={
|
|
234
236
|
"markdown.code": palette.purple,
|
|
235
|
-
"markdown.code.
|
|
237
|
+
"markdown.code.border": palette.grey3,
|
|
236
238
|
"markdown.h1": "bold reverse",
|
|
237
239
|
"markdown.h1.border": palette.grey3,
|
|
238
240
|
"markdown.h2.border": palette.grey3,
|
|
@@ -245,8 +247,7 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
245
247
|
),
|
|
246
248
|
thinking_markdown_theme=Theme(
|
|
247
249
|
styles={
|
|
248
|
-
"markdown.code": palette.grey1 + " on " + palette.text_background,
|
|
249
|
-
"markdown.code.panel": palette.grey3,
|
|
250
|
+
"markdown.code": palette.grey1 + " italic on " + palette.text_background,
|
|
250
251
|
"markdown.h1": "bold reverse",
|
|
251
252
|
"markdown.h1.border": palette.grey3,
|
|
252
253
|
"markdown.h2.border": palette.grey3,
|
|
@@ -255,6 +256,7 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
255
256
|
"markdown.hr": palette.grey3,
|
|
256
257
|
"markdown.item.bullet": palette.grey2,
|
|
257
258
|
"markdown.item.number": palette.grey2,
|
|
259
|
+
"markdown.code.block": palette.grey1,
|
|
258
260
|
"markdown.strong": "bold italic " + palette.grey1,
|
|
259
261
|
}
|
|
260
262
|
),
|
|
@@ -265,7 +267,6 @@ def get_theme(theme: str | None = None) -> Themes:
|
|
|
265
267
|
Style(color=palette.blue),
|
|
266
268
|
Style(color=palette.purple),
|
|
267
269
|
Style(color=palette.orange),
|
|
268
|
-
Style(color=palette.grey_blue),
|
|
269
270
|
Style(color=palette.red),
|
|
270
271
|
Style(color=palette.grey1),
|
|
271
272
|
Style(color=palette.yellow),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: klaude-code
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.16
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Dist: anthropic>=0.66.0
|
|
6
6
|
Requires-Dist: openai>=1.102.0
|
|
@@ -22,6 +22,7 @@ An minimal and opinionated code agent with multi-model support.
|
|
|
22
22
|
## Key Features
|
|
23
23
|
- **Adaptive Tooling**: Model-aware toolsets (Claude Code tools for Sonnet, Codex `apply_patch` for GPT-5.1/Codex).
|
|
24
24
|
- **Multi-Provider Support**: Compatible with `anthropic-messages-api`,`openai-responses-api`, and `openai-compatible-api`(`openrouter-api`), featuring interleaved thinking, OpenRouter's provider sorting etc.
|
|
25
|
+
- **Structured Sub-Agent Output**: Main agent defines output JSON schema for sub-agents; sub-agents use `report_back` tool with constrained decoding to return schema-compliant structured data.
|
|
25
26
|
- **Skill System**: Extensible support for loading Claude Skills.
|
|
26
27
|
- **Session Management**: Robust context preservation with resumable sessions (`--continue`).
|
|
27
28
|
- **Simple TUI**: Clean interface offering full visibility into model responses, reasoning and actions.
|
|
@@ -107,7 +108,7 @@ model_list:
|
|
|
107
108
|
provider_routing:
|
|
108
109
|
sort: throughput
|
|
109
110
|
main_model: gpt-5.1-codex
|
|
110
|
-
|
|
111
|
+
sub_agent_models:
|
|
111
112
|
oracle: gpt-5.1-high
|
|
112
113
|
explore: haiku
|
|
113
114
|
task: sonnet
|