klaude-code 1.7.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/main.py CHANGED
@@ -95,10 +95,20 @@ def read_input_content(cli_argument: str) -> str | None:
95
95
  return content
96
96
 
97
97
 
98
+ ENV_HELP = """\
99
+ Environment Variables:
100
+
101
+ KLAUDE_READ_GLOBAL_LINE_CAP Max lines to read (default: 2000)
102
+
103
+ KLAUDE_READ_MAX_CHARS Max total chars to read (default: 50000)
104
+ """
105
+
98
106
  app = typer.Typer(
99
107
  add_completion=False,
100
108
  pretty_exceptions_enable=False,
101
109
  no_args_is_help=False,
110
+ rich_markup_mode="rich",
111
+ epilog=ENV_HELP,
102
112
  )
103
113
 
104
114
  # Register subcommands from modules
@@ -379,7 +379,7 @@ async def run_interactive(init_config: AppInitConfig, session_id: str | None = N
379
379
  model_name=model_name,
380
380
  save_as_default=False,
381
381
  defer_thinking_selection=True,
382
- emit_welcome_event=False,
382
+ emit_welcome_event=True,
383
383
  emit_switch_message=False,
384
384
  )
385
385
  )
@@ -398,7 +398,7 @@ async def run_interactive(init_config: AppInitConfig, session_id: str | None = N
398
398
  op.ChangeThinkingOperation(
399
399
  session_id=sid,
400
400
  thinking=thinking,
401
- emit_welcome_event=False,
401
+ emit_welcome_event=True,
402
402
  emit_switch_message=False,
403
403
  )
404
404
  )
@@ -7,6 +7,7 @@ from prompt_toolkit.styles import Style
7
7
 
8
8
  from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
9
9
  from klaude_code.protocol import commands, events, model
10
+ from klaude_code.ui.modes.repl.clipboard import copy_to_clipboard
10
11
  from klaude_code.ui.terminal.selector import SelectItem, select_one
11
12
 
