klaude-code 2.5.0__py3-none-any.whl → 2.5.2__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/.DS_Store +0 -0
- klaude_code/cli/list_model.py +8 -0
- klaude_code/cli/main.py +1 -1
- klaude_code/config/assets/builtin_config.yaml +0 -2
- klaude_code/config/config.py +30 -7
- klaude_code/config/model_matcher.py +2 -2
- klaude_code/config/sub_agent_model_helper.py +1 -1
- klaude_code/const.py +1 -1
- klaude_code/core/agent_profile.py +1 -0
- klaude_code/core/executor.py +4 -0
- klaude_code/core/loaded_skills.py +36 -0
- klaude_code/core/task.py +1 -0
- klaude_code/core/tool/context.py +1 -3
- klaude_code/core/turn.py +0 -7
- klaude_code/llm/anthropic/client.py +23 -11
- klaude_code/protocol/events/lifecycle.py +1 -0
- klaude_code/protocol/events/streaming.py +0 -1
- klaude_code/protocol/events/system.py +3 -0
- klaude_code/protocol/llm_param.py +1 -0
- klaude_code/session/export.py +259 -91
- klaude_code/session/templates/export_session.html +141 -59
- klaude_code/skill/.DS_Store +0 -0
- klaude_code/skill/assets/.DS_Store +0 -0
- klaude_code/skill/loader.py +1 -0
- klaude_code/tui/command/refresh_cmd.py +2 -0
- klaude_code/tui/components/metadata.py +6 -6
- klaude_code/tui/components/rich/markdown.py +8 -0
- klaude_code/tui/components/welcome.py +32 -0
- klaude_code/tui/machine.py +29 -18
- klaude_code/tui/renderer.py +0 -1
- {klaude_code-2.5.0.dist-info → klaude_code-2.5.2.dist-info}/METADATA +1 -1
- {klaude_code-2.5.0.dist-info → klaude_code-2.5.2.dist-info}/RECORD +34 -31
- klaude_code/skill/assets/jj-workspace/SKILL.md +0 -20
- {klaude_code-2.5.0.dist-info → klaude_code-2.5.2.dist-info}/WHEEL +0 -0
- {klaude_code-2.5.0.dist-info → klaude_code-2.5.2.dist-info}/entry_points.txt +0 -0
klaude_code/session/export.py
CHANGED
|
@@ -19,7 +19,7 @@ from klaude_code.protocol.sub_agent import is_sub_agent_tool
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
20
20
|
from klaude_code.session.session import Session
|
|
21
21
|
|
|
22
|
-
_TOOL_OUTPUT_PREVIEW_LINES: Final[int] =
|
|
22
|
+
_TOOL_OUTPUT_PREVIEW_LINES: Final[int] = 8
|
|
23
23
|
_MAX_FILENAME_MESSAGE_LEN: Final[int] = 50
|
|
24
24
|
_IMAGE_MAX_DISPLAY_WIDTH: Final[int] = 600
|
|
25
25
|
|
|
@@ -489,9 +489,9 @@ def _render_text_block(text: str) -> str:
|
|
|
489
489
|
full = "\n".join(escaped_lines)
|
|
490
490
|
|
|
491
491
|
return (
|
|
492
|
-
f'<div class="expandable-output expandable">'
|
|
492
|
+
f'<div class="expandable-output expandable" style="--preview-max-lines: {_TOOL_OUTPUT_PREVIEW_LINES};">'
|
|
493
493
|
f'<div class="preview-text" style="white-space: pre-wrap; font-family: var(--font-mono);">{preview}</div>'
|
|
494
|
-
f'<div class="expand-hint expand-text">click to expand full output ({len(lines)} lines)</div>'
|
|
494
|
+
f'<div class="expand-hint expand-text">click to expand full output ({len(lines)} lines; showing first {_TOOL_OUTPUT_PREVIEW_LINES})</div>'
|
|
495
495
|
f'<div class="full-text" style="white-space: pre-wrap; font-family: var(--font-mono);">{full}</div>'
|
|
496
496
|
f'<div class="collapse-hint">click to collapse</div>'
|
|
497
497
|
f"</div>"
|
|
@@ -827,106 +827,274 @@ def _build_messages_html(
|
|
|
827
827
|
seen_session_ids: set[str] | None = None,
|
|
828
828
|
nesting_level: int = 0,
|
|
829
829
|
) -> str:
|
|
830
|
+
"""Render session history into HTML.
|
|
831
|
+
|
|
832
|
+
This export groups the conversation into Tasks:
|
|
833
|
+
- A Task starts with a UserMessage.
|
|
834
|
+
- The Task includes all subsequent model thinking/replies/tool calls/results until the next UserMessage.
|
|
835
|
+
- By default, only the final assistant reply is shown; intermediate steps are folded.
|
|
836
|
+
- Mermaid is a special case: show the last consecutive assistant messages + Mermaid diagram result.
|
|
837
|
+
"""
|
|
838
|
+
|
|
830
839
|
if seen_session_ids is None:
|
|
831
840
|
seen_session_ids = set()
|
|
832
841
|
|
|
842
|
+
renderable_items = [item for item in history if not isinstance(item, message.ToolResultMessage)]
|
|
833
843
|
blocks: list[str] = []
|
|
834
|
-
assistant_counter = 0
|
|
835
844
|
|
|
836
|
-
|
|
845
|
+
def _render_user_message(item: message.UserMessage) -> str:
|
|
846
|
+
text = message.join_text_parts(item.parts)
|
|
847
|
+
images = _extract_image_parts(item.parts)
|
|
848
|
+
images_html = _render_image_parts(images)
|
|
849
|
+
ts_str = _format_msg_timestamp(item.created_at)
|
|
850
|
+
body_parts: list[str] = []
|
|
851
|
+
if images_html:
|
|
852
|
+
body_parts.append(images_html)
|
|
853
|
+
if text:
|
|
854
|
+
body_parts.append(f'<div style="white-space: pre-wrap;">{_escape_html(text)}</div>')
|
|
855
|
+
if not body_parts:
|
|
856
|
+
body_parts.append('<div style="color: var(--text-dim); font-style: italic;">(empty)</div>')
|
|
857
|
+
return (
|
|
858
|
+
f'<div class="message-group">'
|
|
859
|
+
f'<div class="role-label user">'
|
|
860
|
+
f"User"
|
|
861
|
+
f'<span class="timestamp">{ts_str}</span>'
|
|
862
|
+
f"</div>"
|
|
863
|
+
f'<div class="message-content user">{"".join(body_parts)}</div>'
|
|
864
|
+
f"</div>"
|
|
865
|
+
)
|
|
837
866
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
867
|
+
def _render_developer_message(item: message.DeveloperMessage, next_item: message.HistoryEvent | None) -> str:
|
|
868
|
+
content = message.join_text_parts(item.parts)
|
|
869
|
+
images = _extract_image_parts(item.parts)
|
|
870
|
+
images_html = _render_image_parts(images)
|
|
871
|
+
ts_str = _format_msg_timestamp(item.created_at)
|
|
872
|
+
|
|
873
|
+
extra_class = ""
|
|
874
|
+
if isinstance(next_item, (message.UserMessage, message.AssistantMessage)):
|
|
875
|
+
extra_class = " gap-below"
|
|
876
|
+
|
|
877
|
+
detail_body = ""
|
|
878
|
+
if images_html:
|
|
879
|
+
detail_body += images_html
|
|
880
|
+
if content:
|
|
881
|
+
detail_body += f'<div style="white-space: pre-wrap;">{_escape_html(content)}</div>'
|
|
882
|
+
if not detail_body:
|
|
883
|
+
detail_body = '<div style="color: var(--text-dim); font-style: italic;">(empty)</div>'
|
|
884
|
+
|
|
885
|
+
return (
|
|
886
|
+
f'<details class="developer-message{extra_class}">'
|
|
887
|
+
f"<summary>"
|
|
888
|
+
f"Developer"
|
|
889
|
+
f'<span class="timestamp">{ts_str}</span>'
|
|
890
|
+
f"</summary>"
|
|
891
|
+
f'<div class="details-content">{detail_body}</div>'
|
|
892
|
+
f"</details>"
|
|
893
|
+
)
|
|
894
|
+
|
|
895
|
+
def _render_system_message(item: message.SystemMessage) -> str | None:
|
|
896
|
+
content = message.join_text_parts(item.parts)
|
|
897
|
+
if not content:
|
|
898
|
+
return None
|
|
899
|
+
ts_str = _format_msg_timestamp(item.created_at)
|
|
900
|
+
return (
|
|
901
|
+
f'<details class="developer-message">'
|
|
902
|
+
f"<summary>"
|
|
903
|
+
f"System"
|
|
904
|
+
f'<span class="timestamp">{ts_str}</span>'
|
|
905
|
+
f"</summary>"
|
|
906
|
+
f'<div class="details-content" style="white-space: pre-wrap;">{_escape_html(content)}</div>'
|
|
907
|
+
f"</details>"
|
|
908
|
+
)
|
|
909
|
+
|
|
910
|
+
def _render_task_full(task_events: list[message.HistoryEvent]) -> str:
|
|
911
|
+
full_parts: list[str] = []
|
|
912
|
+
assistant_counter = 0
|
|
913
|
+
|
|
914
|
+
for idx, event in enumerate(task_events):
|
|
915
|
+
next_event = task_events[idx + 1] if idx + 1 < len(task_events) else None
|
|
916
|
+
|
|
917
|
+
if isinstance(event, message.AssistantMessage):
|
|
918
|
+
assistant_counter += 1
|
|
919
|
+
|
|
920
|
+
thinking_text = "".join(
|
|
921
|
+
part.text for part in event.parts if isinstance(part, message.ThinkingTextPart)
|
|
922
|
+
).strip()
|
|
923
|
+
if thinking_text:
|
|
924
|
+
full_parts.append(_render_thinking_block(thinking_text))
|
|
925
|
+
|
|
926
|
+
assistant_text = message.join_text_parts(event.parts)
|
|
927
|
+
assistant_images = _extract_image_parts(event.parts)
|
|
928
|
+
if assistant_text or assistant_images:
|
|
929
|
+
full_parts.append(
|
|
930
|
+
_render_assistant_message(
|
|
931
|
+
assistant_counter,
|
|
932
|
+
assistant_text,
|
|
933
|
+
event.created_at,
|
|
934
|
+
assistant_images,
|
|
935
|
+
)
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
for part in event.parts:
|
|
939
|
+
if isinstance(part, message.ToolCallPart):
|
|
940
|
+
result = tool_results.get(part.call_id)
|
|
941
|
+
full_parts.append(_format_tool_call(part, result, event.created_at))
|
|
942
|
+
if result is not None:
|
|
943
|
+
sub_agent_html = _render_sub_agent_session(result, seen_session_ids, nesting_level)
|
|
944
|
+
if sub_agent_html:
|
|
945
|
+
full_parts.append(sub_agent_html)
|
|
946
|
+
elif isinstance(event, model.TaskMetadataItem):
|
|
947
|
+
full_parts.append(_render_metadata_item(event))
|
|
948
|
+
elif isinstance(event, message.DeveloperMessage):
|
|
949
|
+
full_parts.append(_render_developer_message(event, next_event))
|
|
950
|
+
elif isinstance(event, message.SystemMessage):
|
|
951
|
+
rendered = _render_system_message(event)
|
|
952
|
+
if rendered:
|
|
953
|
+
full_parts.append(rendered)
|
|
954
|
+
|
|
955
|
+
return "\n".join(full_parts)
|
|
956
|
+
|
|
957
|
+
def _choose_compact_assistants(
|
|
958
|
+
task_events: list[message.HistoryEvent],
|
|
959
|
+
) -> tuple[list[message.AssistantMessage], bool]:
|
|
960
|
+
# Mermaid exception: show last consecutive assistant messages + Mermaid diagram result.
|
|
961
|
+
tail: list[message.AssistantMessage] = []
|
|
962
|
+
|
|
963
|
+
idx = len(task_events) - 1
|
|
964
|
+
while idx >= 0 and isinstance(task_events[idx], model.TaskMetadataItem):
|
|
965
|
+
idx -= 1
|
|
966
|
+
|
|
967
|
+
while idx >= 0 and isinstance(task_events[idx], message.AssistantMessage):
|
|
968
|
+
tail.append(cast(message.AssistantMessage, task_events[idx]))
|
|
969
|
+
idx -= 1
|
|
970
|
+
|
|
971
|
+
tail.reverse()
|
|
972
|
+
if tail:
|
|
973
|
+
has_mermaid = any(
|
|
974
|
+
isinstance(p, message.ToolCallPart) and p.tool_name == "Mermaid" for msg in tail for p in msg.parts
|
|
859
975
|
)
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
thinking_text = "".join(part.text for part in item.parts if isinstance(part, message.ThinkingTextPart))
|
|
863
|
-
if thinking_text:
|
|
864
|
-
blocks.append(_render_thinking_block(thinking_text))
|
|
976
|
+
if has_mermaid:
|
|
977
|
+
return tail, True
|
|
865
978
|
|
|
866
|
-
|
|
867
|
-
|
|
979
|
+
for idx in range(len(task_events) - 1, -1, -1):
|
|
980
|
+
if isinstance(task_events[idx], message.AssistantMessage):
|
|
981
|
+
return [cast(message.AssistantMessage, task_events[idx])], False
|
|
982
|
+
|
|
983
|
+
return [], False
|
|
984
|
+
|
|
985
|
+
def _render_assistant_compact(assistant_messages: list[message.AssistantMessage], *, show_mermaid: bool) -> str:
|
|
986
|
+
parts: list[str] = []
|
|
987
|
+
|
|
988
|
+
for counter, msg in enumerate(assistant_messages, start=1):
|
|
989
|
+
assistant_text = message.join_text_parts(msg.parts)
|
|
990
|
+
assistant_images = _extract_image_parts(msg.parts)
|
|
868
991
|
if assistant_text or assistant_images:
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
992
|
+
parts.append(_render_assistant_message(counter, assistant_text, msg.created_at, assistant_images))
|
|
993
|
+
|
|
994
|
+
if show_mermaid:
|
|
995
|
+
# Keep Mermaid diagram interleaved where the tool call occurred.
|
|
996
|
+
for p in msg.parts:
|
|
997
|
+
if not isinstance(p, message.ToolCallPart) or p.tool_name != "Mermaid":
|
|
998
|
+
continue
|
|
999
|
+
|
|
1000
|
+
result = tool_results.get(p.call_id)
|
|
1001
|
+
ui_extra = result.ui_extra if result else None
|
|
1002
|
+
extras = _collect_ui_extras(ui_extra)
|
|
1003
|
+
mermaid_extra = next((x for x in extras if isinstance(x, model.MermaidLinkUIExtra)), None)
|
|
1004
|
+
mermaid_source = mermaid_extra if mermaid_extra else ui_extra
|
|
1005
|
+
mermaid_html = _get_mermaid_link_html(mermaid_source, p)
|
|
1006
|
+
if mermaid_html:
|
|
1007
|
+
parts.append(f'<div class="task-mermaid-result">{mermaid_html}</div>')
|
|
1008
|
+
|
|
1009
|
+
return "\n".join(parts)
|
|
1010
|
+
|
|
1011
|
+
def _count_hidden_steps(
|
|
1012
|
+
task_events: list[message.HistoryEvent],
|
|
1013
|
+
compact_assistants: list[message.AssistantMessage],
|
|
1014
|
+
) -> int:
|
|
1015
|
+
compact_ids = {id(x) for x in compact_assistants}
|
|
1016
|
+
hidden_assistant_messages = sum(
|
|
1017
|
+
1 for x in task_events if isinstance(x, message.AssistantMessage) and id(x) not in compact_ids
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
thinking_blocks = 0
|
|
1021
|
+
tool_calls = 0
|
|
1022
|
+
other_events = 0
|
|
1023
|
+
for e in task_events:
|
|
1024
|
+
if isinstance(e, message.AssistantMessage):
|
|
1025
|
+
for part in e.parts:
|
|
1026
|
+
if isinstance(part, message.ThinkingTextPart) and part.text.strip():
|
|
1027
|
+
thinking_blocks += 1
|
|
1028
|
+
elif isinstance(part, message.ToolCallPart):
|
|
1029
|
+
tool_calls += 1
|
|
1030
|
+
elif isinstance(e, (model.TaskMetadataItem, message.DeveloperMessage, message.SystemMessage)):
|
|
1031
|
+
other_events += 1
|
|
1032
|
+
|
|
1033
|
+
return hidden_assistant_messages + thinking_blocks + tool_calls + other_events
|
|
1034
|
+
|
|
1035
|
+
def _render_task(user: message.UserMessage, task_events: list[message.HistoryEvent]) -> str:
|
|
1036
|
+
compact_assistants, show_mermaid = _choose_compact_assistants(task_events)
|
|
1037
|
+
hidden_steps = _count_hidden_steps(task_events, compact_assistants)
|
|
1038
|
+
|
|
1039
|
+
full_html = _render_task_full(task_events)
|
|
1040
|
+
compact_html = _render_assistant_compact(compact_assistants, show_mermaid=show_mermaid)
|
|
1041
|
+
|
|
1042
|
+
if not compact_html.strip() and full_html.strip():
|
|
1043
|
+
compact_html = '<div style="color: var(--text-dim); font-style: italic;">(no assistant message)</div>'
|
|
1044
|
+
|
|
1045
|
+
steps_details = ""
|
|
1046
|
+
if hidden_steps > 0 and full_html.strip():
|
|
1047
|
+
steps_details = (
|
|
1048
|
+
f'<details class="task-steps" data-step-count="{hidden_steps}">'
|
|
1049
|
+
f'<summary data-step-count="{hidden_steps}">Show {hidden_steps} steps</summary>'
|
|
1050
|
+
f'<div class="task-steps-content">{full_html}</div>'
|
|
914
1051
|
f"</details>"
|
|
915
1052
|
)
|
|
1053
|
+
|
|
1054
|
+
return (
|
|
1055
|
+
'<div class="task">'
|
|
1056
|
+
f"{_render_user_message(user)}"
|
|
1057
|
+
f"{steps_details}"
|
|
1058
|
+
f'<div class="task-final">{compact_html}</div>'
|
|
1059
|
+
"</div>"
|
|
1060
|
+
)
|
|
1061
|
+
|
|
1062
|
+
current_user: message.UserMessage | None = None
|
|
1063
|
+
current_task_events: list[message.HistoryEvent] = []
|
|
1064
|
+
|
|
1065
|
+
def _flush_task() -> None:
|
|
1066
|
+
nonlocal current_user, current_task_events
|
|
1067
|
+
if current_user is None:
|
|
1068
|
+
return
|
|
1069
|
+
blocks.append(_render_task(current_user, current_task_events))
|
|
1070
|
+
current_user = None
|
|
1071
|
+
current_task_events = []
|
|
1072
|
+
|
|
1073
|
+
for idx, item in enumerate(renderable_items):
|
|
1074
|
+
if isinstance(item, message.UserMessage):
|
|
1075
|
+
_flush_task()
|
|
1076
|
+
current_user = item
|
|
1077
|
+
current_task_events = []
|
|
1078
|
+
continue
|
|
1079
|
+
|
|
1080
|
+
if current_user is not None:
|
|
1081
|
+
current_task_events.append(item)
|
|
1082
|
+
continue
|
|
1083
|
+
|
|
1084
|
+
# Events before the first user message.
|
|
1085
|
+
next_item = renderable_items[idx + 1] if idx + 1 < len(renderable_items) else None
|
|
1086
|
+
if isinstance(item, message.DeveloperMessage):
|
|
1087
|
+
blocks.append(_render_developer_message(item, next_item))
|
|
916
1088
|
elif isinstance(item, message.SystemMessage):
|
|
917
|
-
|
|
918
|
-
if
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
blocks.append(
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
f"</summary>"
|
|
927
|
-
f'<div class="details-content" style="white-space: pre-wrap;">{_escape_html(content)}</div>'
|
|
928
|
-
f"</details>"
|
|
929
|
-
)
|
|
1089
|
+
rendered = _render_system_message(item)
|
|
1090
|
+
if rendered:
|
|
1091
|
+
blocks.append(rendered)
|
|
1092
|
+
elif isinstance(item, message.AssistantMessage):
|
|
1093
|
+
blocks.append(_render_task_full([item]))
|
|
1094
|
+
elif isinstance(item, model.TaskMetadataItem):
|
|
1095
|
+
blocks.append(_render_metadata_item(item))
|
|
1096
|
+
|
|
1097
|
+
_flush_task()
|
|
930
1098
|
|
|
931
1099
|
return "\n".join(blocks)
|
|
932
1100
|
|
|
@@ -202,6 +202,86 @@
|
|
|
202
202
|
display: block;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
/* Task: one user message + full agent run */
|
|
206
|
+
.task {
|
|
207
|
+
margin-bottom: 24px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.task:last-child {
|
|
211
|
+
margin-bottom: 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* Task steps: fold intermediate events by default */
|
|
215
|
+
details.task-steps {
|
|
216
|
+
margin: 6px 0 0 0;
|
|
217
|
+
background: transparent;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
details.task-steps > summary {
|
|
221
|
+
list-style: none;
|
|
222
|
+
user-select: none;
|
|
223
|
+
cursor: pointer;
|
|
224
|
+
display: inline-flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
gap: 8px;
|
|
227
|
+
padding: 4px 10px;
|
|
228
|
+
border-radius: 999px;
|
|
229
|
+
border: 1px solid var(--border);
|
|
230
|
+
background: var(--bg-card);
|
|
231
|
+
color: var(--text-dim);
|
|
232
|
+
font-family: var(--font-mono);
|
|
233
|
+
font-size: var(--font-size-xs);
|
|
234
|
+
line-height: 1.2;
|
|
235
|
+
transition: color 0.2s, border-color 0.2s, background 0.2s;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
details.task-steps > summary::-webkit-details-marker {
|
|
239
|
+
display: none;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
details.task-steps > summary::before {
|
|
243
|
+
content: "▶";
|
|
244
|
+
font-size: 0.8em;
|
|
245
|
+
color: var(--accent);
|
|
246
|
+
transform: translateY(-0.05em);
|
|
247
|
+
transition: transform 0.2s;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
details.task-steps[open] > summary {
|
|
251
|
+
color: var(--accent);
|
|
252
|
+
border-color: var(--accent);
|
|
253
|
+
background: var(--accent-dim);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
details.task-steps[open] > summary::before {
|
|
257
|
+
transform: rotate(90deg) translateY(-0.05em);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
details.task-steps > summary:hover {
|
|
261
|
+
color: var(--text);
|
|
262
|
+
border-color: var(--accent);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
details.task-steps > .task-steps-content {
|
|
266
|
+
margin-top: 12px;
|
|
267
|
+
padding: 0 0 0 18px;
|
|
268
|
+
margin-left: 10px;
|
|
269
|
+
border-left: 2px solid var(--border);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* When expanded, hide compact final answer to avoid duplication */
|
|
273
|
+
.task details.task-steps[open] ~ .task-final {
|
|
274
|
+
display: none;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.task-final {
|
|
278
|
+
margin-top: 12px;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.task-mermaid-result {
|
|
282
|
+
margin: 12px 0;
|
|
283
|
+
}
|
|
284
|
+
|
|
205
285
|
.message-group {
|
|
206
286
|
display: flex;
|
|
207
287
|
flex-direction: column;
|
|
@@ -1036,6 +1116,19 @@
|
|
|
1036
1116
|
.expandable.expanded {
|
|
1037
1117
|
cursor: auto;
|
|
1038
1118
|
}
|
|
1119
|
+
|
|
1120
|
+
.expandable-output {
|
|
1121
|
+
/* Default fallback; per-block override comes from inline style in export.py */
|
|
1122
|
+
--preview-max-lines: 8;
|
|
1123
|
+
}
|
|
1124
|
+
.expandable-output .preview-text {
|
|
1125
|
+
line-height: 1.45;
|
|
1126
|
+
}
|
|
1127
|
+
.expandable-output:not(.expanded) .preview-text {
|
|
1128
|
+
max-height: calc(var(--preview-max-lines) * 1.45em);
|
|
1129
|
+
overflow: hidden;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1039
1132
|
.expandable .full-text {
|
|
1040
1133
|
display: none;
|
|
1041
1134
|
}
|
|
@@ -1423,6 +1516,21 @@
|
|
|
1423
1516
|
el.textContent = el.textContent.trim();
|
|
1424
1517
|
});
|
|
1425
1518
|
|
|
1519
|
+
// Task steps toggle label
|
|
1520
|
+
document.querySelectorAll("details.task-steps").forEach((details) => {
|
|
1521
|
+
const summary = details.querySelector("summary");
|
|
1522
|
+
const count = details.getAttribute("data-step-count");
|
|
1523
|
+
if (!summary || !count) return;
|
|
1524
|
+
|
|
1525
|
+
const setLabel = () => {
|
|
1526
|
+
summary.textContent =
|
|
1527
|
+
(details.open ? "Hide" : "Show") + " " + count + " steps";
|
|
1528
|
+
};
|
|
1529
|
+
|
|
1530
|
+
setLabel();
|
|
1531
|
+
details.addEventListener("toggle", setLabel);
|
|
1532
|
+
});
|
|
1533
|
+
|
|
1426
1534
|
// Process markdown for collapsible sections
|
|
1427
1535
|
// foldH2: if true, H2 headers start collapsed; otherwise all headers start open
|
|
1428
1536
|
function structureMarkdown(root, foldH2 = false) {
|
|
@@ -1809,70 +1917,44 @@
|
|
|
1809
1917
|
});
|
|
1810
1918
|
}
|
|
1811
1919
|
|
|
1812
|
-
// 2.
|
|
1920
|
+
// 2. Tasks (User + final Assistant)
|
|
1813
1921
|
const stream = document.querySelector(".message-stream");
|
|
1814
1922
|
if (stream) {
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
userCount
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
} else if (roleLabel.classList.contains("assistant")) {
|
|
1829
|
-
assistantCount++;
|
|
1830
|
-
label = `ASSISTANT $$$${assistantCount}`;
|
|
1831
|
-
idPrefix = "assistant";
|
|
1832
|
-
} else {
|
|
1833
|
-
label = roleLabel.textContent.trim();
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
} else if (child.classList.contains("tool-call")) {
|
|
1837
|
-
const toolName = child.querySelector(".tool-name");
|
|
1838
|
-
if (toolName) {
|
|
1839
|
-
label = toolName.textContent.trim();
|
|
1840
|
-
idPrefix = "tool";
|
|
1841
|
-
|
|
1842
|
-
// Try to extract description for Sub Agents
|
|
1843
|
-
try {
|
|
1844
|
-
const argsContent = child.querySelector(".tool-args-content");
|
|
1845
|
-
if (argsContent) {
|
|
1846
|
-
const argsText = argsContent.textContent.trim();
|
|
1847
|
-
if (argsText.startsWith("{")) {
|
|
1848
|
-
const args = JSON.parse(argsText);
|
|
1849
|
-
if (
|
|
1850
|
-
args &&
|
|
1851
|
-
typeof args.description === "string" &&
|
|
1852
|
-
args.description.trim()
|
|
1853
|
-
) {
|
|
1854
|
-
let desc = args.description.trim();
|
|
1855
|
-
if (desc.length > 30) {
|
|
1856
|
-
desc = desc.substring(0, 30) + "…";
|
|
1857
|
-
}
|
|
1858
|
-
label = label + " - " + desc;
|
|
1859
|
-
}
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
} catch (e) {
|
|
1863
|
-
// Ignore JSON parse errors
|
|
1864
|
-
}
|
|
1865
|
-
} else {
|
|
1866
|
-
label = "Tool";
|
|
1923
|
+
Array.from(stream.querySelectorAll(":scope > .task")).forEach((task) => {
|
|
1924
|
+
const userBlock = task.querySelector(":scope > .message-group");
|
|
1925
|
+
if (userBlock) {
|
|
1926
|
+
const roleLabel = userBlock.querySelector(".role-label");
|
|
1927
|
+
if (roleLabel && roleLabel.classList.contains("user")) {
|
|
1928
|
+
userCount++;
|
|
1929
|
+
userBlock.id =
|
|
1930
|
+
userBlock.id || "user-" + Math.random().toString(36).substr(2, 9);
|
|
1931
|
+
sections.push({
|
|
1932
|
+
id: userBlock.id,
|
|
1933
|
+
label: "USER " + userCount,
|
|
1934
|
+
el: userBlock,
|
|
1935
|
+
});
|
|
1867
1936
|
}
|
|
1868
1937
|
}
|
|
1869
1938
|
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1939
|
+
const finalContainer = task.querySelector(":scope > .task-final");
|
|
1940
|
+
if (!finalContainer) return;
|
|
1941
|
+
|
|
1942
|
+
Array.from(finalContainer.querySelectorAll(":scope > .message-group")).forEach(
|
|
1943
|
+
(child) => {
|
|
1944
|
+
const roleLabel = child.querySelector(".role-label");
|
|
1945
|
+
if (!roleLabel || !roleLabel.classList.contains("assistant")) return;
|
|
1946
|
+
|
|
1947
|
+
assistantCount++;
|
|
1948
|
+
child.id =
|
|
1949
|
+
child.id ||
|
|
1950
|
+
"assistant-" + Math.random().toString(36).substr(2, 9);
|
|
1951
|
+
sections.push({
|
|
1952
|
+
id: child.id,
|
|
1953
|
+
label: "ASSISTANT " + assistantCount,
|
|
1954
|
+
el: child,
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
);
|
|
1876
1958
|
});
|
|
1877
1959
|
}
|
|
1878
1960
|
|
|
Binary file
|
|
Binary file
|
klaude_code/skill/loader.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from klaude_code.core.loaded_skills import get_loaded_skill_names_by_location
|
|
1
2
|
from klaude_code.protocol import commands, events, message
|
|
2
3
|
|
|
3
4
|
from .command_abc import Agent, CommandABC, CommandResult
|
|
@@ -30,6 +31,7 @@ class RefreshTerminalCommand(CommandABC):
|
|
|
30
31
|
session_id=agent.session.id,
|
|
31
32
|
work_dir=str(agent.session.work_dir),
|
|
32
33
|
llm_config=agent.get_llm_client().get_llm_config(),
|
|
34
|
+
loaded_skills=get_loaded_skill_names_by_location(),
|
|
33
35
|
),
|
|
34
36
|
events.ReplayHistoryEvent(
|
|
35
37
|
session_id=agent.session.id,
|