klaude-code 1.2.16__py3-none-any.whl → 1.2.17__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/runtime.py +12 -2
- klaude_code/command/__init__.py +3 -0
- klaude_code/command/export_online_cmd.py +149 -0
- klaude_code/config/config.py +16 -17
- klaude_code/core/manager/sub_agent_manager.py +1 -1
- klaude_code/core/prompts/prompt-sub-agent-oracle.md +0 -1
- klaude_code/core/prompts/prompt-sub-agent-web.md +48 -0
- klaude_code/core/task.py +8 -0
- klaude_code/core/tool/__init__.py +2 -0
- klaude_code/core/tool/report_back_tool.py +28 -2
- klaude_code/core/tool/web/web_search_tool.md +23 -0
- klaude_code/core/tool/web/web_search_tool.py +126 -0
- klaude_code/protocol/commands.py +1 -0
- klaude_code/protocol/events.py +8 -0
- klaude_code/protocol/sub_agent/__init__.py +1 -1
- klaude_code/protocol/sub_agent/explore.py +1 -1
- klaude_code/protocol/sub_agent/web.py +78 -0
- klaude_code/protocol/tools.py +1 -0
- klaude_code/session/templates/export_session.html +99 -24
- klaude_code/ui/modes/repl/event_handler.py +36 -9
- klaude_code/ui/modes/repl/renderer.py +3 -3
- klaude_code/ui/renderers/sub_agent.py +14 -10
- klaude_code/ui/renderers/tools.py +4 -10
- klaude_code/ui/rich/status.py +32 -6
- {klaude_code-1.2.16.dist-info → klaude_code-1.2.17.dist-info}/METADATA +112 -21
- {klaude_code-1.2.16.dist-info → klaude_code-1.2.17.dist-info}/RECORD +28 -25
- klaude_code/core/prompts/prompt-sub-agent-webfetch.md +0 -46
- klaude_code/protocol/sub_agent/web_fetch.py +0 -74
- {klaude_code-1.2.16.dist-info → klaude_code-1.2.17.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.16.dist-info → klaude_code-1.2.17.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from klaude_code.protocol import tools
|
|
6
|
+
from klaude_code.protocol.sub_agent import SubAgentProfile, register_sub_agent
|
|
7
|
+
|
|
8
|
+
WEB_AGENT_DESCRIPTION = """\
|
|
9
|
+
Launch a sub-agent to search the web, fetch pages, and analyze content. Use this for:
|
|
10
|
+
- Accessing up-to-date information beyond your knowledge cutoff (current events, recent releases, latest docs)
|
|
11
|
+
- Researching topics, news, APIs, or technical references
|
|
12
|
+
- Fetching and analyzing specific URLs
|
|
13
|
+
- Gathering comprehensive information from multiple web sources
|
|
14
|
+
|
|
15
|
+
Capabilities:
|
|
16
|
+
- Search the web to find relevant pages (no URL required)
|
|
17
|
+
- Fetch and parse web pages (HTML-to-Markdown conversion)
|
|
18
|
+
- Follow links across multiple pages autonomously
|
|
19
|
+
- Aggregate findings from multiple sources
|
|
20
|
+
|
|
21
|
+
How to use:
|
|
22
|
+
- Write a clear prompt describing what information you need - the agent will search and fetch as needed
|
|
23
|
+
- Optionally provide a `url` if you already know the target page
|
|
24
|
+
- Use `output_format` (JSON Schema) to get structured data back from the agent
|
|
25
|
+
- Account for "Today's date" in <env>. For example, if <env> says "Today's date: 2025-07-01", and the user wants the latest docs, do not use 2024 in the search query. Use 2025.
|
|
26
|
+
|
|
27
|
+
What you receive:
|
|
28
|
+
- The agent returns a text response summarizing its findings
|
|
29
|
+
- With `output_format`, you receive structured JSON matching your schema
|
|
30
|
+
- The response is the agent's analysis, not raw web content\
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
WEB_AGENT_PARAMETERS = {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"properties": {
|
|
36
|
+
"description": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "A short (3-5 word) description of the task",
|
|
39
|
+
},
|
|
40
|
+
"url": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "The URL to fetch and analyze. If not provided, the agent will search the web first",
|
|
43
|
+
},
|
|
44
|
+
"prompt": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"description": "Instructions for searching, analyzing, or extracting content from the web page",
|
|
47
|
+
},
|
|
48
|
+
"output_format": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"description": "Optional JSON Schema for sub-agent structured output",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
"required": ["description", "prompt"],
|
|
54
|
+
"additionalProperties": False,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _web_agent_prompt_builder(args: dict[str, Any]) -> str:
|
|
59
|
+
"""Build the WebAgent prompt from tool arguments."""
|
|
60
|
+
url = args.get("url", "")
|
|
61
|
+
prompt = args.get("prompt", "")
|
|
62
|
+
if url:
|
|
63
|
+
return f"URL to fetch: {url}\nTask: {prompt}"
|
|
64
|
+
return prompt
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
register_sub_agent(
|
|
68
|
+
SubAgentProfile(
|
|
69
|
+
name="WebAgent",
|
|
70
|
+
description=WEB_AGENT_DESCRIPTION,
|
|
71
|
+
parameters=WEB_AGENT_PARAMETERS,
|
|
72
|
+
prompt_file="prompts/prompt-sub-agent-web.md",
|
|
73
|
+
tool_set=(tools.BASH, tools.READ, tools.WEB_FETCH, tools.WEB_SEARCH),
|
|
74
|
+
prompt_builder=_web_agent_prompt_builder,
|
|
75
|
+
active_form="Surfing",
|
|
76
|
+
output_schema_arg="output_format",
|
|
77
|
+
)
|
|
78
|
+
)
|
klaude_code/protocol/tools.py
CHANGED
|
@@ -672,8 +672,102 @@
|
|
|
672
672
|
/* Markdown Elements */
|
|
673
673
|
.markdown-body {
|
|
674
674
|
font-family: var(--font-markdown);
|
|
675
|
-
line-height: 1.
|
|
675
|
+
line-height: 1.6;
|
|
676
|
+
font-size: var(--font-size-base);
|
|
677
|
+
}
|
|
678
|
+
.markdown-body > *:first-child {
|
|
679
|
+
margin-top: 0 !important;
|
|
680
|
+
}
|
|
681
|
+
.markdown-body > *:last-child {
|
|
682
|
+
margin-bottom: 0 !important;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.markdown-body h1,
|
|
686
|
+
.markdown-body h2,
|
|
687
|
+
.markdown-body h3,
|
|
688
|
+
.markdown-body h4,
|
|
689
|
+
.markdown-body h5,
|
|
690
|
+
.markdown-body h6 {
|
|
691
|
+
margin-top: 24px;
|
|
692
|
+
margin-bottom: 16px;
|
|
693
|
+
font-weight: var(--font-weight-bold);
|
|
694
|
+
line-height: 1.25;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.markdown-body a {
|
|
698
|
+
color: var(--accent);
|
|
699
|
+
text-decoration: none;
|
|
700
|
+
border-bottom: 1px solid rgba(8, 81, 178, 0.2);
|
|
701
|
+
transition: border-color 0.2s, background-color 0.2s;
|
|
702
|
+
}
|
|
703
|
+
.markdown-body a:hover {
|
|
704
|
+
border-bottom-color: var(--accent);
|
|
705
|
+
background-color: rgba(8, 81, 178, 0.05);
|
|
706
|
+
border-radius: 2px;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.markdown-body p {
|
|
710
|
+
margin-bottom: 8px; /* Tighter spacing */
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.markdown-body ul,
|
|
714
|
+
.markdown-body ol {
|
|
715
|
+
margin-bottom: 8px; /* Tighter spacing */
|
|
716
|
+
padding-left: 1.5rem;
|
|
717
|
+
list-style-position: outside;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
.markdown-body ul ul,
|
|
721
|
+
.markdown-body ol ul,
|
|
722
|
+
.markdown-body ul ol,
|
|
723
|
+
.markdown-body ol ol {
|
|
724
|
+
margin-top: 0;
|
|
725
|
+
margin-bottom: 0;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
.markdown-body li > p {
|
|
729
|
+
margin-bottom: 0;
|
|
730
|
+
}
|
|
731
|
+
.markdown-body li + li {
|
|
732
|
+
margin-top: 0.25em;
|
|
676
733
|
}
|
|
734
|
+
|
|
735
|
+
.markdown-body blockquote {
|
|
736
|
+
margin: 0 0 16px;
|
|
737
|
+
padding: 0 1em;
|
|
738
|
+
color: var(--text-dim);
|
|
739
|
+
border-left: 0.25em solid var(--border);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
.markdown-body table {
|
|
743
|
+
border-collapse: collapse;
|
|
744
|
+
width: 100%;
|
|
745
|
+
margin-top: 8px;
|
|
746
|
+
margin-bottom: 16px;
|
|
747
|
+
display: block;
|
|
748
|
+
overflow-x: auto;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
.markdown-body table tr {
|
|
752
|
+
background-color: var(--bg-card);
|
|
753
|
+
border-top: 1px solid var(--border);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.markdown-body table tr:nth-child(2n) {
|
|
757
|
+
background-color: rgba(0, 0, 0, 0.02);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
.markdown-body table th,
|
|
761
|
+
.markdown-body table td {
|
|
762
|
+
padding: 6px 13px;
|
|
763
|
+
border: 1px solid var(--border);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.markdown-body table th {
|
|
767
|
+
font-weight: var(--font-weight-bold);
|
|
768
|
+
background-color: rgba(0, 0, 0, 0.04);
|
|
769
|
+
}
|
|
770
|
+
|
|
677
771
|
.markdown-body hr {
|
|
678
772
|
height: 0;
|
|
679
773
|
margin: 24px 0;
|
|
@@ -682,10 +776,10 @@
|
|
|
682
776
|
}
|
|
683
777
|
.markdown-body pre {
|
|
684
778
|
background: var(--bg-code);
|
|
685
|
-
padding:
|
|
779
|
+
padding: 12px;
|
|
686
780
|
border-radius: var(--radius-md);
|
|
687
781
|
overflow-x: auto;
|
|
688
|
-
margin:
|
|
782
|
+
margin: 8px 0 16px 0;
|
|
689
783
|
border: 1px solid var(--border);
|
|
690
784
|
}
|
|
691
785
|
.markdown-body code {
|
|
@@ -694,36 +788,17 @@
|
|
|
694
788
|
font-size: var(--font-size-sm);
|
|
695
789
|
padding: 2px 4px;
|
|
696
790
|
border-radius: var(--radius-sm);
|
|
791
|
+
background-color: rgba(0, 0, 0, 0.05); /* Slight bg for inline code */
|
|
697
792
|
}
|
|
698
793
|
.markdown-body pre code {
|
|
699
794
|
background: transparent;
|
|
700
795
|
padding: 0;
|
|
701
796
|
border-radius: 0;
|
|
797
|
+
color: inherit;
|
|
702
798
|
}
|
|
703
799
|
.markdown-body pre code.hljs {
|
|
704
800
|
background: transparent;
|
|
705
801
|
}
|
|
706
|
-
.markdown-body p {
|
|
707
|
-
margin-bottom: 12px;
|
|
708
|
-
}
|
|
709
|
-
.markdown-body > *:first-child {
|
|
710
|
-
margin-top: 0;
|
|
711
|
-
}
|
|
712
|
-
.markdown-body > *:last-child {
|
|
713
|
-
margin-bottom: 0;
|
|
714
|
-
}
|
|
715
|
-
.markdown-body ul,
|
|
716
|
-
.markdown-body ol {
|
|
717
|
-
margin-bottom: 12px;
|
|
718
|
-
padding-left: 1.5rem;
|
|
719
|
-
list-style-position: outside;
|
|
720
|
-
}
|
|
721
|
-
.markdown-body ul ul,
|
|
722
|
-
.markdown-body ol ul,
|
|
723
|
-
.markdown-body ul ol,
|
|
724
|
-
.markdown-body ol ol {
|
|
725
|
-
margin-left: 1rem;
|
|
726
|
-
}
|
|
727
802
|
|
|
728
803
|
/* Diff View */
|
|
729
804
|
.diff-view {
|
|
@@ -137,11 +137,13 @@ class SpinnerStatusState:
|
|
|
137
137
|
Composed of two independent layers:
|
|
138
138
|
- base_status: Set by TodoChange, persistent within a turn
|
|
139
139
|
- activity: Current activity (composing or tool_calls), mutually exclusive
|
|
140
|
+
- context_percent: Context usage percentage, updated during task execution
|
|
140
141
|
|
|
141
142
|
Display logic:
|
|
142
143
|
- If activity: show base + activity (if base exists) or activity + "..."
|
|
143
144
|
- Elif base_status: show base_status
|
|
144
145
|
- Else: show "Thinking …"
|
|
146
|
+
- Context percent is appended at the end if available
|
|
145
147
|
"""
|
|
146
148
|
|
|
147
149
|
DEFAULT_STATUS = "Thinking …"
|
|
@@ -149,11 +151,13 @@ class SpinnerStatusState:
|
|
|
149
151
|
def __init__(self) -> None:
|
|
150
152
|
self._base_status: str | None = None
|
|
151
153
|
self._activity = ActivityState()
|
|
154
|
+
self._context_percent: float | None = None
|
|
152
155
|
|
|
153
156
|
def reset(self) -> None:
|
|
154
157
|
"""Reset all layers."""
|
|
155
158
|
self._base_status = None
|
|
156
159
|
self._activity.reset()
|
|
160
|
+
self._context_percent = None
|
|
157
161
|
|
|
158
162
|
def set_base_status(self, status: str | None) -> None:
|
|
159
163
|
"""Set base status from TodoChange."""
|
|
@@ -175,12 +179,16 @@ class SpinnerStatusState:
|
|
|
175
179
|
"""Clear activity state for a new turn."""
|
|
176
180
|
self._activity.reset()
|
|
177
181
|
|
|
182
|
+
def set_context_percent(self, percent: float) -> None:
|
|
183
|
+
"""Set context usage percentage."""
|
|
184
|
+
self._context_percent = percent
|
|
185
|
+
|
|
178
186
|
def get_activity_text(self) -> Text | None:
|
|
179
187
|
"""Get current activity text. Returns None if idle."""
|
|
180
188
|
return self._activity.get_activity_text()
|
|
181
189
|
|
|
182
190
|
def get_status(self) -> Text:
|
|
183
|
-
"""Get current spinner status as rich Text."""
|
|
191
|
+
"""Get current spinner status as rich Text (without context)."""
|
|
184
192
|
activity_text = self._activity.get_activity_text()
|
|
185
193
|
|
|
186
194
|
if self._base_status:
|
|
@@ -188,11 +196,19 @@ class SpinnerStatusState:
|
|
|
188
196
|
if activity_text:
|
|
189
197
|
result.append(" | ")
|
|
190
198
|
result.append_text(activity_text)
|
|
191
|
-
|
|
192
|
-
if activity_text:
|
|
199
|
+
elif activity_text:
|
|
193
200
|
activity_text.append(" …")
|
|
194
|
-
|
|
195
|
-
|
|
201
|
+
result = activity_text
|
|
202
|
+
else:
|
|
203
|
+
result = Text(self.DEFAULT_STATUS)
|
|
204
|
+
|
|
205
|
+
return result
|
|
206
|
+
|
|
207
|
+
def get_context_text(self) -> Text | None:
|
|
208
|
+
"""Get context usage text for right-aligned display."""
|
|
209
|
+
if self._context_percent is None:
|
|
210
|
+
return None
|
|
211
|
+
return Text(f"{self._context_percent:.1f}%", style=ThemeKey.METADATA_DIM)
|
|
196
212
|
|
|
197
213
|
|
|
198
214
|
class DisplayEventHandler:
|
|
@@ -247,6 +263,8 @@ class DisplayEventHandler:
|
|
|
247
263
|
self._on_task_metadata(e)
|
|
248
264
|
case events.TodoChangeEvent() as e:
|
|
249
265
|
self._on_todo_change(e)
|
|
266
|
+
case events.ContextUsageEvent() as e:
|
|
267
|
+
self._on_context_usage(e)
|
|
250
268
|
case events.TurnEndEvent():
|
|
251
269
|
pass
|
|
252
270
|
case events.ResponseMetadataEvent():
|
|
@@ -412,6 +430,12 @@ class DisplayEventHandler:
|
|
|
412
430
|
self.spinner_status.clear_for_new_turn()
|
|
413
431
|
self._update_spinner()
|
|
414
432
|
|
|
433
|
+
def _on_context_usage(self, event: events.ContextUsageEvent) -> None:
|
|
434
|
+
if self.renderer.is_sub_agent_session(event.session_id):
|
|
435
|
+
return
|
|
436
|
+
self.spinner_status.set_context_percent(event.context_percent)
|
|
437
|
+
self._update_spinner()
|
|
438
|
+
|
|
415
439
|
async def _on_task_finish(self, event: events.TaskFinishEvent) -> None:
|
|
416
440
|
self.renderer.display_task_finish(event)
|
|
417
441
|
if not self.renderer.is_sub_agent_session(event.session_id):
|
|
@@ -459,7 +483,10 @@ class DisplayEventHandler:
|
|
|
459
483
|
|
|
460
484
|
def _update_spinner(self) -> None:
|
|
461
485
|
"""Update spinner text from current status state."""
|
|
462
|
-
self.renderer.spinner_update(
|
|
486
|
+
self.renderer.spinner_update(
|
|
487
|
+
self.spinner_status.get_status(),
|
|
488
|
+
self.spinner_status.get_context_text(),
|
|
489
|
+
)
|
|
463
490
|
|
|
464
491
|
async def _flush_assistant_buffer(self, state: StreamState) -> None:
|
|
465
492
|
if state.is_active:
|
|
@@ -525,15 +552,15 @@ class DisplayEventHandler:
|
|
|
525
552
|
"""Calculate max length for base_status based on terminal width.
|
|
526
553
|
|
|
527
554
|
Reserve space for:
|
|
528
|
-
- Spinner glyph + space: 2 chars
|
|
555
|
+
- Spinner glyph + space + context text: 2 chars + context text length 10 chars
|
|
529
556
|
- " | " separator: 3 chars (only if activity text present)
|
|
530
557
|
- Activity text: actual length (only if present)
|
|
531
558
|
- Status hint text (esc to interrupt)
|
|
532
559
|
"""
|
|
533
560
|
terminal_width = self.renderer.console.size.width
|
|
534
561
|
|
|
535
|
-
# Base reserved space: spinner + status hint
|
|
536
|
-
reserved_space =
|
|
562
|
+
# Base reserved space: spinner + context + status hint
|
|
563
|
+
reserved_space = 12 + len(const.STATUS_HINT_TEXT)
|
|
537
564
|
|
|
538
565
|
# Add space for activity text if present
|
|
539
566
|
activity_text = self.spinner_status.get_activity_text()
|
|
@@ -264,9 +264,9 @@ class REPLRenderer:
|
|
|
264
264
|
"""Stop the spinner animation."""
|
|
265
265
|
self._spinner.stop()
|
|
266
266
|
|
|
267
|
-
def spinner_update(self, status_text: str | Text) -> None:
|
|
268
|
-
"""Update the spinner status text."""
|
|
269
|
-
self._spinner.update(ShimmerStatusText(status_text, ThemeKey.SPINNER_STATUS_TEXT))
|
|
267
|
+
def spinner_update(self, status_text: str | Text, right_text: Text | None = None) -> None:
|
|
268
|
+
"""Update the spinner status text with optional right-aligned text."""
|
|
269
|
+
self._spinner.update(ShimmerStatusText(status_text, ThemeKey.SPINNER_STATUS_TEXT, right_text))
|
|
270
270
|
|
|
271
271
|
def spinner_renderable(self) -> Spinner:
|
|
272
272
|
"""Return the spinner's renderable for embedding in other components."""
|
|
@@ -16,7 +16,7 @@ from klaude_code.ui.rich.theme import ThemeKey
|
|
|
16
16
|
|
|
17
17
|
def _compact_schema_value(value: dict[str, Any]) -> str | list[Any] | dict[str, Any]:
|
|
18
18
|
"""Convert a JSON Schema value to compact representation."""
|
|
19
|
-
value_type = value.get("type", "any")
|
|
19
|
+
value_type = value.get("type", "any").lower()
|
|
20
20
|
desc = value.get("description", "")
|
|
21
21
|
|
|
22
22
|
if value_type == "object":
|
|
@@ -64,16 +64,20 @@ def render_sub_agent_result(
|
|
|
64
64
|
|
|
65
65
|
# Use rich JSON for structured output
|
|
66
66
|
if has_structured_output:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
try:
|
|
68
|
+
return Panel.fit(
|
|
69
|
+
Group(
|
|
70
|
+
Text(
|
|
71
|
+
"use /export to view full output",
|
|
72
|
+
style=ThemeKey.TOOL_RESULT,
|
|
73
|
+
),
|
|
74
|
+
JSON(stripped_result),
|
|
72
75
|
),
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
border_style=ThemeKey.LINES,
|
|
77
|
+
)
|
|
78
|
+
except json.JSONDecodeError:
|
|
79
|
+
# Fall back to markdown if not valid JSON
|
|
80
|
+
pass
|
|
77
81
|
|
|
78
82
|
lines = stripped_result.splitlines()
|
|
79
83
|
if len(lines) > const.SUB_AGENT_RESULT_MAX_LINES:
|
|
@@ -186,14 +186,7 @@ def render_write_tool_call(arguments: str) -> RenderableType:
|
|
|
186
186
|
try:
|
|
187
187
|
json_dict = json.loads(arguments)
|
|
188
188
|
file_path = json_dict.get("file_path")
|
|
189
|
-
|
|
190
|
-
if isinstance(file_path, str):
|
|
191
|
-
abs_path = Path(file_path)
|
|
192
|
-
if not abs_path.is_absolute():
|
|
193
|
-
abs_path = (Path().cwd() / abs_path).resolve()
|
|
194
|
-
if abs_path.exists():
|
|
195
|
-
op_label = "Overwrite"
|
|
196
|
-
tool_name_column = Text.assemble(("→", ThemeKey.TOOL_MARK), " ", (op_label, ThemeKey.TOOL_NAME))
|
|
189
|
+
tool_name_column = Text.assemble(("→", ThemeKey.TOOL_MARK), " ", ("Write", ThemeKey.TOOL_NAME))
|
|
197
190
|
arguments_column = render_path(file_path, ThemeKey.TOOL_PARAM_FILE_PATH)
|
|
198
191
|
except json.JSONDecodeError:
|
|
199
192
|
tool_name_column = Text.assemble(("→", ThemeKey.TOOL_MARK), " ", ("Write", ThemeKey.TOOL_NAME))
|
|
@@ -455,8 +448,9 @@ _TOOL_ACTIVE_FORM: dict[str, str] = {
|
|
|
455
448
|
tools.SKILL: "Skilling",
|
|
456
449
|
tools.MERMAID: "Diagramming",
|
|
457
450
|
tools.MEMORY: "Memorizing",
|
|
458
|
-
tools.WEB_FETCH: "Fetching",
|
|
459
|
-
tools.
|
|
451
|
+
tools.WEB_FETCH: "Fetching Web",
|
|
452
|
+
tools.WEB_SEARCH: "Searching Web",
|
|
453
|
+
tools.REPORT_BACK: "Reporting",
|
|
460
454
|
}
|
|
461
455
|
|
|
462
456
|
|
klaude_code/ui/rich/status.py
CHANGED
|
@@ -21,15 +21,22 @@ from klaude_code.ui.terminal.color import get_last_terminal_background_rgb
|
|
|
21
21
|
BREATHING_SPINNER_NAME = "dots"
|
|
22
22
|
|
|
23
23
|
# Alternating glyphs for the breathing spinner - switches at each "transparent" point
|
|
24
|
-
# All glyphs are center-symmetric (point-symmetric)
|
|
25
24
|
_BREATHING_SPINNER_GLYPHS_BASE = [
|
|
26
|
-
# Stars
|
|
27
25
|
"✦",
|
|
28
26
|
"✶",
|
|
29
27
|
"✲",
|
|
30
|
-
"⏺",
|
|
31
28
|
"◆",
|
|
32
29
|
"❖",
|
|
30
|
+
"✧",
|
|
31
|
+
"❋",
|
|
32
|
+
"✸",
|
|
33
|
+
"✻",
|
|
34
|
+
"◇",
|
|
35
|
+
"✴",
|
|
36
|
+
"✷",
|
|
37
|
+
"⟡",
|
|
38
|
+
"⬡",
|
|
39
|
+
"⬢",
|
|
33
40
|
]
|
|
34
41
|
|
|
35
42
|
# Shuffle glyphs on module load for variety across sessions
|
|
@@ -172,15 +179,34 @@ def _breathing_style(console: Console, base_style: Style, intensity: float) -> S
|
|
|
172
179
|
|
|
173
180
|
|
|
174
181
|
class ShimmerStatusText:
|
|
175
|
-
"""Renderable status line with shimmer effect on the main text and hint.
|
|
182
|
+
"""Renderable status line with shimmer effect on the main text and hint.
|
|
176
183
|
|
|
177
|
-
|
|
184
|
+
Supports optional right-aligned text that stays fixed at the right edge.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
def __init__(self, main_text: str | Text, main_style: ThemeKey, right_text: Text | None = None) -> None:
|
|
178
188
|
self._main_text = main_text if isinstance(main_text, Text) else Text(main_text)
|
|
179
189
|
self._main_style = main_style
|
|
180
190
|
self._hint_text = Text(const.STATUS_HINT_TEXT)
|
|
181
191
|
self._hint_style = ThemeKey.STATUS_HINT
|
|
192
|
+
self._right_text = right_text
|
|
182
193
|
|
|
183
194
|
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
|
|
195
|
+
left_text = self._render_left_text(console)
|
|
196
|
+
|
|
197
|
+
if self._right_text is None:
|
|
198
|
+
yield left_text
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
# Use Table.grid to create left-right aligned layout
|
|
202
|
+
table = Table.grid(expand=True)
|
|
203
|
+
table.add_column(justify="left", ratio=1)
|
|
204
|
+
table.add_column(justify="right")
|
|
205
|
+
table.add_row(left_text, self._right_text)
|
|
206
|
+
yield table
|
|
207
|
+
|
|
208
|
+
def _render_left_text(self, console: Console) -> Text:
|
|
209
|
+
"""Render the left part with shimmer effect."""
|
|
184
210
|
result = Text()
|
|
185
211
|
main_style = console.get_style(str(self._main_style))
|
|
186
212
|
hint_style = console.get_style(str(self._hint_style))
|
|
@@ -198,7 +224,7 @@ class ShimmerStatusText:
|
|
|
198
224
|
style = _shimmer_style(console, base_style, intensity)
|
|
199
225
|
result.append(ch, style=style)
|
|
200
226
|
|
|
201
|
-
|
|
227
|
+
return result
|
|
202
228
|
|
|
203
229
|
|
|
204
230
|
def spinner_name() -> str:
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: klaude-code
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.17
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Dist: anthropic>=0.66.0
|
|
6
|
+
Requires-Dist: ddgs>=9.9.3
|
|
6
7
|
Requires-Dist: openai>=1.102.0
|
|
7
8
|
Requires-Dist: pillow>=12.0.0
|
|
8
9
|
Requires-Dist: prompt-toolkit>=3.0.52
|
|
@@ -71,47 +72,137 @@ Open the configuration file in editor:
|
|
|
71
72
|
klaude config
|
|
72
73
|
```
|
|
73
74
|
|
|
74
|
-
An
|
|
75
|
+
An example config yaml:
|
|
75
76
|
|
|
76
77
|
```yaml
|
|
77
78
|
provider_list:
|
|
78
|
-
- provider_name: openrouter
|
|
79
|
+
- provider_name: openrouter
|
|
79
80
|
protocol: openrouter # support <responses|openrouter|anthropic|openai>
|
|
80
81
|
api_key: <your-openrouter-api-key>
|
|
82
|
+
|
|
83
|
+
- provider_name: openai-responses
|
|
84
|
+
protocol: responses
|
|
85
|
+
api_key: <your-openai-api-key>
|
|
86
|
+
|
|
87
|
+
- provider_name: anthropic
|
|
88
|
+
protocol: anthropic
|
|
89
|
+
api_key: <your-anthropic-api-key>
|
|
90
|
+
|
|
91
|
+
- provider_name: moonshot
|
|
92
|
+
protocol: anthropic
|
|
93
|
+
base_url: https://api.moonshot.cn/anthropic
|
|
94
|
+
api_key: <your-api-key>
|
|
95
|
+
|
|
96
|
+
- provider_name: deepseek
|
|
97
|
+
protocol: anthropic
|
|
98
|
+
base_url: https://api.deepseek.com/anthropic
|
|
99
|
+
api_key: <your-api-key>
|
|
100
|
+
|
|
81
101
|
model_list:
|
|
82
|
-
|
|
83
|
-
|
|
102
|
+
|
|
103
|
+
- model_name: deepseek
|
|
104
|
+
provider: deepseek
|
|
105
|
+
model_params:
|
|
106
|
+
model: deepseek-reasoner
|
|
107
|
+
context_limit: 128000
|
|
108
|
+
thinking:
|
|
109
|
+
type: enabled
|
|
110
|
+
budget_tokens: 8192
|
|
111
|
+
cost:
|
|
112
|
+
currency: CNY
|
|
113
|
+
input: 2
|
|
114
|
+
output: 3
|
|
115
|
+
cache_read: 0.2
|
|
116
|
+
|
|
117
|
+
- model_name: codex-max
|
|
118
|
+
provider: openai-responses
|
|
84
119
|
model_params:
|
|
85
|
-
model:
|
|
86
|
-
context_limit: 368000
|
|
120
|
+
model: gpt-5.1-codex-max
|
|
87
121
|
thinking:
|
|
88
122
|
reasoning_effort: medium
|
|
89
|
-
|
|
123
|
+
context_limit: 400000
|
|
124
|
+
max_tokens: 128000
|
|
125
|
+
cost:
|
|
126
|
+
input: 1.25
|
|
127
|
+
output: 10
|
|
128
|
+
cache_read: 0.13
|
|
129
|
+
|
|
130
|
+
- model_name: gpt-5.1
|
|
90
131
|
provider: openrouter
|
|
91
132
|
model_params:
|
|
92
133
|
model: openai/gpt-5.1
|
|
93
|
-
context_limit:
|
|
134
|
+
context_limit: 400000
|
|
135
|
+
max_tokens: 128000
|
|
136
|
+
verbosity: high
|
|
94
137
|
thinking:
|
|
95
138
|
reasoning_effort: high
|
|
96
|
-
|
|
139
|
+
cost:
|
|
140
|
+
input: 1.25
|
|
141
|
+
output: 10
|
|
142
|
+
cache_read: 0.13
|
|
143
|
+
|
|
144
|
+
- model_name: kimi@moonshot
|
|
145
|
+
provider: moonshot
|
|
146
|
+
model_params:
|
|
147
|
+
model: kimi-k2-thinking
|
|
148
|
+
context_limit: 262144
|
|
149
|
+
thinking:
|
|
150
|
+
type: enabled
|
|
151
|
+
budget_tokens: 8192
|
|
152
|
+
cost:
|
|
153
|
+
currency: CNY
|
|
154
|
+
input: 4
|
|
155
|
+
output: 16
|
|
156
|
+
cache_read: 1
|
|
157
|
+
|
|
158
|
+
- model_name: opus
|
|
97
159
|
provider: openrouter
|
|
98
160
|
model_params:
|
|
99
|
-
model: anthropic/claude-4.5-
|
|
100
|
-
context_limit:
|
|
161
|
+
model: anthropic/claude-4.5-opus
|
|
162
|
+
context_limit: 200000
|
|
101
163
|
provider_routing:
|
|
102
|
-
|
|
103
|
-
|
|
164
|
+
only: [ google-vertex ]
|
|
165
|
+
verbosity: high
|
|
166
|
+
thinking:
|
|
167
|
+
type: enabled
|
|
168
|
+
budget_tokens: 31999
|
|
169
|
+
cost:
|
|
170
|
+
input: 5
|
|
171
|
+
output: 25
|
|
172
|
+
cache_read: 0.5
|
|
173
|
+
cache_write: 6.25
|
|
174
|
+
|
|
175
|
+
- model_name: gemini
|
|
104
176
|
provider: openrouter
|
|
105
177
|
model_params:
|
|
106
|
-
model:
|
|
107
|
-
context_limit:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
178
|
+
model: google/gemini-3-pro-preview
|
|
179
|
+
context_limit: 1048576
|
|
180
|
+
thinking:
|
|
181
|
+
reasoning_effort: medium
|
|
182
|
+
cost:
|
|
183
|
+
input: 2
|
|
184
|
+
output: 12
|
|
185
|
+
cache_read: 0.2
|
|
186
|
+
|
|
187
|
+
- model_name: haiku
|
|
188
|
+
provider: anthropic
|
|
189
|
+
model_params:
|
|
190
|
+
model: claude-haiku-4-5-20251001
|
|
191
|
+
context_limit: 200000
|
|
192
|
+
cost:
|
|
193
|
+
input: 1
|
|
194
|
+
output: 5
|
|
195
|
+
cache_read: 0.1
|
|
196
|
+
cache_write: 1.25
|
|
197
|
+
|
|
198
|
+
main_model: opus
|
|
199
|
+
|
|
111
200
|
sub_agent_models:
|
|
112
|
-
oracle: gpt-5.1
|
|
201
|
+
oracle: gpt-5.1
|
|
113
202
|
explore: haiku
|
|
114
|
-
task:
|
|
203
|
+
task: opus
|
|
204
|
+
webfetchagent: haiku
|
|
205
|
+
|
|
115
206
|
```
|
|
116
207
|
|
|
117
208
|
List configured providers and models:
|