12
13
  FORK_SELECT_STYLE = Style(
@@ -215,6 +216,9 @@ class ForkSessionCommand(CommandABC):
215
216
  new_session = agent.session.fork()
216
217
  await new_session.wait_for_flush()
217
218
 
219
+ resume_cmd = f"klaude --resume-by-id {new_session.id}"
220
+ copy_to_clipboard(resume_cmd)
221
+
218
222
  event = events.DeveloperMessageEvent(
219
223
  session_id=agent.session.id,
220
224
  item=model.DeveloperMessageItem(
@@ -247,6 +251,9 @@ class ForkSessionCommand(CommandABC):
247
251
  # Build result message
248
252
  fork_description = "entire conversation" if selected is None else f"up to message index {selected}"
249
253
 
254
+ resume_cmd = f"klaude --resume-by-id {new_session.id}"
255
+ copy_to_clipboard(resume_cmd)
256
+
250
257
  event = events.DeveloperMessageEvent(
251
258
  session_id=agent.session.id,
252
259
  item=model.DeveloperMessageItem(
@@ -87,6 +87,30 @@ provider_list:
87
87
  input: 1.75
88
88
  output: 14.0
89
89
  cache_read: 0.17
90
+ - model_name: gpt-5.2-medium
91
+ model_params:
92
+ model: openai/gpt-5.2
93
+ max_tokens: 128000
94
+ context_limit: 400000
95
+ verbosity: high
96
+ thinking:
97
+ reasoning_effort: medium
98
+ cost:
99
+ input: 1.75
100
+ output: 14.0
101
+ cache_read: 0.17
102
+ - model_name: gpt-5.2-low
103
+ model_params:
104
+ model: openai/gpt-5.2
105
+ max_tokens: 128000
106
+ context_limit: 400000
107
+ verbosity: low
108
+ thinking:
109
+ reasoning_effort: low
110
+ cost:
111
+ input: 1.75
112
+ output: 14.0
113
+ cache_read: 0.17
90
114
  - model_name: gpt-5.2-fast
91
115
  model_params:
92
116
  model: openai/gpt-5.2
@@ -257,6 +257,11 @@ def get_example_config() -> UserConfig:
257
257
  model="model-id-from-provider",
258
258
  max_tokens=16000,
259
259
  context_limit=200000,
260
+ cost=llm_param.Cost(
261
+ input=1,
262
+ output=10,
263
+ cache_read=0.1,
264
+ ),
260
265
  ),
261
266
  ),
262
267
  ],
klaude_code/const.py CHANGED
@@ -4,8 +4,21 @@ This module consolidates all magic numbers and configuration values
4
4
  that were previously scattered across the codebase.
5
5
  """
6
6
 
7
+ import os
7
8
  from pathlib import Path
8
9
 
10
+
11
+ def _get_int_env(name: str, default: int) -> int:
12
+ """Get an integer value from environment variable, or return default."""
13
+ val = os.environ.get(name)
14
+ if val is None:
15
+ return default
16
+ try:
17
+ return int(val)
18
+ except ValueError:
19
+ return default
20
+
21
+
9
22
  # =============================================================================
10
23
  # Agent Configuration
11
24
  # =============================================================================
@@ -47,10 +60,12 @@ TODO_REMINDER_TOOL_CALL_THRESHOLD = 10
47
60
  READ_CHAR_LIMIT_PER_LINE = 2000
48
61
 
49
62
  # Maximum number of lines to read from a file
50
- READ_GLOBAL_LINE_CAP = 2000
63
+ # Can be overridden via KLAUDE_READ_GLOBAL_LINE_CAP environment variable
64
+ READ_GLOBAL_LINE_CAP = _get_int_env("KLAUDE_READ_GLOBAL_LINE_CAP", 2000)
51
65
 
52
66
  # Maximum total characters to read (truncates beyond this limit)
53
- READ_MAX_CHARS = 50000
67
+ # Can be overridden via KLAUDE_READ_MAX_CHARS environment variable
68
+ READ_MAX_CHARS = _get_int_env("KLAUDE_READ_MAX_CHARS", 50000)
54
69
 
55
70
  # Maximum image file size in bytes (4MB)
56
71
  READ_MAX_IMAGE_BYTES = 4 * 1024 * 1024
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
 
10
10
  import asyncio
11
11
  import subprocess
12
+ import sys
12
13
  from collections.abc import Callable
13
14
  from dataclasses import dataclass
14
15
  from pathlib import Path
@@ -427,14 +428,26 @@ class ExecutorContext:
427
428
  return build_export_html(agent.session, system_prompt, tool_schemas, model_name)
428
429
 
429
430
  def _open_file(self, path: Path) -> None:
431
+ # Select platform-appropriate command
432
+ if sys.platform == "darwin":
433
+ cmd = "open"
434
+ elif sys.platform == "win32":
435
+ cmd = "start"
436
+ else:
437
+ cmd = "xdg-open"
438
+
430
439
  try:
431
440
  # Detach stdin to prevent interference with prompt_toolkit's terminal state
432
- subprocess.run(["open", str(path)], stdin=subprocess.DEVNULL, check=True)
441
+ if sys.platform == "win32":
442
+ # Windows 'start' requires shell=True
443
+ subprocess.run(f'start "" "{path}"', shell=True, stdin=subprocess.DEVNULL, check=True)
444
+ else:
445
+ subprocess.run([cmd, str(path)], stdin=subprocess.DEVNULL, check=True)
433
446
  except FileNotFoundError as exc: # pragma: no cover
434
- msg = "`open` command not found; please open the HTML manually."
447
+ msg = f"`{cmd}` command not found; please open the HTML manually."
435
448
  raise RuntimeError(msg) from exc
436
449
  except subprocess.CalledProcessError as exc: # pragma: no cover
437
- msg = f"Failed to open HTML with `open`: {exc}"
450
+ msg = f"Failed to open HTML with `{cmd}`: {exc}"
438
451
  raise RuntimeError(msg) from exc
439
452
 
440
453
  async def handle_interrupt(self, operation: op.InterruptOperation) -> None:
klaude_code/core/task.py CHANGED
@@ -220,7 +220,9 @@ class TaskExecutor:
220
220
  error_msg = f"Retrying {attempt + 1}/{const.MAX_FAILED_TURN_RETRIES} in {delay:.1f}s"
221
221
  if last_error_message:
222
222
  error_msg = f"{error_msg} - {last_error_message}"
223
- yield events.ErrorEvent(error_message=error_msg, can_retry=True)
223
+ yield events.ErrorEvent(
224
+ error_message=error_msg, can_retry=True, session_id=session_ctx.session_id
225
+ )
224
226
  await asyncio.sleep(delay)
225
227
  finally:
226
228
  self._current_turn = None
@@ -234,7 +236,7 @@ class TaskExecutor:
234
236
  final_error = f"Turn failed after {const.MAX_FAILED_TURN_RETRIES} retries."
235
237
  if last_error_message:
236
238
  final_error = f"{last_error_message}\n{final_error}"
237
- yield events.ErrorEvent(error_message=final_error, can_retry=False)
239
+ yield events.ErrorEvent(error_message=final_error, can_retry=False, session_id=session_ctx.session_id)
238
240
  return
239
241
 
240
242
  if turn is None or turn.task_finished:
@@ -244,7 +246,7 @@ class TaskExecutor:
244
246
  error_msg = "Sub-agent returned empty result, retrying..."
245
247
  else:
246
248
  error_msg = "Agent returned empty result, retrying..."
247
- yield events.ErrorEvent(error_message=error_msg, can_retry=True)
249
+ yield events.ErrorEvent(error_message=error_msg, can_retry=True, session_id=session_ctx.session_id)
248
250
  continue
249
251
  break
250
252
 
@@ -275,12 +275,10 @@ def _is_safe_argv(argv: list[str]) -> SafetyCheckResult:
275
275
  "tag",
276
276
  "clone",
277
277
  "worktree",
278
+ "push",
279
+ "pull",
280
+ "remote",
278
281
  }
279
- # Block remote operations
280
- blocked_git_cmds = {"push", "pull", "remote"}
281
-
282
- if sub in blocked_git_cmds:
283
- return SafetyCheckResult(False, f"git: Remote operation '{sub}' not allowed")
284
282
  if sub not in allowed_git_cmds:
285
283
  return SafetyCheckResult(False, f"git: Subcommand '{sub}' not in allow list")
286
284
  return SafetyCheckResult(True)
@@ -16,6 +16,7 @@ class EndEvent(BaseModel):
16
16
  class ErrorEvent(BaseModel):
17
17
  error_message: str
18
18
  can_retry: bool = False
19
+ session_id: str | None = None
19
20
 
20
21
 
21
22
  class TaskStartEvent(BaseModel):
@@ -308,13 +308,17 @@ def _try_render_todo_args(arguments: str, tool_name: str) -> str | None:
308
308
  return None
309
309
 
310
310
 
311
- def _render_sub_agent_result(content: str) -> str:
311
+ def _render_sub_agent_result(content: str, description: str | None = None) -> str:
312
312
  # Try to format as JSON for better readability
313
313
  try:
314
314
  parsed = json.loads(content)
315
315
  formatted = "```json\n" + json.dumps(parsed, ensure_ascii=False, indent=2) + "\n```"
316
316
  except (json.JSONDecodeError, TypeError):
317
317
  formatted = content
318
+
319
+ if description:
320
+ formatted = f"# {description}\n\n{formatted}"
321
+
318
322
  encoded = _escape_html(formatted)
319
323
  return (
320
324
  f'<div class="sub-agent-result-container">'
@@ -628,7 +632,15 @@ def _format_tool_call(tool_call: model.ToolCallItem, result: model.ToolResultIte
628
632
 
629
633
  if result.output and not should_hide_text:
630
634
  if is_sub_agent_tool(tool_call.name):
631
- items_to_render.append(_render_sub_agent_result(result.output))
635
+ description = None
636
+ try:
637
+ args = json.loads(tool_call.arguments)
638
+ if isinstance(args, dict):
639
+ typed_args = cast(dict[str, Any], args)
640
+ description = cast(str | None, typed_args.get("description"))
641
+ except (json.JSONDecodeError, TypeError):
642
+ pass
643
+ items_to_render.append(_render_sub_agent_result(result.output, description))
632
644
  else:
633
645
  items_to_render.append(_render_text_block(result.output))
634
646
 
@@ -62,6 +62,7 @@ class Session(BaseModel):
62
62
  need_todo_not_used_cooldown_counter: int = Field(exclude=True, default=0)
63
63
 
64
64
  _messages_count_cache: int | None = PrivateAttr(default=None)
65
+ _user_messages_cache: list[str] | None = PrivateAttr(default=None)
65
66
  _store: JsonlSessionStore = PrivateAttr(default_factory=get_default_store)
66
67
 
67
68
  @property
@@ -78,6 +79,20 @@ class Session(BaseModel):
78
79
  def _invalidate_messages_count_cache(self) -> None:
79
80
  self._messages_count_cache = None
80
81
 
82
+ @property
83
+ def user_messages(self) -> list[str]:
84
+ """All user message contents in this session.
85
+
86
+ This is used for session selection UI and search, and is also persisted
87
+ in meta.json to avoid scanning events.jsonl for every session.
88
+ """
89
+
90
+ if self._user_messages_cache is None:
91
+ self._user_messages_cache = [
92
+ it.content for it in self.conversation_history if isinstance(it, model.UserMessageItem) and it.content
93
+ ]
94
+ return self._user_messages_cache
95
+
81
96
  @staticmethod
82
97
  def _project_key() -> str:
83
98
  return _project_key_from_cwd()
@@ -178,6 +193,18 @@ class Session(BaseModel):
178
193
  self.conversation_history.extend(items)
179
194
  self._invalidate_messages_count_cache()
180
195
 
196
+ new_user_messages = [
197
+ it.content for it in items if isinstance(it, model.UserMessageItem) and it.content
198
+ ]
199
+ if new_user_messages:
200
+ if self._user_messages_cache is None:
201
+ # Build from full history once to ensure correctness when resuming older sessions.
202
+ self._user_messages_cache = [
203
+ it.content for it in self.conversation_history if isinstance(it, model.UserMessageItem) and it.content
204
+ ]
205
+ else:
206
+ self._user_messages_cache.extend(new_user_messages)
207
+
181
208
  if self.created_at <= 0:
182
209
  self.created_at = time.time()
183
210
  self.updated_at = time.time()
@@ -188,6 +215,7 @@ class Session(BaseModel):
188
215
  sub_agent_state=self.sub_agent_state,
189
216
  file_tracker=self.file_tracker,
190
217
  todos=list(self.todos),
218
+ user_messages=self.user_messages,
191
219
  created_at=self.created_at,
192
220
  updated_at=self.updated_at,
193
221
  messages_count=self.messages_count,
@@ -311,7 +339,7 @@ class Session(BaseModel):
311
339
  case model.DeveloperMessageItem() as dm:
312
340
  yield events.DeveloperMessageEvent(session_id=self.id, item=dm)
313
341
  case model.StreamErrorItem() as se:
314
- yield events.ErrorEvent(error_message=se.error, can_retry=False)
342
+ yield events.ErrorEvent(error_message=se.error, can_retry=False, session_id=self.id)
315
343
  case _:
316
344
  continue
317
345
  prev_item = it
@@ -378,6 +406,17 @@ class Session(BaseModel):
378
406
  pass
379
407
  return messages
380
408
 
409
+ def _maybe_backfill_user_messages(*, meta_path: Path, meta: dict[str, Any], user_messages: list[str]) -> None:
410
+ if isinstance(meta.get("user_messages"), list):
411
+ return
412
+ meta["user_messages"] = user_messages
413
+ try:
414
+ tmp_path = meta_path.with_suffix(".json.tmp")
415
+ tmp_path.write_text(json.dumps(meta, ensure_ascii=False, indent=2), encoding="utf-8")
416
+ tmp_path.replace(meta_path)
417
+ except OSError:
418
+ return
419
+
381
420
  items: list[Session.SessionMetaBrief] = []
382
421
  for meta_path in store.iter_meta_files():
383
422
  data = _read_json_dict(meta_path)
@@ -390,7 +429,15 @@ class Session(BaseModel):
390
429
  created = float(data.get("created_at", meta_path.stat().st_mtime))
391
430
  updated = float(data.get("updated_at", meta_path.stat().st_mtime))
392
431
  work_dir = str(data.get("work_dir", ""))
393
- user_messages = _get_user_messages(sid)
432
+
433
+ user_messages_raw = data.get("user_messages")
434
+ if isinstance(user_messages_raw, list) and all(
435
+ isinstance(m, str) for m in cast(list[object], user_messages_raw)
436
+ ):
437
+ user_messages = cast(list[str], user_messages_raw)
438
+ else:
439
+ user_messages = _get_user_messages(sid)
440
+ _maybe_backfill_user_messages(meta_path=meta_path, meta=data, user_messages=user_messages)
394
441
  messages_count = int(data.get("messages_count", -1))
395
442
  model_name = data.get("model_name") if isinstance(data.get("model_name"), str) else None
396
443
 
@@ -193,6 +193,7 @@ def build_meta_snapshot(
193
193
  sub_agent_state: model.SubAgentState | None,
194
194
  file_tracker: dict[str, model.FileStatus],
195
195
  todos: list[model.TodoItem],
196
+ user_messages: list[str],
196
197
  created_at: float,
197
198
  updated_at: float,
198
199
  messages_count: int,
@@ -206,6 +207,8 @@ def build_meta_snapshot(
206
207
  "sub_agent_state": sub_agent_state.model_dump(mode="json") if sub_agent_state else None,
207
208
  "file_tracker": {path: status.model_dump(mode="json") for path, status in file_tracker.items()},
208
209
  "todos": [todo.model_dump(mode="json", exclude_defaults=True) for todo in todos],
210
+ # Cache user messages to avoid scanning events.jsonl during session listing.
211
+ "user_messages": list(user_messages),
209
212
  "created_at": created_at,
210
213
  "updated_at": updated_at,
211
214
  "messages_count": messages_count,
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: klaude-code
3
- Version: 1.7.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
@@ -146,20 +146,62 @@ klaude config
146
146
 
147
147
  ##### Adding Models to Built-in Providers
148
148
 
149
- 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`:
150
150
 
151
151
  ```yaml
152
- # Just specify provider_name and your new models - no need for protocol/api_key
152
+ # ~/.klaude/klaude-config.yaml
153
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
154
175
  - provider_name: openrouter
155
176
  model_list:
156
- - model_name: my-custom-model
177
+ - model_name: qwen-coder
178
+ model_params:
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
157
196
  model_params:
158
- model: some-provider/some-model-id
197
+ model: claude-3-5-haiku-20241022
159
198
  context_limit: 200000
199
+ cost:
200
+ input: 1.0
201
+ output: 5.0
160
202
  ```
161
203
 
162
- 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.
163
205
 
164
206
  ##### Overriding Provider Settings
165
207
 
@@ -230,6 +272,8 @@ provider_list:
230
272
  - `openai` - OpenAI-compatible API
231
273
  - `responses` - OpenAI Responses API (for o-series, GPT-5, Codex)
232
274
  - `openrouter` - OpenRouter API
275
+ - `google` - Google Gemini API
276
+ - `bedrock` - AWS Bedrock (uses AWS credentials instead of api_key)
233
277
  - `codex` - OpenAI Codex CLI (OAuth-based)
234
278
 
235
279
  List configured providers and models:
@@ -10,8 +10,8 @@ klaude_code/cli/auth_cmd.py,sha256=UWMHjn9xZp2o8OZc-x8y9MnkZgRWOkFXk05iKJYcySE,2
10
10
  klaude_code/cli/config_cmd.py,sha256=hlvslLNgdRHkokq1Pnam0XOdR3jqO3K0vNLqtWnPa6Q,3261
11
11
  klaude_code/cli/debug.py,sha256=cPQ7cgATcJTyBIboleW_Q4Pa_t-tGG6x-Hj3woeeuHE,2669
12
12
  klaude_code/cli/list_model.py,sha256=3SLURZXH_WgX-vGWIt52NuRm2D14-jcONtiS5GDM2xA,11248
13
- klaude_code/cli/main.py,sha256=uNZl0RjeLRITbfHerma4_kq2f0hF166dFZqAHLBu580,13236
14
- klaude_code/cli/runtime.py,sha256=6CtsQa8UcC9ppnNm2AvsF3yxgncyEYwpIIX0bb-3NN0,19826
13
+ klaude_code/cli/main.py,sha256=2hd0peqkzMrbZzfXwThx432iR559k96iS_uTKdcVcgA,13469
14
+ klaude_code/cli/runtime.py,sha256=muuuqWPDdZPNFwq_Eb08MFbnaCzUGZCDFiC-dMvpzCY,19824
15
15
  klaude_code/cli/self_update.py,sha256=iGuj0i869Zi0M70W52-VVLxZp90ISr30fQpZkHGMK2o,8059
16
16
  klaude_code/cli/session_cmd.py,sha256=9C30dzXCbPobminqenCjYvEuzZBS5zWXg3JpuhxT_OQ,3199
17
17
  klaude_code/command/__init__.py,sha256=IK2jz2SFMLVIcVzD5evKk3zWv6u1CjgCgfJXzWdvDlk,3470
@@ -20,7 +20,7 @@ klaude_code/command/command_abc.py,sha256=wZl_azY6Dpd4OvjtkSEPI3ilXaygLIVkO7NCgN
20
20
  klaude_code/command/debug_cmd.py,sha256=9sBIAwHz28QoI-tHsU3ksQlDObF1ilIbtAAEAVMR0v0,2734
21
21
  klaude_code/command/export_cmd.py,sha256=Cs7YXWtos-ZfN9OEppIl8Xrb017kDG7R6hGiilqt2bM,1623
22
22
  klaude_code/command/export_online_cmd.py,sha256=RYYLnkLtg6edsgysmhsfTw16ncFRIT6PqeTdWhWXLHE,6094
23
- klaude_code/command/fork_session_cmd.py,sha256=ocVg1YZw99RWmoCu67L6zOyIzz49CgTQRA36NFjgt-k,9672
23
+ klaude_code/command/fork_session_cmd.py,sha256=_8QXiFVC3tMjlW3F5jUjhCWQ0CkhWfKJnlNFujjbVHs,9950
24
24
  klaude_code/command/help_cmd.py,sha256=wtmOoi4DVaMJPCXLlNKJ4s-kNycNKuYk0MZkZijXLcQ,1666
25
25
  klaude_code/command/model_cmd.py,sha256=h3jUi9YOhT9rN87yfCxxU-yN3UiUzwI7Xf2UsRjjP5I,2956
26
26
  klaude_code/command/model_select.py,sha256=_TquYw8zDQHkEaRCqOCIcD2XWt8Jg-3WfGhHFSsjFw0,3189
@@ -36,15 +36,15 @@ klaude_code/command/terminal_setup_cmd.py,sha256=SivM1gX_anGY_8DCQNFZ5VblFqt4sVg
36
36
  klaude_code/command/thinking_cmd.py,sha256=NPejWmx6HDxoWzAJVLEENCr3Wi6sQSbT8A8LRh1-2Nk,3059
37
37
  klaude_code/config/__init__.py,sha256=Qe1BeMekBfO2-Zd30x33lB70hdM1QQZGrp4DbWSQ-II,353
38
38
  klaude_code/config/assets/__init__.py,sha256=uMUfmXT3I-gYiI-HVr1DrE60mx5cY1o8V7SYuGqOmvY,32
39
- klaude_code/config/assets/builtin_config.yaml,sha256=9kQZOEd5PmNZhhQWUnrCENTfHNTBhRIJUfZ444X3rmY,6491
39
+ klaude_code/config/assets/builtin_config.yaml,sha256=m-jWYrfe-cv5NBZ8lrsKTnQWNj7-MiGjDkvrEl7mg0w,7042
40
40
  klaude_code/config/builtin_config.py,sha256=LkHr7Ml-6ir6rObn9hUj5-wa-fgfJsc4T2_NdRa1ax0,1135
41
- klaude_code/config/config.py,sha256=Nxnwcu8SvOweX6YC6ueVgEEdClLAij1G1v6yyFeiXEc,17114
41
+ klaude_code/config/config.py,sha256=4CRkpR0ect1as0j4MK6F1Rd2uLX-ZV1orzQ-DIfB9Z8,17326
42
42
  klaude_code/config/select_model.py,sha256=PPbQ-BAJkwXPINBcCSPAlZjiXm4rEtg2y0hPnZE8Bnc,5183
43
43
  klaude_code/config/thinking.py,sha256=X-vywa36ggO_2z4iVhss1mAVEPAwAbcj1s68F0-B0G4,9223
44
- klaude_code/const.py,sha256=Xc6UKku2sGQE05mvPNCpBbKK205vJrS9CaNAeKvu1AA,4612
44
+ klaude_code/const.py,sha256=49ic3m4NHUtmA2xZjOSkYxy0KxFoADVAaMMtH6E5C0g,5129
45
45
  klaude_code/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
46
  klaude_code/core/agent.py,sha256=bWm-UFX_0-KAy5j_YHH8X8o3MJT4-40Ni2EaDP2SL5k,5819
47
- klaude_code/core/executor.py,sha256=odu92fhdIuVbRoK8bmqYUEz8fPX-BHeXSBcnrN-U-10,27745
47
+ klaude_code/core/executor.py,sha256=oIDE0O_kxtudSjfQcN3o0AWinkz3ddAEgqj84tYh7yY,28191
48
48
  klaude_code/core/manager/__init__.py,sha256=hdIbpnYj6i18byiWjtJIm5l7NYYDQMvafw8fePVPydc,562
49
49
  klaude_code/core/manager/llm_clients.py,sha256=X2oMFWgJcP0tK8GEtMMDYR3HyR6_H8FuyCqpzWF5x2k,871
50
50
  klaude_code/core/manager/llm_clients_builder.py,sha256=vvNV6hSpWtUCMZORQN6OfFWyA_3OAOGS5SFGy6oaKMs,1641
@@ -61,7 +61,7 @@ klaude_code/core/prompts/prompt-sub-agent-oracle.md,sha256=1PLI3snvxnenCOPVrL0Ix
61
61
  klaude_code/core/prompts/prompt-sub-agent-web.md,sha256=ewS7-h8_u4QZftFpqrZWpht9Ap08s7zF9D4k4md8oD8,2360
62
62
  klaude_code/core/prompts/prompt-sub-agent.md,sha256=dmmdsOenbAOfqG6FmdR88spOLZkXmntDBs-cmZ9DN_g,897
63
63
  klaude_code/core/reminders.py,sha256=M_YPlOuZ2TpTjoxfEp1FbswB4yuk9_XUgSGb9MoMBCA,24741
64
- klaude_code/core/task.py,sha256=V0z-QSDSxB4d4mcqXl6z_KydG_yT0JhD7274AQbNGKM,11858
64
+ klaude_code/core/task.py,sha256=hVplXxo9RvMQgCBJSMH7vhftYp29ZzAhmhSJK0OjWOw,12017
65
65
  klaude_code/core/tool/__init__.py,sha256=Y1r-xka9Zk5e5SrB0kcweFp4LyH0aafQ-BDiwYCAqFY,1992
66
66
  klaude_code/core/tool/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
67
  klaude_code/core/tool/file/_utils.py,sha256=OG4BE9WyJqzH8ilVCL3D9yvAcHk-r-L9snd-E8gO_io,967
@@ -81,7 +81,7 @@ klaude_code/core/tool/report_back_tool.py,sha256=KRZzQAIxniwXe58SDJcfK_DCf9TFFAx
81
81
  klaude_code/core/tool/shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
82
  klaude_code/core/tool/shell/bash_tool.md,sha256=ILKpnRCBTkU2uSDEdZQjNYo1l6hsM4TO-3RD5zWC61c,3935
83
83
  klaude_code/core/tool/shell/bash_tool.py,sha256=H3DlUZUdtmFYwI9wlkJpv8daGeeqP7GhOjQIKriGoog,14759
84
- klaude_code/core/tool/shell/command_safety.py,sha256=bGsooLovuzq8WmLcZ2v24AVBDj3bZv2p4GSL0IlixvM,13192
84
+ klaude_code/core/tool/shell/command_safety.py,sha256=Xlyn8QvjaA_krPNScpcTQwP3byoXxwoX_jc97AWvr6Q,13039
85
85
  klaude_code/core/tool/skill/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
86
  klaude_code/core/tool/skill/skill_tool.md,sha256=UfjJK5EGAd3mf7ym5rIrRdPyV3kBTxNnwzOjNnHOBrE,973
87
87
  klaude_code/core/tool/skill/skill_tool.py,sha256=QLcweYEfO-ncv2tdPSqANZrq2U651AG4RjGpFKeAweY,2880
@@ -134,7 +134,7 @@ klaude_code/llm/responses/input.py,sha256=qr61LmQJdcb_f-ofrAz06WpK_k4PEcI36XsyuZ
134
134
  klaude_code/llm/usage.py,sha256=ohQ6EBsWXZj6B4aJ4lDPqfhXRyd0LUAM1nXEJ_elD7A,4207
135
135
  klaude_code/protocol/__init__.py,sha256=aGUgzhYqvhuT3Mk2vj7lrHGriH4h9TSbqV1RsRFAZjQ,194
136
136
  klaude_code/protocol/commands.py,sha256=4tFt98CD_KvS9C-XEaHLN-S-QFsbDxQb_kGKnPkQlrk,958
137
- klaude_code/protocol/events.py,sha256=KUMf1rLNdHQO9cZiQ9Pa1VsKkP1PTMbUkp18bu_jGy8,3935
137
+ klaude_code/protocol/events.py,sha256=LlrxsHdWitcIg5tLrS2KbSd5Jv1TH-XlhF8e1xprYAo,3969
138
138
  klaude_code/protocol/llm_param.py,sha256=O5sn3-KJnhS_0FOxDDvAFJ2Sx7ZYdJ74ugnwu-gHZG8,4538
139
139
  klaude_code/protocol/model.py,sha256=zz1DeSkpUWDT-OZBlypaGWA4z78TSeefA-Tj8mJMHp4,14257
140
140
  klaude_code/protocol/op.py,sha256=--RllgP6Upacb6cqHd26RSwrvqZg4w6GhcebmV8gKJo,5763
@@ -147,11 +147,11 @@ klaude_code/protocol/sub_agent/web.py,sha256=Z5vUM367kz8CIexN6UVPG4XxzVOaaRek-Ga
147
147
  klaude_code/protocol/tools.py,sha256=ejhMCBBMz1ODbPEiynhzjB-aLbIRKL-wipPFv-nEz4g,373
148
148
  klaude_code/session/__init__.py,sha256=4sw81uQvEd3YUOOjamKk1KqGmxeb4Ic9T1Tee5zztyU,241
149
149
  klaude_code/session/codec.py,sha256=ummbqT7t6uHHXtaS9lOkyhi1h0YpMk7SNSms8DyGAHU,2015
150
- klaude_code/session/export.py,sha256=dj-IRUNtXL8uONDj9bsEXcEHKyeHY7lIcXv80yP88h4,31022
150
+ klaude_code/session/export.py,sha256=30FZrkEDDjccMgXlAwsBMlCXuPDS9531__SCX_nk29M,31538
151
151
  klaude_code/session/selector.py,sha256=FpKpGs06fM-LdV-yVUqEY-FJsFn2OtGK-0paXjsZVTg,2770
152
- klaude_code/session/session.py,sha256=VvGMxu5gwFasLlaT9h5x1gBFpuIfXDZJKC1qNwKU8tQ,17342
153
- klaude_code/session/store.py,sha256=-e-lInCB3N1nFLlet7bipkmPk1PXmGthuMxv5z3hg5o,6953
154
- klaude_code/session/templates/export_session.html,sha256=bA27AkcC7DQRoWmcMBeaR8WOx1z76hezEDf0aYH-0HQ,119780
152
+ klaude_code/session/session.py,sha256=kepapzAcidzOi9Tl7WjisbGgQzkkmL7HlMeGNVYUKU0,19546
153
+ klaude_code/session/store.py,sha256=DOf5R3pxQmmexfWIjCwZ27SBVsk7b_qIN7deXghCy90,7114
154
+ klaude_code/session/templates/export_session.html,sha256=hGmlGFb7a2Iu4mkev_Q2-GywIQ9lEXuDlh83dDs4bgs,125862
155
155
  klaude_code/session/templates/mermaid_viewer.html,sha256=lOkETxlctX1C1WJtS1wFw6KhNQmemxwJZFpXDSjlMOk,27842
156
156
  klaude_code/skill/__init__.py,sha256=yeWeCfRGPOhT4mx_pjdo4fLondQ_Vx0edBtnFusLhls,839
157
157
  klaude_code/skill/assets/deslop/SKILL.md,sha256=XMBER6gOyYnZof_u7l30CZSzmDcINe8XP-n_loah0EQ,873
@@ -179,19 +179,19 @@ klaude_code/ui/modes/repl/clipboard.py,sha256=ZCpk7kRSXGhh0Q_BWtUUuSYT7ZOqRjAoRc
179
179
  klaude_code/ui/modes/repl/completers.py,sha256=zH5zslovTKJwH1Gu8ZufvMDGkSd342F6fHE1hjlxHgM,31849
180
180
  klaude_code/ui/modes/repl/display.py,sha256=06wawOHWO2ItEA9EIEh97p3GDID7TJhAtpaA03nPQXs,2335
181
181
  klaude_code/ui/modes/repl/event_handler.py,sha256=O8yDr4xNMAqgXEiT90KWBoQX-2pIPjVf591QJ0ftjIo,25482
182
- klaude_code/ui/modes/repl/input_prompt_toolkit.py,sha256=40PfnhMCeOHEkb8MQFpyA4qIkWhYmhjosb3NAeFxyqM,28434
182
+ klaude_code/ui/modes/repl/input_prompt_toolkit.py,sha256=QZBBAUK7Bkq1TBwtvBnJ0bGYtGH5HWvpWcb1bppGnl0,27072
183
183
  klaude_code/ui/modes/repl/key_bindings.py,sha256=tZV0ILMWpHCPcVFpf9bnbTSXgnnqsW0-6cCMMVTRciA,13023
184
- klaude_code/ui/modes/repl/renderer.py,sha256=kdJKRGMGEQFskHeibkI-heoFZP6ucHOK_x7brXPhNCI,15912
184
+ klaude_code/ui/modes/repl/renderer.py,sha256=ljsgu4bLLnGOeCpbWbQPdJnMI7SP_K5WD88yOhZLGjU,16111
185
185
  klaude_code/ui/renderers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
186
186
  klaude_code/ui/renderers/assistant.py,sha256=7iu5zlHR7JGviHs2eA25Dsbd7ZkzCR2_0XzkqMPVxDI,862
187
187
  klaude_code/ui/renderers/bash_syntax.py,sha256=VcX_tuojOtS58s_Ff-Zmhw_6LBRn2wsvR5UBtEr_qQU,5923
188
188
  klaude_code/ui/renderers/common.py,sha256=l9V7yuiejowyw3FdZ2n3VJ2OO_K1rEUINmFz-mC2xlw,2648
189
- klaude_code/ui/renderers/developer.py,sha256=JB8NZ6blur8U6Gn4uUMg6dOTmaMNvTcxxaOk3P9toHo,8163
189
+ klaude_code/ui/renderers/developer.py,sha256=GI6MreYDvLHNa40KlhOR0PQoR_ePOLtlYhv3pAPKHp8,8164
190
190
  klaude_code/ui/renderers/diffs.py,sha256=uLpgYTudH38wucozoUw4xbPWMC6uYTQTaDTHcg-0zvM,10418
191
191
  klaude_code/ui/renderers/errors.py,sha256=MavmYOQ7lyjA_VpuUpDVFCuY9W7XrMVdLsg2lCOn4GY,655
192
192
  klaude_code/ui/renderers/mermaid_viewer.py,sha256=TIUFLtTqdG-iFD4Mgm8OdzU_9UO14niftTJ11f4makc,1691
193
193
  klaude_code/ui/renderers/metadata.py,sha256=EWxh5UTSZG_vRVf6taKI_E1YkR_56U1Gs9soDuZcpq4,8576
194
- klaude_code/ui/renderers/sub_agent.py,sha256=g8QCFXTtFX_w8oTaGMYGuy6u5KqbFMlvzWofER0hGKk,5946
194
+ klaude_code/ui/renderers/sub_agent.py,sha256=zG4T7hKEb7G-kD_Mgc7lmzwbukvdNLG7Y7a9K1WlHGA,5950
195
195
  klaude_code/ui/renderers/thinking.py,sha256=TbQxkjR6MuDXzASBK_rMaxxqvSdhfwDtVwXhOExuvlM,1946
196
196
  klaude_code/ui/renderers/tools.py,sha256=lebQHccj2tkJIjO-JB0TvCIixx-BKXHfD-egXSxBV7Y,27891
197
197
  klaude_code/ui/renderers/user_input.py,sha256=OPHVOZsCefIRVpgl6WMBFusc3Vwyr1fk5ilFaQ64AqU,4500
@@ -212,7 +212,7 @@ klaude_code/ui/terminal/progress_bar.py,sha256=MDnhPbqCnN4GDgLOlxxOEVZPDwVC_XL2N
212
212
  klaude_code/ui/terminal/selector.py,sha256=NblhWxUp0AW2OyepG4DHNy4yKE947Oi0OiqlafvBCEE,22144
213
213
  klaude_code/ui/utils/__init__.py,sha256=YEsCLjbCPaPza-UXTPUMTJTrc9BmNBUP5CbFWlshyOQ,15
214
214
  klaude_code/ui/utils/common.py,sha256=tqHqwgLtAyP805kwRFyoAL4EgMutcNb3Y-GAXJ4IeuM,2263
215
- klaude_code-1.7.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
216
- klaude_code-1.7.0.dist-info/entry_points.txt,sha256=kkXIXedaTOtjXPr2rVjRVVXZYlFUcBHELaqmyVlWUFA,92
217
- klaude_code-1.7.0.dist-info/METADATA,sha256=AscMVASGUlpBW2pN_633mhuS6Tz9ExAdGZpC97m_6WM,10675
218
- klaude_code-1.7.0.dist-info/RECORD,,
215
+ klaude_code-1.7.1.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
216
+ klaude_code-1.7.1.dist-info/entry_points.txt,sha256=kkXIXedaTOtjXPr2rVjRVVXZYlFUcBHELaqmyVlWUFA,92
217
+ klaude_code-1.7.1.dist-info/METADATA,sha256=mzpnDrtduulkJRur3kZutGAMR58k3hYHvgZ_JskQbH8,12006
218
+ klaude_code-1.7.1.dist-info/RECORD,,