klaude-code 1.2.3__py3-none-any.whl → 1.2.4__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/command/__init__.py +2 -0
- klaude_code/command/export_cmd.py +3 -1
- klaude_code/command/status_cmd.py +111 -0
- klaude_code/protocol/commands.py +1 -0
- klaude_code/protocol/model.py +7 -0
- klaude_code/session/templates/export_session.html +207 -1
- klaude_code/ui/renderers/developer.py +56 -1
- {klaude_code-1.2.3.dist-info → klaude_code-1.2.4.dist-info}/METADATA +1 -1
- {klaude_code-1.2.3.dist-info → klaude_code-1.2.4.dist-info}/RECORD +11 -10
- {klaude_code-1.2.3.dist-info → klaude_code-1.2.4.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.3.dist-info → klaude_code-1.2.4.dist-info}/entry_points.txt +0 -0
klaude_code/command/__init__.py
CHANGED
|
@@ -16,6 +16,7 @@ from .registry import (
|
|
|
16
16
|
load_prompt_commands,
|
|
17
17
|
register_command,
|
|
18
18
|
)
|
|
19
|
+
from .status_cmd import StatusCommand
|
|
19
20
|
from .terminal_setup_cmd import TerminalSetupCommand
|
|
20
21
|
|
|
21
22
|
# Dynamically load prompt commands
|
|
@@ -28,6 +29,7 @@ __all__ = [
|
|
|
28
29
|
"ModelCommand",
|
|
29
30
|
"ExportCommand",
|
|
30
31
|
"RefreshTerminalCommand",
|
|
32
|
+
"StatusCommand",
|
|
31
33
|
"TerminalSetupCommand",
|
|
32
34
|
"register_command",
|
|
33
35
|
"CommandABC",
|
|
@@ -46,10 +46,12 @@ class ExportCommand(CommandABC):
|
|
|
46
46
|
)
|
|
47
47
|
return CommandResult(events=[event])
|
|
48
48
|
except Exception as exc: # pragma: no cover - safeguard for unexpected errors
|
|
49
|
+
import traceback
|
|
50
|
+
|
|
49
51
|
event = events.DeveloperMessageEvent(
|
|
50
52
|
session_id=agent.session.id,
|
|
51
53
|
item=model.DeveloperMessageItem(
|
|
52
|
-
content=f"Failed to export session: {exc}",
|
|
54
|
+
content=f"Failed to export session: {exc}\n{traceback.format_exc()}",
|
|
53
55
|
command_output=model.CommandOutput(command_name=self.name, is_error=True),
|
|
54
56
|
),
|
|
55
57
|
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
2
|
+
from klaude_code.command.registry import register_command
|
|
3
|
+
from klaude_code.core.agent import Agent
|
|
4
|
+
from klaude_code.protocol import commands, events, model
|
|
5
|
+
from klaude_code.session.session import Session
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def accumulate_session_usage(session: Session) -> tuple[model.Usage, int]:
|
|
9
|
+
"""Accumulate usage statistics from all ResponseMetadataItems in session history.
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
A tuple of (accumulated_usage, task_count)
|
|
13
|
+
"""
|
|
14
|
+
total = model.Usage()
|
|
15
|
+
task_count = 0
|
|
16
|
+
|
|
17
|
+
for item in session.conversation_history:
|
|
18
|
+
if isinstance(item, model.ResponseMetadataItem) and item.usage:
|
|
19
|
+
task_count += 1
|
|
20
|
+
usage = item.usage
|
|
21
|
+
total.input_tokens += usage.input_tokens
|
|
22
|
+
total.cached_tokens += usage.cached_tokens
|
|
23
|
+
total.reasoning_tokens += usage.reasoning_tokens
|
|
24
|
+
total.output_tokens += usage.output_tokens
|
|
25
|
+
total.total_tokens += usage.total_tokens
|
|
26
|
+
|
|
27
|
+
# Accumulate costs
|
|
28
|
+
if usage.input_cost is not None:
|
|
29
|
+
total.input_cost = (total.input_cost or 0.0) + usage.input_cost
|
|
30
|
+
if usage.output_cost is not None:
|
|
31
|
+
total.output_cost = (total.output_cost or 0.0) + usage.output_cost
|
|
32
|
+
if usage.cache_read_cost is not None:
|
|
33
|
+
total.cache_read_cost = (total.cache_read_cost or 0.0) + usage.cache_read_cost
|
|
34
|
+
if usage.total_cost is not None:
|
|
35
|
+
total.total_cost = (total.total_cost or 0.0) + usage.total_cost
|
|
36
|
+
|
|
37
|
+
# Keep the latest context_usage_percent
|
|
38
|
+
if usage.context_usage_percent is not None:
|
|
39
|
+
total.context_usage_percent = usage.context_usage_percent
|
|
40
|
+
|
|
41
|
+
return total, task_count
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _format_tokens(tokens: int) -> str:
|
|
45
|
+
"""Format token count with K/M suffix for readability."""
|
|
46
|
+
if tokens >= 1_000_000:
|
|
47
|
+
return f"{tokens / 1_000_000:.2f}M"
|
|
48
|
+
if tokens >= 1_000:
|
|
49
|
+
return f"{tokens / 1_000:.1f}K"
|
|
50
|
+
return str(tokens)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _format_cost(cost: float | None) -> str:
|
|
54
|
+
"""Format cost in USD."""
|
|
55
|
+
if cost is None:
|
|
56
|
+
return "-"
|
|
57
|
+
if cost < 0.01:
|
|
58
|
+
return f"${cost:.4f}"
|
|
59
|
+
return f"${cost:.2f}"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def format_status_content(usage: model.Usage) -> str:
|
|
63
|
+
"""Format session status as comma-separated text."""
|
|
64
|
+
parts: list[str] = []
|
|
65
|
+
|
|
66
|
+
parts.append(f"Input: {_format_tokens(usage.input_tokens)}")
|
|
67
|
+
if usage.cached_tokens > 0:
|
|
68
|
+
parts.append(f"Cached: {_format_tokens(usage.cached_tokens)}")
|
|
69
|
+
parts.append(f"Output: {_format_tokens(usage.output_tokens)}")
|
|
70
|
+
parts.append(f"Total: {_format_tokens(usage.total_tokens)}")
|
|
71
|
+
|
|
72
|
+
if usage.total_cost is not None:
|
|
73
|
+
parts.append(f"Cost: {_format_cost(usage.total_cost)}")
|
|
74
|
+
|
|
75
|
+
return ", ".join(parts)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@register_command
|
|
79
|
+
class StatusCommand(CommandABC):
|
|
80
|
+
"""Display session usage statistics."""
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def name(self) -> commands.CommandName:
|
|
84
|
+
return commands.CommandName.STATUS
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def summary(self) -> str:
|
|
88
|
+
return "Show session usage statistics"
|
|
89
|
+
|
|
90
|
+
async def run(self, raw: str, agent: Agent) -> CommandResult:
|
|
91
|
+
session = agent.session
|
|
92
|
+
usage, task_count = accumulate_session_usage(session)
|
|
93
|
+
|
|
94
|
+
event = events.DeveloperMessageEvent(
|
|
95
|
+
session_id=session.id,
|
|
96
|
+
item=model.DeveloperMessageItem(
|
|
97
|
+
content=format_status_content(usage),
|
|
98
|
+
command_output=model.CommandOutput(
|
|
99
|
+
command_name=self.name,
|
|
100
|
+
ui_extra=model.ToolResultUIExtra(
|
|
101
|
+
type=model.ToolResultUIExtraType.SESSION_STATUS,
|
|
102
|
+
session_status=model.SessionStatusUIExtra(
|
|
103
|
+
usage=usage,
|
|
104
|
+
task_count=task_count,
|
|
105
|
+
),
|
|
106
|
+
),
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return CommandResult(events=[event])
|
klaude_code/protocol/commands.py
CHANGED
|
@@ -11,6 +11,7 @@ class CommandName(str, Enum):
|
|
|
11
11
|
CLEAR = "clear"
|
|
12
12
|
TERMINAL_SETUP = "terminal-setup"
|
|
13
13
|
EXPORT = "export"
|
|
14
|
+
STATUS = "status"
|
|
14
15
|
# PLAN and DOC are dynamically registered now, but kept here if needed for reference
|
|
15
16
|
# or we can remove them if no code explicitly imports them.
|
|
16
17
|
# PLAN = "plan"
|
klaude_code/protocol/model.py
CHANGED
|
@@ -45,6 +45,7 @@ class ToolResultUIExtraType(str, Enum):
|
|
|
45
45
|
SESSION_ID = "session_id"
|
|
46
46
|
MERMAID_LINK = "mermaid_link"
|
|
47
47
|
TRUNCATION = "truncation"
|
|
48
|
+
SESSION_STATUS = "session_status"
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
class ToolSideEffect(str, Enum):
|
|
@@ -62,6 +63,11 @@ class TruncationUIExtra(BaseModel):
|
|
|
62
63
|
truncated_length: int
|
|
63
64
|
|
|
64
65
|
|
|
66
|
+
class SessionStatusUIExtra(BaseModel):
|
|
67
|
+
usage: "Usage"
|
|
68
|
+
task_count: int
|
|
69
|
+
|
|
70
|
+
|
|
65
71
|
class ToolResultUIExtra(BaseModel):
|
|
66
72
|
type: ToolResultUIExtraType
|
|
67
73
|
diff_text: str | None = None
|
|
@@ -69,6 +75,7 @@ class ToolResultUIExtra(BaseModel):
|
|
|
69
75
|
session_id: str | None = None
|
|
70
76
|
mermaid_link: MermaidLinkUIExtra | None = None
|
|
71
77
|
truncation: TruncationUIExtra | None = None
|
|
78
|
+
session_status: SessionStatusUIExtra | None = None
|
|
72
79
|
|
|
73
80
|
|
|
74
81
|
class AtPatternParseResult(BaseModel):
|
|
@@ -908,9 +908,81 @@
|
|
|
908
908
|
font-size: var(--font-size-sm);
|
|
909
909
|
margin-top: 2px;
|
|
910
910
|
}
|
|
911
|
+
/* TOC Sidebar */
|
|
912
|
+
.toc-sidebar {
|
|
913
|
+
position: fixed;
|
|
914
|
+
top: 33vh;
|
|
915
|
+
left: 20px;
|
|
916
|
+
width: 220px;
|
|
917
|
+
bottom: 33vh;
|
|
918
|
+
overflow-y: auto;
|
|
919
|
+
padding-right: 12px;
|
|
920
|
+
/* Vertical padding to offset mask */
|
|
921
|
+
padding-top: 30px;
|
|
922
|
+
padding-bottom: 30px;
|
|
923
|
+
display: none;
|
|
924
|
+
scrollbar-width: none;
|
|
925
|
+
z-index: 100;
|
|
926
|
+
/* Linear mask for fading edges */
|
|
927
|
+
-webkit-mask-image: linear-gradient(
|
|
928
|
+
to bottom,
|
|
929
|
+
transparent 0%,
|
|
930
|
+
black 30px,
|
|
931
|
+
black calc(100% - 30px),
|
|
932
|
+
transparent 100%
|
|
933
|
+
);
|
|
934
|
+
mask-image: linear-gradient(
|
|
935
|
+
to bottom,
|
|
936
|
+
transparent 0%,
|
|
937
|
+
black 30px,
|
|
938
|
+
black calc(100% - 30px),
|
|
939
|
+
transparent 100%
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
.toc-sidebar::-webkit-scrollbar {
|
|
944
|
+
display: none;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/* Show TOC on wide screens */
|
|
948
|
+
@media (min-width: 1400px) {
|
|
949
|
+
.toc-sidebar {
|
|
950
|
+
display: block;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
.toc-item {
|
|
955
|
+
display: block;
|
|
956
|
+
padding: 3px 10px;
|
|
957
|
+
margin-bottom: 1px;
|
|
958
|
+
font-family: var(--font-mono);
|
|
959
|
+
font-size: 12px;
|
|
960
|
+
line-height: 1.3;
|
|
961
|
+
color: var(--text-dim);
|
|
962
|
+
text-decoration: none;
|
|
963
|
+
cursor: pointer;
|
|
964
|
+
transition: all 0.2s;
|
|
965
|
+
white-space: nowrap;
|
|
966
|
+
overflow: hidden;
|
|
967
|
+
text-overflow: ellipsis;
|
|
968
|
+
border-radius: 4px;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
.toc-item:hover {
|
|
972
|
+
color: var(--text);
|
|
973
|
+
background: rgba(0, 0, 0, 0.03);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
.toc-item.active {
|
|
977
|
+
color: var(--text);
|
|
978
|
+
background: var(--bg-card);
|
|
979
|
+
font-weight: bold;
|
|
980
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
981
|
+
}
|
|
911
982
|
</style>
|
|
912
983
|
</head>
|
|
913
984
|
<body>
|
|
985
|
+
<div id="toc-sidebar" class="toc-sidebar"></div>
|
|
914
986
|
<div class="container">
|
|
915
987
|
<div class="header">
|
|
916
988
|
<h1>Klaude Code</h1>
|
|
@@ -941,7 +1013,9 @@
|
|
|
941
1013
|
<div
|
|
942
1014
|
class="details-content system-prompt-content"
|
|
943
1015
|
style="font-family: var(--font-mono); white-space: pre-wrap"
|
|
944
|
-
|
|
1016
|
+
>
|
|
1017
|
+
$system_prompt
|
|
1018
|
+
</div>
|
|
945
1019
|
</details>
|
|
946
1020
|
|
|
947
1021
|
<details class="collapsible-section">
|
|
@@ -1215,6 +1289,138 @@
|
|
|
1215
1289
|
behavior: "smooth",
|
|
1216
1290
|
});
|
|
1217
1291
|
});
|
|
1292
|
+
|
|
1293
|
+
// TOC Logic
|
|
1294
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
1295
|
+
const tocSidebar = document.getElementById("toc-sidebar");
|
|
1296
|
+
if (!tocSidebar) return;
|
|
1297
|
+
|
|
1298
|
+
const sections = [];
|
|
1299
|
+
let userCount = 0;
|
|
1300
|
+
let assistantCount = 0;
|
|
1301
|
+
|
|
1302
|
+
// 1. System Prompt
|
|
1303
|
+
const sysPrompt = document.querySelector(
|
|
1304
|
+
".collapsible-section summary"
|
|
1305
|
+
);
|
|
1306
|
+
if (sysPrompt && sysPrompt.textContent.includes("System Prompt")) {
|
|
1307
|
+
const details = sysPrompt.parentElement;
|
|
1308
|
+
details.id = details.id || "section-system-prompt";
|
|
1309
|
+
sections.push({
|
|
1310
|
+
id: details.id,
|
|
1311
|
+
label: "System Prompt",
|
|
1312
|
+
el: details,
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// 2. Messages and Tools
|
|
1317
|
+
const stream = document.querySelector(".message-stream");
|
|
1318
|
+
if (stream) {
|
|
1319
|
+
// Use a Walker to find top-level relevant items if structure is complex
|
|
1320
|
+
// But assuming flat children of message-stream based on CSS
|
|
1321
|
+
Array.from(stream.children).forEach((child) => {
|
|
1322
|
+
let label = null;
|
|
1323
|
+
let idPrefix = "section";
|
|
1324
|
+
|
|
1325
|
+
if (child.classList.contains("message-group")) {
|
|
1326
|
+
const roleLabel = child.querySelector(".role-label");
|
|
1327
|
+
if (roleLabel) {
|
|
1328
|
+
if (roleLabel.classList.contains("user")) {
|
|
1329
|
+
userCount++;
|
|
1330
|
+
label = `USER $${userCount}`;
|
|
1331
|
+
idPrefix = "user";
|
|
1332
|
+
} else if (roleLabel.classList.contains("assistant")) {
|
|
1333
|
+
assistantCount++;
|
|
1334
|
+
label = `ASSISTANT $${assistantCount}`;
|
|
1335
|
+
idPrefix = "assistant";
|
|
1336
|
+
} else {
|
|
1337
|
+
label = roleLabel.textContent.trim();
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
} else if (child.classList.contains("tool-call")) {
|
|
1341
|
+
const toolName = child.querySelector(".tool-name");
|
|
1342
|
+
if (toolName) {
|
|
1343
|
+
label = toolName.textContent.trim();
|
|
1344
|
+
idPrefix = "tool";
|
|
1345
|
+
} else {
|
|
1346
|
+
label = "Tool";
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
if (label) {
|
|
1351
|
+
child.id =
|
|
1352
|
+
child.id ||
|
|
1353
|
+
`$${idPrefix}-$${Math.random().toString(36).substr(2, 9)}`;
|
|
1354
|
+
sections.push({ id: child.id, label: label, el: child });
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// Render TOC
|
|
1360
|
+
sections.forEach((section) => {
|
|
1361
|
+
const item = document.createElement("div");
|
|
1362
|
+
item.className = "toc-item";
|
|
1363
|
+
item.textContent = section.label;
|
|
1364
|
+
item.dataset.target = section.id;
|
|
1365
|
+
item.addEventListener("click", () => {
|
|
1366
|
+
section.el.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
1367
|
+
});
|
|
1368
|
+
tocSidebar.appendChild(item);
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1371
|
+
// Scroll Spy with throttling
|
|
1372
|
+
let ticking = false;
|
|
1373
|
+
const offset = 150; // Pixel offset for "active" area
|
|
1374
|
+
|
|
1375
|
+
function updateActiveSection() {
|
|
1376
|
+
let currentId = sections.length > 0 ? sections[0].id : null;
|
|
1377
|
+
const scrollY = window.scrollY;
|
|
1378
|
+
|
|
1379
|
+
// We look for the last section that has passed the top threshold (+ offset)
|
|
1380
|
+
for (let i = 0; i < sections.length; i++) {
|
|
1381
|
+
const el = sections[i].el;
|
|
1382
|
+
// If element top is above the "active line" (scrollY + offset)
|
|
1383
|
+
if (el.offsetTop <= scrollY + offset) {
|
|
1384
|
+
currentId = sections[i].id;
|
|
1385
|
+
} else {
|
|
1386
|
+
// Since sections are ordered by position, we can stop once we find one below the line
|
|
1387
|
+
break;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// Update UI
|
|
1392
|
+
document.querySelectorAll(".toc-item").forEach((item) => {
|
|
1393
|
+
const isActive = item.dataset.target === currentId;
|
|
1394
|
+
if (item.classList.contains("active") !== isActive) {
|
|
1395
|
+
item.classList.toggle("active", isActive);
|
|
1396
|
+
if (isActive) {
|
|
1397
|
+
// Auto-scroll sidebar to visible
|
|
1398
|
+
const sidebarRect = tocSidebar.getBoundingClientRect();
|
|
1399
|
+
const itemRect = item.getBoundingClientRect();
|
|
1400
|
+
// Check if item is out of view in sidebar (accounting for mask)
|
|
1401
|
+
if (
|
|
1402
|
+
itemRect.top < sidebarRect.top + 40 ||
|
|
1403
|
+
itemRect.bottom > sidebarRect.bottom - 40
|
|
1404
|
+
) {
|
|
1405
|
+
item.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
ticking = false;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
window.addEventListener("scroll", () => {
|
|
1415
|
+
if (!ticking) {
|
|
1416
|
+
window.requestAnimationFrame(updateActiveSection);
|
|
1417
|
+
ticking = true;
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
// Initial update
|
|
1422
|
+
updateActiveSection();
|
|
1423
|
+
});
|
|
1218
1424
|
</script>
|
|
1219
1425
|
</body>
|
|
1220
1426
|
</html>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from rich.console import Group, RenderableType
|
|
2
2
|
from rich.padding import Padding
|
|
3
|
+
from rich.table import Table
|
|
3
4
|
from rich.text import Text
|
|
4
5
|
|
|
5
|
-
from klaude_code.protocol import commands, events
|
|
6
|
+
from klaude_code.protocol import commands, events, model
|
|
6
7
|
from klaude_code.ui.renderers import diffs as r_diffs
|
|
7
8
|
from klaude_code.ui.renderers.common import create_grid
|
|
8
9
|
from klaude_code.ui.renderers.tools import render_path
|
|
@@ -97,7 +98,61 @@ def render_command_output(e: events.DeveloperMessageEvent) -> RenderableType:
|
|
|
97
98
|
return r_diffs.render_diff_panel(e.item.content, show_file_name=True)
|
|
98
99
|
case commands.CommandName.HELP:
|
|
99
100
|
return Padding.indent(Text.from_markup(e.item.content or ""), level=2)
|
|
101
|
+
case commands.CommandName.STATUS:
|
|
102
|
+
return _render_status_output(e.item.command_output)
|
|
100
103
|
case _:
|
|
101
104
|
content = e.item.content or "(no content)"
|
|
102
105
|
style = ThemeKey.TOOL_RESULT if not e.item.command_output.is_error else ThemeKey.ERROR
|
|
103
106
|
return Padding.indent(Text(truncate_display(content), style=style), level=2)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _format_tokens(tokens: int) -> str:
|
|
110
|
+
"""Format token count with K/M suffix for readability."""
|
|
111
|
+
if tokens >= 1_000_000:
|
|
112
|
+
return f"{tokens / 1_000_000:.2f}M"
|
|
113
|
+
if tokens >= 1_000:
|
|
114
|
+
return f"{tokens / 1_000:.1f}K"
|
|
115
|
+
return str(tokens)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _format_cost(cost: float | None) -> str:
|
|
119
|
+
"""Format cost in USD."""
|
|
120
|
+
if cost is None:
|
|
121
|
+
return "-"
|
|
122
|
+
if cost < 0.01:
|
|
123
|
+
return f"${cost:.4f}"
|
|
124
|
+
return f"${cost:.2f}"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _render_status_output(command_output: model.CommandOutput) -> RenderableType:
|
|
128
|
+
"""Render session status as a two-column table with sections."""
|
|
129
|
+
if not command_output.ui_extra or not command_output.ui_extra.session_status:
|
|
130
|
+
return Text("(no status data)", style=ThemeKey.TOOL_RESULT)
|
|
131
|
+
|
|
132
|
+
status = command_output.ui_extra.session_status
|
|
133
|
+
usage = status.usage
|
|
134
|
+
|
|
135
|
+
table = Table.grid(padding=(0, 2))
|
|
136
|
+
table.add_column(style=ThemeKey.TOOL_RESULT, no_wrap=True)
|
|
137
|
+
table.add_column(style=ThemeKey.TOOL_RESULT, no_wrap=True)
|
|
138
|
+
# Token Usage section
|
|
139
|
+
table.add_row(Text("Token Usage", style="bold"), "")
|
|
140
|
+
table.add_row("Input Tokens", _format_tokens(usage.input_tokens))
|
|
141
|
+
if usage.cached_tokens > 0:
|
|
142
|
+
table.add_row("Cached Tokens", _format_tokens(usage.cached_tokens))
|
|
143
|
+
if usage.reasoning_tokens > 0:
|
|
144
|
+
table.add_row("Reasoning Tokens", _format_tokens(usage.reasoning_tokens))
|
|
145
|
+
table.add_row("Output Tokens", _format_tokens(usage.output_tokens))
|
|
146
|
+
table.add_row("Total Tokens", _format_tokens(usage.total_tokens))
|
|
147
|
+
|
|
148
|
+
# Cost section
|
|
149
|
+
if usage.total_cost is not None:
|
|
150
|
+
table.add_row("", "") # Empty line
|
|
151
|
+
table.add_row(Text("Cost", style="bold"), "")
|
|
152
|
+
table.add_row("Input Cost", _format_cost(usage.input_cost))
|
|
153
|
+
if usage.cache_read_cost is not None and usage.cache_read_cost > 0:
|
|
154
|
+
table.add_row("Cache Read Cost", _format_cost(usage.cache_read_cost))
|
|
155
|
+
table.add_row("Output Cost", _format_cost(usage.output_cost))
|
|
156
|
+
table.add_row("Total Cost", _format_cost(usage.total_cost))
|
|
157
|
+
|
|
158
|
+
return Padding.indent(table, level=2)
|
|
@@ -3,11 +3,11 @@ klaude_code/cli/__init__.py,sha256=YzlAoWAr5rx5oe6B_4zPxRFS4QaZauuy1AFwampP5fg,4
|
|
|
3
3
|
klaude_code/cli/main.py,sha256=vQt1jZVX4pxQUYceWQcXkRO3w0glOUIhazI7uDex2fE,9279
|
|
4
4
|
klaude_code/cli/runtime.py,sha256=jipzUciIlY-w0hlUsid8HfSZuf_xCH23XoOuByJijU8,12288
|
|
5
5
|
klaude_code/cli/session_cmd.py,sha256=cIBm3uUurke-TfBvQHz9mGW29LOAh22FIpXVyypnwDo,2549
|
|
6
|
-
klaude_code/command/__init__.py,sha256=
|
|
6
|
+
klaude_code/command/__init__.py,sha256=ApRFYqzmDxXe-jJ0tHg4qe4XsJ4QSICu8ICQcmwxQ1Y,1125
|
|
7
7
|
klaude_code/command/clear_cmd.py,sha256=ojsDdZnv_-bDYBPecpdY85DfGicst9Jf9j7esg2z2hE,674
|
|
8
8
|
klaude_code/command/command_abc.py,sha256=WWY9qi7SILttPl6fJNi9ThevsqpGFoQJ1ecgoosd_Ms,2547
|
|
9
9
|
klaude_code/command/diff_cmd.py,sha256=306PcTeKokLmchp6mIWeZ2rokINsU5-2f4iGrjEbkCE,5269
|
|
10
|
-
klaude_code/command/export_cmd.py,sha256=
|
|
10
|
+
klaude_code/command/export_cmd.py,sha256=lsnBwxjiHyxgU7TPmT038ZVsOVqNxV2l-6U9T-dxI1g,3498
|
|
11
11
|
klaude_code/command/help_cmd.py,sha256=VddWegAJS5OjbAZ35c9Pb0SSDZpPH5PqRyQ--sdseII,1709
|
|
12
12
|
klaude_code/command/model_cmd.py,sha256=EApytBR6gl1Po9Mi7YVvDWgV-0ivgw-RQTnQ__EIvFQ,1510
|
|
13
13
|
klaude_code/command/prompt-dev-docs-update.md,sha256=g1IWIWIa-3qlNOw5mBA4N9H1_nvYcw8AKo7XoQw_AZQ,1855
|
|
@@ -16,6 +16,7 @@ klaude_code/command/prompt-init.md,sha256=a4_FQ3gKizqs2vl9oEY5jtG6HNhv3f-1b5RSCF
|
|
|
16
16
|
klaude_code/command/prompt_command.py,sha256=cDdLAyGk3JFS-nRlNzTnT8T35QQ-FC6RT0fD5ELZGO4,2405
|
|
17
17
|
klaude_code/command/refresh_cmd.py,sha256=kgpbcnqf1n_ihMW99AfTXeZEkE2_MNwgQ9DbNfl2jqY,1288
|
|
18
18
|
klaude_code/command/registry.py,sha256=OZhQqoLfWjqojJauemc8L2rnwPhYoAh2Kjx3HSAz_ZE,3554
|
|
19
|
+
klaude_code/command/status_cmd.py,sha256=mVGxyaqaP2qgjdjQ5BhweUb-Cc0VpsK8-7Pab92kKJg,3961
|
|
19
20
|
klaude_code/command/terminal_setup_cmd.py,sha256=bQfMDZIzak8emkmuaY43_2YaS7t908sUJ_orYwccTtY,10957
|
|
20
21
|
klaude_code/config/__init__.py,sha256=9XVCYYqzJtCi46I94hbUmJ2yTFuZ-UlH-QTx7OpLAkQ,292
|
|
21
22
|
klaude_code/config/config.py,sha256=Vc9u7-40T81Rbx1OdMqSWZLh3vf9aj4wmBUnIOH7jAw,6526
|
|
@@ -95,10 +96,10 @@ klaude_code/llm/responses/client.py,sha256=laNTjJ7WJ8hooqZ55XPfAkrurKKryMJTx4xz0
|
|
|
95
96
|
klaude_code/llm/responses/input.py,sha256=NtJhWyzg6XzHKuplgvta0ubzqA1u33gfgjL7X6xNeIw,6011
|
|
96
97
|
klaude_code/llm/usage.py,sha256=zDW47J-sxHhRJqDZihXeW2eyxpZS6F39qLsB8K0cua0,4240
|
|
97
98
|
klaude_code/protocol/__init__.py,sha256=aGUgzhYqvhuT3Mk2vj7lrHGriH4h9TSbqV1RsRFAZjQ,194
|
|
98
|
-
klaude_code/protocol/commands.py,sha256=
|
|
99
|
+
klaude_code/protocol/commands.py,sha256=zoqyBSpFZI8q8LP3rlr1mR-ekCufx-DfPGbzqsgx7X8,544
|
|
99
100
|
klaude_code/protocol/events.py,sha256=Pw8lY7rJLwk-FjKWtX8C_-Op7p0KWLaI-PH1_U7Uoyg,3290
|
|
100
101
|
klaude_code/protocol/llm_param.py,sha256=8_B8e83iPQs62CyVicTKlEKWBoOmRMlOOWkKBK__R58,4337
|
|
101
|
-
klaude_code/protocol/model.py,sha256=
|
|
102
|
+
klaude_code/protocol/model.py,sha256=op7xqxF8S8L1xwtI4-DWnZgUVW-9q0OUlTc_-suzuY8,7755
|
|
102
103
|
klaude_code/protocol/op.py,sha256=rkPvDRsOgPyibMI1n0pDO7ghFWJBfDGas9BnACtmfO4,2654
|
|
103
104
|
klaude_code/protocol/op_handler.py,sha256=_lnv3-RxKkrTfGTNBlQ23gbHJBEtMLC8O48SYWDtPjE,843
|
|
104
105
|
klaude_code/protocol/sub_agent.py,sha256=rD4nDX3jBp1HIseQTjd83I4VJ_fFcJ9NzkKCWO6JGsk,13351
|
|
@@ -107,7 +108,7 @@ klaude_code/session/__init__.py,sha256=oXcDA5w-gJCbzmlF8yuWy3ezIW9DgFBNUs-gJHUJ-
|
|
|
107
108
|
klaude_code/session/export.py,sha256=NDUZwjV6niQcZk2H4IRv9REviDGmw8ZmvGqko6TTjbA,23675
|
|
108
109
|
klaude_code/session/selector.py,sha256=HTboyzih7V8zbZoSsArJ-GVC1EnXTGocHJwFmZfbeSg,2567
|
|
109
110
|
klaude_code/session/session.py,sha256=KLE3QUNvt8d7Zu_Mj2x6rdHygzsUtLsvMzQbsiIOrKg,19195
|
|
110
|
-
klaude_code/session/templates/export_session.html,sha256=
|
|
111
|
+
klaude_code/session/templates/export_session.html,sha256=obRJtqmGEkSu2urixX4AeYlPTSEr9788omU---3PvkU,40850
|
|
111
112
|
klaude_code/trace/__init__.py,sha256=B-S4qdCj8W88AaC_gVmhTaejH6eLYClBVh2Q6aGAVBk,184
|
|
112
113
|
klaude_code/trace/log.py,sha256=0K0uek2KWq0zkUZijcO-TBM6STLe-h_8IIW0lx7V7ZA,4865
|
|
113
114
|
klaude_code/ui/__init__.py,sha256=AL1Ro0u8ObNPMj4sci_U4zakG7IpnI3bie-C63E2QPI,2873
|
|
@@ -131,7 +132,7 @@ klaude_code/ui/modes/repl/renderer.py,sha256=o1QubSm9lej-bVZ1G3ie-5qRxgPMDUo42kC
|
|
|
131
132
|
klaude_code/ui/renderers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
132
133
|
klaude_code/ui/renderers/assistant.py,sha256=Dxy6v4pX28RyWhnrjBteY8_NvDIi_jQa-j0mWt-eqWY,569
|
|
133
134
|
klaude_code/ui/renderers/common.py,sha256=TPH7LCbeJGqB8ArTsVitqJHEyOxHU6nwnRtvF04nLJ4,184
|
|
134
|
-
klaude_code/ui/renderers/developer.py,sha256
|
|
135
|
+
klaude_code/ui/renderers/developer.py,sha256=-pyy3Fj3M-Agsv3YsqGa6mMru1IoEQa40n6YkLJ4fqs,6019
|
|
135
136
|
klaude_code/ui/renderers/diffs.py,sha256=P--aLjvZy4z77FDx6uM9LlIYVjYlyZwj0MncdJTO2AA,7691
|
|
136
137
|
klaude_code/ui/renderers/errors.py,sha256=c_fbnoNOnvuI3Bb24IujwV8Mpes-qWS_xCWfAcBvg6A,517
|
|
137
138
|
klaude_code/ui/renderers/metadata.py,sha256=KSF6z4LHM_eBtaSPTLSIMK7GI-1MU6FUHaT2Hv4z8tk,6985
|
|
@@ -155,7 +156,7 @@ klaude_code/ui/utils/__init__.py,sha256=YEsCLjbCPaPza-UXTPUMTJTrc9BmNBUP5CbFWlsh
|
|
|
155
156
|
klaude_code/ui/utils/common.py,sha256=xzw-Mgj0agxrf22QxpH7YzVIpkMXIRY6SgXWtLYF0yU,2881
|
|
156
157
|
klaude_code/ui/utils/debouncer.py,sha256=TFF1z7B7-FxONEigkYohhShDlqo4cOcqydE9zz7JBHc,1270
|
|
157
158
|
klaude_code/version.py,sha256=QDgEqSFhyTSiABQFQc9VfujMPQcdjgoCaYTQsr94x0Q,4752
|
|
158
|
-
klaude_code-1.2.
|
|
159
|
-
klaude_code-1.2.
|
|
160
|
-
klaude_code-1.2.
|
|
161
|
-
klaude_code-1.2.
|
|
159
|
+
klaude_code-1.2.4.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
160
|
+
klaude_code-1.2.4.dist-info/entry_points.txt,sha256=7CWKjolvs6dZiYHpelhA_FRJ-sVDh43eu3iWuOhKc_w,53
|
|
161
|
+
klaude_code-1.2.4.dist-info/METADATA,sha256=O_BGmrEiCeeUj6RdP0wUe-zbPATYRKWa5fx2HJHk9JY,5140
|
|
162
|
+
klaude_code-1.2.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|