klaude-code 1.6.0__py3-none-any.whl → 1.7.1__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/list_model.py +55 -4
- klaude_code/cli/main.py +10 -0
- klaude_code/cli/runtime.py +2 -2
- klaude_code/cli/session_cmd.py +3 -2
- klaude_code/command/fork_session_cmd.py +7 -0
- klaude_code/config/assets/builtin_config.yaml +61 -2
- klaude_code/config/builtin_config.py +1 -0
- klaude_code/config/config.py +19 -0
- klaude_code/config/thinking.py +14 -0
- klaude_code/const.py +17 -2
- klaude_code/core/executor.py +16 -3
- klaude_code/core/task.py +5 -3
- klaude_code/core/tool/shell/command_safety.py +3 -5
- klaude_code/llm/anthropic/client.py +127 -114
- klaude_code/llm/bedrock/__init__.py +3 -0
- klaude_code/llm/bedrock/client.py +60 -0
- klaude_code/llm/google/__init__.py +3 -0
- klaude_code/llm/google/client.py +309 -0
- klaude_code/llm/google/input.py +215 -0
- klaude_code/llm/registry.py +10 -5
- klaude_code/protocol/events.py +1 -0
- klaude_code/protocol/llm_param.py +9 -0
- klaude_code/session/export.py +14 -2
- klaude_code/session/session.py +52 -3
- klaude_code/session/store.py +3 -0
- klaude_code/session/templates/export_session.html +210 -18
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +6 -46
- klaude_code/ui/modes/repl/renderer.py +5 -1
- klaude_code/ui/renderers/developer.py +1 -1
- klaude_code/ui/renderers/sub_agent.py +1 -1
- {klaude_code-1.6.0.dist-info → klaude_code-1.7.1.dist-info}/METADATA +82 -10
- {klaude_code-1.6.0.dist-info → klaude_code-1.7.1.dist-info}/RECORD +34 -29
- {klaude_code-1.6.0.dist-info → klaude_code-1.7.1.dist-info}/WHEEL +0 -0
- {klaude_code-1.6.0.dist-info → klaude_code-1.7.1.dist-info}/entry_points.txt +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title
|
|
6
|
+
<title>$first_user_message - Klaude Code</title>
|
|
7
7
|
<link
|
|
8
8
|
rel="icon"
|
|
9
9
|
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%230b5bd3%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22><polyline points=%2216 18 22 12 16 6%22></polyline><polyline points=%228 6 2 12 8 18%22></polyline></svg>"
|
|
@@ -16,6 +16,18 @@
|
|
|
16
16
|
href="https://cdn.jsdelivr.net/npm/@fontsource/jetbrains-mono/700.css"
|
|
17
17
|
rel="stylesheet"
|
|
18
18
|
/>
|
|
19
|
+
<link
|
|
20
|
+
href="https://cdn.jsdelivr.net/npm/@fontsource/inter@5.1.0/400.min.css"
|
|
21
|
+
rel="stylesheet"
|
|
22
|
+
/>
|
|
23
|
+
<link
|
|
24
|
+
href="https://cdn.jsdelivr.net/npm/@fontsource/inter@5.1.0/400-italic.min.css"
|
|
25
|
+
rel="stylesheet"
|
|
26
|
+
/>
|
|
27
|
+
<link
|
|
28
|
+
href="https://cdn.jsdelivr.net/npm/@fontsource/inter@5.1.0/700.min.css"
|
|
29
|
+
rel="stylesheet"
|
|
30
|
+
/>
|
|
19
31
|
<style>
|
|
20
32
|
@font-face {
|
|
21
33
|
font-family: 'TX-02';
|
|
@@ -44,9 +56,10 @@
|
|
|
44
56
|
--success: #1a7f37;
|
|
45
57
|
--error: #c62828;
|
|
46
58
|
--fg-inline-code: #1f4fbf;
|
|
59
|
+
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
47
60
|
--font-mono: "TX-02", "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
|
48
61
|
--font-markdown-mono: "TX-02", "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
|
49
|
-
--font-markdown:
|
|
62
|
+
--font-markdown: var(--font-sans);
|
|
50
63
|
--font-weight-bold: 700;
|
|
51
64
|
--font-size-xs: 12px;
|
|
52
65
|
--font-size-sm: 13px;
|
|
@@ -70,7 +83,7 @@
|
|
|
70
83
|
body {
|
|
71
84
|
background-color: var(--bg-body);
|
|
72
85
|
color: var(--text);
|
|
73
|
-
font-family: var(--font-
|
|
86
|
+
font-family: var(--font-sans);
|
|
74
87
|
line-height: 1.6;
|
|
75
88
|
font-size: var(--font-size-lg);
|
|
76
89
|
-webkit-font-smoothing: antialiased;
|
|
@@ -95,6 +108,7 @@
|
|
|
95
108
|
margin-bottom: 16px;
|
|
96
109
|
color: var(--text);
|
|
97
110
|
display: inline-block;
|
|
111
|
+
font-family: var(--font-mono);
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
.meta-grid {
|
|
@@ -114,6 +128,7 @@
|
|
|
114
128
|
font-weight: var(--font-weight-bold);
|
|
115
129
|
text-transform: uppercase;
|
|
116
130
|
color: var(--text-dim);
|
|
131
|
+
font-family: var(--font-mono);
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
.meta-value {
|
|
@@ -533,6 +548,7 @@
|
|
|
533
548
|
|
|
534
549
|
.tool-name {
|
|
535
550
|
font-weight: var(--font-weight-bold);
|
|
551
|
+
font-family: var(--font-sans);
|
|
536
552
|
color: var(--accent);
|
|
537
553
|
}
|
|
538
554
|
.tool-id {
|
|
@@ -677,6 +693,66 @@
|
|
|
677
693
|
display: block;
|
|
678
694
|
}
|
|
679
695
|
|
|
696
|
+
.sub-agent-rendered h1 {
|
|
697
|
+
font-size: 1.5em;
|
|
698
|
+
border-bottom: 1px solid var(--border);
|
|
699
|
+
padding-bottom: 0.3em;
|
|
700
|
+
margin-bottom: 16px;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/* Collapsible Markdown Headings */
|
|
704
|
+
.markdown-body details {
|
|
705
|
+
margin-top: 1em;
|
|
706
|
+
margin-bottom: 1em;
|
|
707
|
+
}
|
|
708
|
+
.markdown-body details > summary {
|
|
709
|
+
cursor: pointer;
|
|
710
|
+
list-style: none; /* Modern browsers */
|
|
711
|
+
position: relative;
|
|
712
|
+
padding-left: 1.5em; /* Space for triangle */
|
|
713
|
+
display: flex;
|
|
714
|
+
align-items: baseline;
|
|
715
|
+
gap: 8px;
|
|
716
|
+
margin-top: 24px;
|
|
717
|
+
margin-bottom: 16px;
|
|
718
|
+
}
|
|
719
|
+
.markdown-body details > summary::-webkit-details-marker {
|
|
720
|
+
display: none;
|
|
721
|
+
}
|
|
722
|
+
/* Triangle indicator */
|
|
723
|
+
.markdown-body details > summary::before {
|
|
724
|
+
content: "▶";
|
|
725
|
+
position: absolute;
|
|
726
|
+
left: 0;
|
|
727
|
+
/* Center vertically relative to line-height */
|
|
728
|
+
top: 0.2em;
|
|
729
|
+
font-size: 0.8em;
|
|
730
|
+
color: var(--text-dim);
|
|
731
|
+
transition: transform 0.2s;
|
|
732
|
+
line-height: inherit;
|
|
733
|
+
flex-shrink: 0;
|
|
734
|
+
}
|
|
735
|
+
.markdown-body details[open] > summary::before {
|
|
736
|
+
transform: rotate(90deg);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/* Indent content to align with triangle */
|
|
740
|
+
.markdown-body details > *:not(summary) {
|
|
741
|
+
margin-left: 1.5em;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/* Reset margins for headings inside summary to avoid double spacing */
|
|
745
|
+
.markdown-body details > summary > h2,
|
|
746
|
+
.markdown-body details > summary > h3,
|
|
747
|
+
.markdown-body details > summary > h4,
|
|
748
|
+
.markdown-body details > summary > h5,
|
|
749
|
+
.markdown-body details > summary > h6 {
|
|
750
|
+
margin-top: 0;
|
|
751
|
+
margin-bottom: 0;
|
|
752
|
+
flex-grow: 1;
|
|
753
|
+
width: 100%;
|
|
754
|
+
}
|
|
755
|
+
|
|
680
756
|
/* Markdown Elements */
|
|
681
757
|
.markdown-body {
|
|
682
758
|
font-family: var(--font-markdown);
|
|
@@ -702,6 +778,37 @@
|
|
|
702
778
|
line-height: 1.25;
|
|
703
779
|
}
|
|
704
780
|
|
|
781
|
+
.markdown-body h1 {
|
|
782
|
+
font-size: 1.75em;
|
|
783
|
+
background: #E8E6E1;
|
|
784
|
+
padding: 6px 12px;
|
|
785
|
+
border-radius: var(--radius-md);
|
|
786
|
+
display: inline-block;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.markdown-body h2 {
|
|
790
|
+
font-size: 1.35em;
|
|
791
|
+
padding-bottom: 0.2em;
|
|
792
|
+
border-bottom: 1px solid var(--border);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.markdown-body h3 {
|
|
796
|
+
font-size: 1.15em;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.markdown-body h4 {
|
|
800
|
+
font-size: 1em;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.markdown-body h5 {
|
|
804
|
+
font-size: 0.9em;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.markdown-body h6 {
|
|
808
|
+
font-size: 0.85em;
|
|
809
|
+
color: var(--text-dim);
|
|
810
|
+
}
|
|
811
|
+
|
|
705
812
|
.markdown-body a {
|
|
706
813
|
color: var(--accent);
|
|
707
814
|
text-decoration: none;
|
|
@@ -1143,10 +1250,10 @@
|
|
|
1143
1250
|
/* TOC Sidebar */
|
|
1144
1251
|
.toc-sidebar {
|
|
1145
1252
|
position: fixed;
|
|
1146
|
-
top:
|
|
1253
|
+
top: 20vh;
|
|
1147
1254
|
left: 20px;
|
|
1148
1255
|
width: 220px;
|
|
1149
|
-
bottom:
|
|
1256
|
+
bottom: 20vh;
|
|
1150
1257
|
overflow-y: auto;
|
|
1151
1258
|
padding-right: 12px;
|
|
1152
1259
|
/* Vertical padding to offset mask */
|
|
@@ -1219,12 +1326,6 @@
|
|
|
1219
1326
|
<div class="header">
|
|
1220
1327
|
<h1>Klaude Code</h1>
|
|
1221
1328
|
<div class="meta-grid">
|
|
1222
|
-
<div class="meta-item">
|
|
1223
|
-
<span class="meta-label">First Message</span>
|
|
1224
|
-
<span class="meta-value" title="$first_user_message"
|
|
1225
|
-
>$first_user_message</span
|
|
1226
|
-
>
|
|
1227
|
-
</div>
|
|
1228
1329
|
<div class="meta-item">
|
|
1229
1330
|
<span class="meta-label">Model</span>
|
|
1230
1331
|
<span class="meta-value">$model_name</span>
|
|
@@ -1308,9 +1409,12 @@
|
|
|
1308
1409
|
mermaid.initialize({
|
|
1309
1410
|
startOnLoad: true,
|
|
1310
1411
|
theme: "neutral",
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1412
|
+
themeVariables: {
|
|
1413
|
+
fontFamily:
|
|
1414
|
+
markdownMonoFont ||
|
|
1415
|
+
"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace",
|
|
1416
|
+
},
|
|
1417
|
+
securityLevel: "loose",
|
|
1314
1418
|
});
|
|
1315
1419
|
</script>
|
|
1316
1420
|
<script>
|
|
@@ -1319,6 +1423,65 @@
|
|
|
1319
1423
|
el.textContent = el.textContent.trim();
|
|
1320
1424
|
});
|
|
1321
1425
|
|
|
1426
|
+
// Process markdown for collapsible sections
|
|
1427
|
+
// foldH2: if true, H2 headers start collapsed; otherwise all headers start open
|
|
1428
|
+
function structureMarkdown(root, foldH2 = false) {
|
|
1429
|
+
// Find all headings
|
|
1430
|
+
const headers = Array.from(root.querySelectorAll("h1, h2, h3, h4, h5, h6"));
|
|
1431
|
+
if (!headers.length) return;
|
|
1432
|
+
|
|
1433
|
+
const stack = [{ element: root, level: 0 }];
|
|
1434
|
+
const children = Array.from(root.childNodes);
|
|
1435
|
+
|
|
1436
|
+
root.innerHTML = '';
|
|
1437
|
+
|
|
1438
|
+
children.forEach(node => {
|
|
1439
|
+
let level = 100; // Content level
|
|
1440
|
+
if (node.tagName && /^H[1-6]$$/.test(node.tagName)) {
|
|
1441
|
+
level = parseInt(node.tagName.substring(1));
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
if (level === 1) {
|
|
1445
|
+
// H1: not collapsible, just append directly
|
|
1446
|
+
while (stack.length > 1) {
|
|
1447
|
+
stack.pop();
|
|
1448
|
+
}
|
|
1449
|
+
stack[0].element.appendChild(node);
|
|
1450
|
+
|
|
1451
|
+
} else if (level >= 2 && level < 100) {
|
|
1452
|
+
// H2-H6: collapsible
|
|
1453
|
+
// Close any open sections that are deeper or same level
|
|
1454
|
+
while (stack.length > 1 && stack[stack.length - 1].level >= level) {
|
|
1455
|
+
stack.pop();
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
// Create new section
|
|
1459
|
+
const details = document.createElement('details');
|
|
1460
|
+
const summary = document.createElement('summary');
|
|
1461
|
+
|
|
1462
|
+
// Move header into summary
|
|
1463
|
+
summary.appendChild(node);
|
|
1464
|
+
details.appendChild(summary);
|
|
1465
|
+
|
|
1466
|
+
// Default open state: fold H2 only if foldH2 is true
|
|
1467
|
+
if (!(foldH2 && level === 2)) {
|
|
1468
|
+
details.setAttribute('open', '');
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// Append this details to the current parent in stack
|
|
1472
|
+
stack[stack.length - 1].element.appendChild(details);
|
|
1473
|
+
|
|
1474
|
+
// Push to stack as the new current container
|
|
1475
|
+
stack.push({ element: details, level: level });
|
|
1476
|
+
|
|
1477
|
+
} else {
|
|
1478
|
+
// Content node (p, ul, text, etc)
|
|
1479
|
+
// Append to current container
|
|
1480
|
+
stack[stack.length - 1].element.appendChild(node);
|
|
1481
|
+
}
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1322
1485
|
// Markdown rendering and Syntax Highlighting
|
|
1323
1486
|
document.querySelectorAll(".markdown-content").forEach((el) => {
|
|
1324
1487
|
const raw = el.dataset.raw;
|
|
@@ -1326,12 +1489,17 @@
|
|
|
1326
1489
|
// 1. Render Markdown
|
|
1327
1490
|
el.innerHTML = window.marked.parse(raw);
|
|
1328
1491
|
|
|
1329
|
-
// 2. Apply Syntax Highlighting
|
|
1492
|
+
// 2. Apply Syntax Highlighting
|
|
1330
1493
|
if (window.hljs) {
|
|
1331
1494
|
el.querySelectorAll("pre code").forEach((block) => {
|
|
1332
1495
|
hljs.highlightElement(block);
|
|
1333
1496
|
});
|
|
1334
1497
|
}
|
|
1498
|
+
|
|
1499
|
+
// 3. Make headings collapsible
|
|
1500
|
+
// Sub-agent results: fold H2 by default; others: all open
|
|
1501
|
+
const foldH2 = el.classList.contains("sub-agent-rendered");
|
|
1502
|
+
structureMarkdown(el, foldH2);
|
|
1335
1503
|
}
|
|
1336
1504
|
});
|
|
1337
1505
|
|
|
@@ -1655,11 +1823,11 @@
|
|
|
1655
1823
|
if (roleLabel) {
|
|
1656
1824
|
if (roleLabel.classList.contains("user")) {
|
|
1657
1825
|
userCount++;
|
|
1658
|
-
label = `USER
|
|
1826
|
+
label = `USER $$$${userCount}`;
|
|
1659
1827
|
idPrefix = "user";
|
|
1660
1828
|
} else if (roleLabel.classList.contains("assistant")) {
|
|
1661
1829
|
assistantCount++;
|
|
1662
|
-
label = `ASSISTANT
|
|
1830
|
+
label = `ASSISTANT $$$${assistantCount}`;
|
|
1663
1831
|
idPrefix = "assistant";
|
|
1664
1832
|
} else {
|
|
1665
1833
|
label = roleLabel.textContent.trim();
|
|
@@ -1670,6 +1838,30 @@
|
|
|
1670
1838
|
if (toolName) {
|
|
1671
1839
|
label = toolName.textContent.trim();
|
|
1672
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
|
+
}
|
|
1673
1865
|
} else {
|
|
1674
1866
|
label = "Tool";
|
|
1675
1867
|
}
|
|
@@ -1678,7 +1870,7 @@
|
|
|
1678
1870
|
if (label) {
|
|
1679
1871
|
child.id =
|
|
1680
1872
|
child.id ||
|
|
1681
|
-
|
|
1873
|
+
`$$$${idPrefix}-$$$${Math.random().toString(36).substr(2, 9)}`;
|
|
1682
1874
|
sections.push({ id: child.id, label: label, el: child });
|
|
1683
1875
|
}
|
|
1684
1876
|
});
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import contextlib
|
|
5
4
|
import shutil
|
|
6
|
-
import time
|
|
7
5
|
from collections.abc import AsyncIterator, Awaitable, Callable
|
|
8
6
|
from pathlib import Path
|
|
9
7
|
from typing import NamedTuple, override
|
|
@@ -245,9 +243,6 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
245
243
|
self._get_current_llm_config = get_current_llm_config
|
|
246
244
|
self._command_info_provider = command_info_provider
|
|
247
245
|
|
|
248
|
-
self._toast_message: str | None = None
|
|
249
|
-
self._toast_until: float = 0.0
|
|
250
|
-
|
|
251
246
|
# Use provided value if available to avoid redundant TTY queries that may interfere
|
|
252
247
|
# with prompt_toolkit's terminal state after interactive UIs have been used.
|
|
253
248
|
self._is_light_terminal_background = (
|
|
@@ -494,7 +489,6 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
494
489
|
if self._on_change_model is None:
|
|
495
490
|
return
|
|
496
491
|
await self._on_change_model(model_name)
|
|
497
|
-
self._set_toast(f"model: {model_name}")
|
|
498
492
|
|
|
499
493
|
# -------------------------------------------------------------------------
|
|
500
494
|
# Thinking picker
|
|
@@ -537,38 +531,7 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
537
531
|
new_thinking = parse_thinking_value(value)
|
|
538
532
|
if new_thinking is None:
|
|
539
533
|
return
|
|
540
|
-
|
|
541
|
-
# Build toast label
|
|
542
|
-
if value.startswith("effort:"):
|
|
543
|
-
toast_label = value[7:]
|
|
544
|
-
elif value.startswith("budget:"):
|
|
545
|
-
budget = int(value[7:])
|
|
546
|
-
toast_label = "off" if budget == 0 else f"{budget} tokens"
|
|
547
|
-
else:
|
|
548
|
-
toast_label = "updated"
|
|
549
|
-
|
|
550
534
|
await self._on_change_thinking(new_thinking)
|
|
551
|
-
self._set_toast(f"thinking: {toast_label}")
|
|
552
|
-
|
|
553
|
-
# -------------------------------------------------------------------------
|
|
554
|
-
# Toast notifications
|
|
555
|
-
# -------------------------------------------------------------------------
|
|
556
|
-
|
|
557
|
-
def _set_toast(self, message: str, *, duration_sec: float = 2.0) -> None:
|
|
558
|
-
self._toast_message = message
|
|
559
|
-
self._toast_until = time.monotonic() + duration_sec
|
|
560
|
-
with contextlib.suppress(Exception):
|
|
561
|
-
self._session.app.invalidate()
|
|
562
|
-
|
|
563
|
-
async def _clear_later() -> None:
|
|
564
|
-
await asyncio.sleep(duration_sec)
|
|
565
|
-
self._toast_message = None
|
|
566
|
-
self._toast_until = 0.0
|
|
567
|
-
with contextlib.suppress(Exception):
|
|
568
|
-
self._session.app.invalidate()
|
|
569
|
-
|
|
570
|
-
with contextlib.suppress(Exception):
|
|
571
|
-
self._session.app.create_background_task(_clear_later())
|
|
572
535
|
|
|
573
536
|
# -------------------------------------------------------------------------
|
|
574
537
|
# Bottom toolbar
|
|
@@ -588,23 +551,17 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
588
551
|
except (AttributeError, RuntimeError):
|
|
589
552
|
update_message = None
|
|
590
553
|
|
|
591
|
-
toast: str | None = None
|
|
592
|
-
now = time.monotonic()
|
|
593
|
-
if self._toast_message is not None and now < self._toast_until:
|
|
594
|
-
toast = self._toast_message
|
|
595
|
-
|
|
596
554
|
# If nothing to show, return a blank line to actively clear any previously
|
|
597
555
|
# rendered content. (When `bottom_toolbar` is a callable, prompt_toolkit
|
|
598
556
|
# will still reserve the toolbar line.)
|
|
599
|
-
if not
|
|
557
|
+
if not update_message:
|
|
600
558
|
try:
|
|
601
559
|
terminal_width = shutil.get_terminal_size().columns
|
|
602
560
|
except (OSError, ValueError):
|
|
603
561
|
terminal_width = 0
|
|
604
562
|
return FormattedText([("", " " * max(0, terminal_width))])
|
|
605
563
|
|
|
606
|
-
|
|
607
|
-
left_text = " " + " · ".join(parts)
|
|
564
|
+
left_text = " " + update_message
|
|
608
565
|
try:
|
|
609
566
|
terminal_width = shutil.get_terminal_size().columns
|
|
610
567
|
padding = " " * max(0, terminal_width - len(left_text))
|
|
@@ -667,7 +624,10 @@ class PromptToolkitInput(InputProviderABC):
|
|
|
667
624
|
with contextlib.suppress(Exception):
|
|
668
625
|
self._pre_prompt()
|
|
669
626
|
|
|
670
|
-
|
|
627
|
+
# Keep ANSI escape sequences intact while prompt_toolkit is active.
|
|
628
|
+
# This allows Rich-rendered panels (e.g. WelcomeEvent) to display with
|
|
629
|
+
# proper styling instead of showing raw escape codes.
|
|
630
|
+
with patch_stdout(raw=True):
|
|
671
631
|
line: str = await self._session.prompt_async(
|
|
672
632
|
placeholder=self._render_input_placeholder(),
|
|
673
633
|
bottom_toolbar=self._get_bottom_toolbar,
|
|
@@ -266,7 +266,11 @@ class REPLRenderer:
|
|
|
266
266
|
self.print(r_user_input.render_interrupt())
|
|
267
267
|
|
|
268
268
|
def display_error(self, event: events.ErrorEvent) -> None:
|
|
269
|
-
|
|
269
|
+
if event.session_id:
|
|
270
|
+
with self.session_print_context(event.session_id):
|
|
271
|
+
self.print(r_errors.render_error(truncate_display(event.error_message)))
|
|
272
|
+
else:
|
|
273
|
+
self.print(r_errors.render_error(truncate_display(event.error_message)))
|
|
270
274
|
|
|
271
275
|
# -------------------------------------------------------------------------
|
|
272
276
|
# Spinner control methods
|
|
@@ -167,7 +167,7 @@ def _render_fork_session_output(command_output: model.CommandOutput) -> Renderab
|
|
|
167
167
|
session_id = command_output.ui_extra.session_id
|
|
168
168
|
grid.add_column(style=ThemeKey.METADATA, overflow="fold")
|
|
169
169
|
|
|
170
|
-
grid.add_row(Text("Session forked.
|
|
170
|
+
grid.add_row(Text("Session forked. Resume command copied to clipboard:", style=ThemeKey.METADATA))
|
|
171
171
|
grid.add_row(Text(f" klaude --resume-by-id {session_id}", style=ThemeKey.METADATA_BOLD))
|
|
172
172
|
|
|
173
173
|
return Padding.indent(grid, level=2)
|
|
@@ -107,7 +107,7 @@ def render_sub_agent_result(
|
|
|
107
107
|
Group(
|
|
108
108
|
NoInsetMarkdown(head_text, code_theme=code_theme, style=style or ""),
|
|
109
109
|
Text(
|
|
110
|
-
f"\n… more {hidden_count} lines — use /export to view full output\n",
|
|
110
|
+
f"\n( … more {hidden_count} lines — use /export to view full output )\n",
|
|
111
111
|
style=ThemeKey.TOOL_RESULT_TRUNCATED,
|
|
112
112
|
),
|
|
113
113
|
NoInsetMarkdown(tail_text, code_theme=code_theme, style=style or ""),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: klaude-code
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.1
|
|
4
4
|
Summary: Minimal code agent CLI
|
|
5
5
|
Requires-Dist: anthropic>=0.66.0
|
|
6
6
|
Requires-Dist: chardet>=5.2.0
|
|
7
7
|
Requires-Dist: ddgs>=9.9.3
|
|
8
8
|
Requires-Dist: diff-match-patch>=20241021
|
|
9
|
+
Requires-Dist: google-genai>=1.56.0
|
|
9
10
|
Requires-Dist: markdown-it-py>=4.0.0
|
|
10
11
|
Requires-Dist: openai>=1.102.0
|
|
11
12
|
Requires-Dist: pillow>=12.0.0
|
|
@@ -34,6 +35,10 @@ Minimal code agent CLI.
|
|
|
34
35
|
- **Output truncation**: Large outputs saved to file system with snapshot links
|
|
35
36
|
- **Skills**: Built-in + user + project Agent Skills (with implicit invocation by Skill tool or explicit invocation by typing `$`)
|
|
36
37
|
- **Sessions**: Resumable with `--continue`
|
|
38
|
+
- **Cost tracking**: Automatic API cost calculation and display (USD/CNY)
|
|
39
|
+
- **Version update check**: Background PyPI version check with upgrade prompts
|
|
40
|
+
- **Terminal title**: Shows current directory and model name
|
|
41
|
+
- **Mermaid diagrams**: Interactive local HTML viewer with zoom, pan, and SVG export
|
|
37
42
|
- **Extras**: Slash commands, sub-agents, image paste, terminal notifications, auto-theming
|
|
38
43
|
|
|
39
44
|
## Installation
|
|
@@ -77,6 +82,7 @@ klaude [--model <name>] [--select-model]
|
|
|
77
82
|
- `--select-model`/`-s`: Open the interactive model selector at startup (shows all models unless `--model` is also provided).
|
|
78
83
|
- `--continue`/`-c`: Resume the most recent session.
|
|
79
84
|
- `--resume`/`-r`: Select a session to resume for this project.
|
|
85
|
+
- `--resume-by-id <id>`: Resume a session by its ID directly.
|
|
80
86
|
- `--vanilla`: Minimal mode with only basic tools (Bash, Read, Edit) and no system prompts.
|
|
81
87
|
|
|
82
88
|
**Model selection behavior:**
|
|
@@ -140,20 +146,62 @@ klaude config
|
|
|
140
146
|
|
|
141
147
|
##### Adding Models to Built-in Providers
|
|
142
148
|
|
|
143
|
-
You can add custom models to existing providers without redefining the entire provider
|
|
149
|
+
You can add custom models to existing built-in providers without redefining the entire provider. Just reference the `provider_name` and add your `model_list`:
|
|
144
150
|
|
|
145
151
|
```yaml
|
|
146
|
-
#
|
|
152
|
+
# ~/.klaude/klaude-config.yaml
|
|
147
153
|
provider_list:
|
|
154
|
+
- provider_name: openrouter # Reference existing built-in provider
|
|
155
|
+
model_list:
|
|
156
|
+
- model_name: seed
|
|
157
|
+
model_params:
|
|
158
|
+
model: bytedance-seed/seed-1.6 # Model ID from OpenRouter
|
|
159
|
+
context_limit: 262000
|
|
160
|
+
cost:
|
|
161
|
+
input: 0.25
|
|
162
|
+
output: 2
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**How merging works:**
|
|
166
|
+
- Your models are merged with the built-in models for that provider
|
|
167
|
+
- You only need `provider_name` and `model_list` - protocol, api_key, etc. are inherited from the built-in config
|
|
168
|
+
- To override a built-in model, use the same `model_name` (e.g., `sonnet` to customize the built-in sonnet)
|
|
169
|
+
|
|
170
|
+
**More examples:**
|
|
171
|
+
|
|
172
|
+
```yaml
|
|
173
|
+
provider_list:
|
|
174
|
+
# Add multiple models to OpenRouter
|
|
148
175
|
- provider_name: openrouter
|
|
149
176
|
model_list:
|
|
150
|
-
- model_name:
|
|
177
|
+
- model_name: qwen-coder
|
|
151
178
|
model_params:
|
|
152
|
-
model:
|
|
179
|
+
model: qwen/qwen-2.5-coder-32b-instruct
|
|
180
|
+
context_limit: 131072
|
|
181
|
+
cost:
|
|
182
|
+
input: 0.3
|
|
183
|
+
output: 0.9
|
|
184
|
+
- model_name: llama-405b
|
|
185
|
+
model_params:
|
|
186
|
+
model: meta-llama/llama-3.1-405b-instruct
|
|
187
|
+
context_limit: 131072
|
|
188
|
+
cost:
|
|
189
|
+
input: 0.8
|
|
190
|
+
output: 0.8
|
|
191
|
+
|
|
192
|
+
# Add models to Anthropic provider
|
|
193
|
+
- provider_name: anthropic
|
|
194
|
+
model_list:
|
|
195
|
+
- model_name: haiku@ant
|
|
196
|
+
model_params:
|
|
197
|
+
model: claude-3-5-haiku-20241022
|
|
153
198
|
context_limit: 200000
|
|
199
|
+
cost:
|
|
200
|
+
input: 1.0
|
|
201
|
+
output: 5.0
|
|
154
202
|
```
|
|
155
203
|
|
|
156
|
-
|
|
204
|
+
After adding models, run `klaude list` to verify they appear in the model list.
|
|
157
205
|
|
|
158
206
|
##### Overriding Provider Settings
|
|
159
207
|
|
|
@@ -224,6 +272,8 @@ provider_list:
|
|
|
224
272
|
- `openai` - OpenAI-compatible API
|
|
225
273
|
- `responses` - OpenAI Responses API (for o-series, GPT-5, Codex)
|
|
226
274
|
- `openrouter` - OpenRouter API
|
|
275
|
+
- `google` - Google Gemini API
|
|
276
|
+
- `bedrock` - AWS Bedrock (uses AWS credentials instead of api_key)
|
|
227
277
|
- `codex` - OpenAI Codex CLI (OAuth-based)
|
|
228
278
|
|
|
229
279
|
List configured providers and models:
|
|
@@ -251,12 +301,18 @@ klaude session clean-all
|
|
|
251
301
|
|
|
252
302
|
Inside the interactive session (`klaude`), use these commands to streamline your workflow:
|
|
253
303
|
|
|
254
|
-
- `/dev-doc [feature]` - Generate a comprehensive execution plan for a feature.
|
|
255
|
-
- `/export` - Export last assistant message to a temp Markdown file.
|
|
256
|
-
- `/init` - Bootstrap a new project structure or module.
|
|
257
304
|
- `/model` - Switch the active LLM during the session.
|
|
305
|
+
- `/thinking` - Configure model thinking/reasoning level.
|
|
258
306
|
- `/clear` - Clear the current conversation context.
|
|
259
|
-
- `/
|
|
307
|
+
- `/status` - Show session usage statistics (cost, tokens, model breakdown).
|
|
308
|
+
- `/resume` - Select and resume a previous session.
|
|
309
|
+
- `/fork-session` - Fork current session to a new session ID (supports interactive fork point selection).
|
|
310
|
+
- `/export` - Export last assistant message to a temp Markdown file.
|
|
311
|
+
- `/export-online` - Export and deploy session to surge.sh as a static webpage.
|
|
312
|
+
- `/debug [filters]` - Toggle debug mode and configure debug filters.
|
|
313
|
+
- `/init` - Bootstrap a new project structure or module.
|
|
314
|
+
- `/dev-doc [feature]` - Generate a comprehensive execution plan for a feature.
|
|
315
|
+
- `/terminal-setup` - Configure terminal for Shift+Enter support.
|
|
260
316
|
- `/help` - List all available commands.
|
|
261
317
|
|
|
262
318
|
|
|
@@ -267,6 +323,8 @@ Inside the interactive session (`klaude`), use these commands to streamline your
|
|
|
267
323
|
| `Enter` | Submit input |
|
|
268
324
|
| `Shift+Enter` | Insert newline (requires `/terminal-setup`) |
|
|
269
325
|
| `Ctrl+J` | Insert newline |
|
|
326
|
+
| `Ctrl+L` | Open model picker overlay |
|
|
327
|
+
| `Ctrl+T` | Open thinking level picker overlay |
|
|
270
328
|
| `Ctrl+V` | Paste image from clipboard |
|
|
271
329
|
| `Left/Right` | Move cursor (wraps across lines) |
|
|
272
330
|
| `Backspace` | Delete character or selected text |
|
|
@@ -290,4 +348,18 @@ echo "generate quicksort in python" | klaude exec --model gpt-5.1
|
|
|
290
348
|
|
|
291
349
|
# Partial/ambiguous name opens the interactive selector (filtered)
|
|
292
350
|
echo "generate quicksort in python" | klaude exec --model gpt
|
|
351
|
+
|
|
352
|
+
# Stream all events as JSON lines (for programmatic processing)
|
|
353
|
+
klaude exec "what is 2+2?" --stream-json
|
|
293
354
|
```
|
|
355
|
+
|
|
356
|
+
### Sub-Agents
|
|
357
|
+
|
|
358
|
+
The main agent can spawn specialized sub-agents for specific tasks:
|
|
359
|
+
|
|
360
|
+
| Sub-Agent | Purpose |
|
|
361
|
+
|-----------|---------|
|
|
362
|
+
| **Explore** | Fast codebase exploration - find files, search code, answer questions about the codebase |
|
|
363
|
+
| **Task** | Handle complex multi-step tasks autonomously |
|
|
364
|
+
| **WebAgent** | Search the web, fetch pages, and analyze content |
|
|
365
|
+
| **Oracle** | Advanced reasoning advisor for code reviews, architecture planning, and bug analysis |
|