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.
Files changed (34) hide show
  1. klaude_code/cli/list_model.py +55 -4
  2. klaude_code/cli/main.py +10 -0
  3. klaude_code/cli/runtime.py +2 -2
  4. klaude_code/cli/session_cmd.py +3 -2
  5. klaude_code/command/fork_session_cmd.py +7 -0
  6. klaude_code/config/assets/builtin_config.yaml +61 -2
  7. klaude_code/config/builtin_config.py +1 -0
  8. klaude_code/config/config.py +19 -0
  9. klaude_code/config/thinking.py +14 -0
  10. klaude_code/const.py +17 -2
  11. klaude_code/core/executor.py +16 -3
  12. klaude_code/core/task.py +5 -3
  13. klaude_code/core/tool/shell/command_safety.py +3 -5
  14. klaude_code/llm/anthropic/client.py +127 -114
  15. klaude_code/llm/bedrock/__init__.py +3 -0
  16. klaude_code/llm/bedrock/client.py +60 -0
  17. klaude_code/llm/google/__init__.py +3 -0
  18. klaude_code/llm/google/client.py +309 -0
  19. klaude_code/llm/google/input.py +215 -0
  20. klaude_code/llm/registry.py +10 -5
  21. klaude_code/protocol/events.py +1 -0
  22. klaude_code/protocol/llm_param.py +9 -0
  23. klaude_code/session/export.py +14 -2
  24. klaude_code/session/session.py +52 -3
  25. klaude_code/session/store.py +3 -0
  26. klaude_code/session/templates/export_session.html +210 -18
  27. klaude_code/ui/modes/repl/input_prompt_toolkit.py +6 -46
  28. klaude_code/ui/modes/repl/renderer.py +5 -1
  29. klaude_code/ui/renderers/developer.py +1 -1
  30. klaude_code/ui/renderers/sub_agent.py +1 -1
  31. {klaude_code-1.6.0.dist-info → klaude_code-1.7.1.dist-info}/METADATA +82 -10
  32. {klaude_code-1.6.0.dist-info → klaude_code-1.7.1.dist-info}/RECORD +34 -29
  33. {klaude_code-1.6.0.dist-info → klaude_code-1.7.1.dist-info}/WHEEL +0 -0
  34. {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>Klaude Code - $first_user_message</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: "TX-02", "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
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-mono);
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: 33vh;
1253
+ top: 20vh;
1147
1254
  left: 20px;
1148
1255
  width: 220px;
1149
- bottom: 33vh;
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
- fontFamily:
1312
- markdownMonoFont ||
1313
- 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace',
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 to generated code blocks
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 $${userCount}`;
1826
+ label = `USER $$$${userCount}`;
1659
1827
  idPrefix = "user";
1660
1828
  } else if (roleLabel.classList.contains("assistant")) {
1661
1829
  assistantCount++;
1662
- label = `ASSISTANT $${assistantCount}`;
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
- `$${idPrefix}-$${Math.random().toString(36).substr(2, 9)}`;
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 toast and not update_message:
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
- parts = [p for p in [toast, update_message] if p]
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
- with patch_stdout():
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
- self.print(r_errors.render_error(truncate_display(event.error_message)))
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. To continue in a new conversation:", style=ThemeKey.METADATA))
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.6.0
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
- # Just specify provider_name and your new models - no need for protocol/api_key
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: my-custom-model
177
+ - model_name: qwen-coder
151
178
  model_params:
152
- model: some-provider/some-model-id
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
- Your models are merged with built-in models. To override a built-in model, use the same `model_name`.
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
- - `/diff` - Show local git diff changes.
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 |