klaude-code 1.2.20__py3-none-any.whl → 1.2.22__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 (42) hide show
  1. klaude_code/cli/debug.py +8 -10
  2. klaude_code/cli/main.py +23 -0
  3. klaude_code/cli/runtime.py +13 -1
  4. klaude_code/command/__init__.py +0 -3
  5. klaude_code/command/prompt-deslop.md +1 -1
  6. klaude_code/command/thinking_cmd.py +10 -0
  7. klaude_code/const/__init__.py +2 -5
  8. klaude_code/core/prompt.py +5 -2
  9. klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +117 -0
  10. klaude_code/core/prompts/{prompt-codex-gpt-5-1.md → prompt-codex.md} +9 -42
  11. klaude_code/core/reminders.py +36 -2
  12. klaude_code/core/tool/__init__.py +0 -5
  13. klaude_code/core/tool/file/_utils.py +6 -0
  14. klaude_code/core/tool/file/apply_patch_tool.py +30 -72
  15. klaude_code/core/tool/file/diff_builder.py +151 -0
  16. klaude_code/core/tool/file/edit_tool.py +35 -18
  17. klaude_code/core/tool/file/read_tool.py +45 -86
  18. klaude_code/core/tool/file/write_tool.py +40 -30
  19. klaude_code/core/tool/shell/bash_tool.py +151 -3
  20. klaude_code/core/tool/web/mermaid_tool.md +26 -0
  21. klaude_code/protocol/commands.py +0 -1
  22. klaude_code/protocol/model.py +29 -10
  23. klaude_code/protocol/tools.py +1 -2
  24. klaude_code/session/export.py +75 -20
  25. klaude_code/session/session.py +7 -0
  26. klaude_code/session/templates/export_session.html +28 -0
  27. klaude_code/ui/renderers/common.py +26 -11
  28. klaude_code/ui/renderers/developer.py +0 -5
  29. klaude_code/ui/renderers/diffs.py +84 -0
  30. klaude_code/ui/renderers/tools.py +19 -98
  31. klaude_code/ui/rich/markdown.py +11 -1
  32. klaude_code/ui/rich/status.py +8 -11
  33. klaude_code/ui/rich/theme.py +14 -4
  34. {klaude_code-1.2.20.dist-info → klaude_code-1.2.22.dist-info}/METADATA +2 -1
  35. {klaude_code-1.2.20.dist-info → klaude_code-1.2.22.dist-info}/RECORD +37 -40
  36. klaude_code/command/diff_cmd.py +0 -136
  37. klaude_code/core/tool/file/multi_edit_tool.md +0 -42
  38. klaude_code/core/tool/file/multi_edit_tool.py +0 -175
  39. klaude_code/core/tool/memory/memory_tool.md +0 -20
  40. klaude_code/core/tool/memory/memory_tool.py +0 -456
  41. {klaude_code-1.2.20.dist-info → klaude_code-1.2.22.dist-info}/WHEEL +0 -0
  42. {klaude_code-1.2.20.dist-info → klaude_code-1.2.22.dist-info}/entry_points.txt +0 -0
@@ -203,24 +203,21 @@ class ShimmerStatusText:
203
203
  yield table
204
204
 
205
205
  def _render_left_text(self, console: Console) -> Text:
206
- """Render the left part with shimmer effect."""
206
+ """Render the left part with shimmer effect on main text only."""
207
207
  result = Text()
208
208
  main_style = console.get_style(str(self._main_style))
209
209
  hint_style = console.get_style(str(self._hint_style))
210
210
 
211
- combined_text = self._main_text.plain + self._hint_text.plain
212
- split_index = len(self._main_text.plain)
213
-
214
- for index, (ch, intensity) in enumerate(_shimmer_profile(combined_text)):
215
- if index < split_index:
216
- # Get style from main_text, merge with main_style
217
- char_style = self._main_text.get_style_at_offset(console, index)
218
- base_style = main_style + char_style
219
- else:
220
- base_style = hint_style
211
+ # Apply shimmer only to main text
212
+ for index, (ch, intensity) in enumerate(_shimmer_profile(self._main_text.plain)):
213
+ char_style = self._main_text.get_style_at_offset(console, index)
214
+ base_style = main_style + char_style
221
215
  style = _shimmer_style(console, base_style, intensity)
222
216
  result.append(ch, style=style)
223
217
 
218
+ # Append hint text without shimmer
219
+ result.append(self._hint_text.plain, style=hint_style)
220
+
224
221
  return result
225
222
 
226
223
 
@@ -21,7 +21,9 @@ class Palette:
21
21
  purple: str
22
22
  lavender: str
23
23
  diff_add: str
24
+ diff_add_char: str
24
25
  diff_remove: str
26
+ diff_remove_char: str
25
27
  code_theme: str
26
28
  text_background: str
27
29
 
@@ -40,8 +42,10 @@ LIGHT_PALETTE = Palette(
40
42
  grey_green="#96a096",
41
43
  purple="#5f5fd7",
42
44
  lavender="#5f87af",
43
- diff_add="#2e5a32 on #e8f5e9",
44
- diff_remove="#5a2e32 on #ffebee",
45
+ diff_add="#2e5a32 on #dafbe1",
46
+ diff_add_char="#2e5a32 on #aceebb",
47
+ diff_remove="#5a2e32 on #ffebe9",
48
+ diff_remove_char="#5a2e32 on #ffc1bb",
45
49
  code_theme="ansi_light",
46
50
  text_background="#e0e0e0",
47
51
  )
@@ -60,8 +64,10 @@ DARK_PALETTE = Palette(
60
64
  grey_green="#6d8672",
61
65
  purple="#afbafe",
62
66
  lavender="#9898b8",
63
- diff_add="#c8e6c9 on #2e4a32",
64
- diff_remove="#ffcdd2 on #4a2e32",
67
+ diff_add="#c8e6c9 on #1b3928",
68
+ diff_add_char="#c8e6c9 on #264f35",
69
+ diff_remove="#ffcdd2 on #3d1f23",
70
+ diff_remove_char="#ffcdd2 on #5c2d33",
65
71
  code_theme="ansi_dark",
66
72
  text_background="#2f3440",
67
73
  )
@@ -73,6 +79,8 @@ class ThemeKey(str, Enum):
73
79
  DIFF_FILE_NAME = "diff.file_name"
74
80
  DIFF_REMOVE = "diff.remove"
75
81
  DIFF_ADD = "diff.add"
82
+ DIFF_ADD_CHAR = "diff.add.char"
83
+ DIFF_REMOVE_CHAR = "diff.remove.char"
76
84
  DIFF_STATS_ADD = "diff.stats.add"
77
85
  DIFF_STATS_REMOVE = "diff.stats.remove"
78
86
  # ERROR
@@ -165,6 +173,8 @@ def get_theme(theme: str | None = None) -> Themes:
165
173
  ThemeKey.DIFF_FILE_NAME.value: palette.blue,
166
174
  ThemeKey.DIFF_REMOVE.value: palette.diff_remove,
167
175
  ThemeKey.DIFF_ADD.value: palette.diff_add,
176
+ ThemeKey.DIFF_ADD_CHAR.value: palette.diff_add_char,
177
+ ThemeKey.DIFF_REMOVE_CHAR.value: palette.diff_remove_char,
168
178
  ThemeKey.DIFF_STATS_ADD.value: palette.green,
169
179
  ThemeKey.DIFF_STATS_REMOVE.value: palette.red,
170
180
  # ERROR
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: klaude-code
3
- Version: 1.2.20
3
+ Version: 1.2.22
4
4
  Summary: Add your description here
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
+ Requires-Dist: diff-match-patch>=20241021
8
9
  Requires-Dist: openai>=1.102.0
9
10
  Requires-Dist: pillow>=12.0.0
10
11
  Requires-Dist: prompt-toolkit>=3.0.52
@@ -8,22 +8,21 @@ klaude_code/auth/codex/token_manager.py,sha256=F4xH5PhE9cksqTiWsU_YAboNe284VgUFW
8
8
  klaude_code/cli/__init__.py,sha256=YzlAoWAr5rx5oe6B_4zPxRFS4QaZauuy1AFwampP5fg,45
9
9
  klaude_code/cli/auth_cmd.py,sha256=UWMHjn9xZp2o8OZc-x8y9MnkZgRWOkFXk05iKJYcySE,2561
10
10
  klaude_code/cli/config_cmd.py,sha256=SBFmBnHvkf5IJtpsDDuHsHWQCmYd2i4PtIMBOKpxmOM,2894
11
- klaude_code/cli/debug.py,sha256=vohQVqy6fB59p4NYoiQb8BiLcl5YiGvugDXc2hYGFTc,2417
11
+ klaude_code/cli/debug.py,sha256=vizBXc3648vBZQonreMqvv_b5UdRgcQoOIT-iEIx1G4,2318
12
12
  klaude_code/cli/list_model.py,sha256=9YOxhWE0J59NaY-SrgPA9_jA1A8rlOGwWmzK0TRuos4,8011
13
- klaude_code/cli/main.py,sha256=K6VkJOmxVPc0jGNJPG3wumupUCx69DSpLmvKlANw98s,9485
14
- klaude_code/cli/runtime.py,sha256=PqYK48EbK4d5Hm8P7SjtMR5p2UQDfPpsyEiNT8q4Zvc,13650
13
+ klaude_code/cli/main.py,sha256=pyU2W2X3lg7Z-4adiOzA9_2l-5QSejYm68HrhAiu470,10469
14
+ klaude_code/cli/runtime.py,sha256=0qYbtTP41m0K8eA2de_VFuERTV1NHn265O1-BMcdrw0,14238
15
15
  klaude_code/cli/self_update.py,sha256=fekLNRm3ivZ-Xbc-79rcgDBXbq-Zb-BkSQOGMRLeTAs,7986
16
16
  klaude_code/cli/session_cmd.py,sha256=jAopkqq_DGgoDIcGxT-RSzn9R4yqBC8NCaNgK1GLqnQ,2634
17
- klaude_code/command/__init__.py,sha256=FBsAJAxQRR9zy-0GEfVsa52dk3CGmJh6-L4cjZ9sfag,3218
17
+ klaude_code/command/__init__.py,sha256=B39fxrrvxb51B6qeQJoh3lXWCsPoI81BJvdSLb-8CYg,3117
18
18
  klaude_code/command/clear_cmd.py,sha256=DzCg6vQ5Eve7Rx4HdL-uta1pBDRQinOW6nwx5YlOA24,658
19
19
  klaude_code/command/command_abc.py,sha256=wUGE8NkCWWKy8lZXcW-UcQyMGnVikxTrCIycLlPU5dY,2350
20
20
  klaude_code/command/debug_cmd.py,sha256=9sBIAwHz28QoI-tHsU3ksQlDObF1ilIbtAAEAVMR0v0,2734
21
- klaude_code/command/diff_cmd.py,sha256=T_Oyo7rHZM5_qKGA0DjSHNZlzAHOhUwJS3Oqvur3Hho,5218
22
21
  klaude_code/command/export_cmd.py,sha256=Cs7YXWtos-ZfN9OEppIl8Xrb017kDG7R6hGiilqt2bM,1623
23
22
  klaude_code/command/export_online_cmd.py,sha256=kOVmFEW7gEEFvaKnXL8suNenktLyTW-13bT64NBGwUQ,5861
24
23
  klaude_code/command/help_cmd.py,sha256=yQJnVtj6sgXQdGsi4u9aS7EcjJLSrXccUA-v_bqmsRw,1633
25
24
  klaude_code/command/model_cmd.py,sha256=GmDln1N6u7eWLK15tm_lcdDJBY8qI0ih48h6AhijafI,1665
26
- klaude_code/command/prompt-deslop.md,sha256=YGaAXqem39zd0UWCFjWUj83Cf7cvUJq1768aJExFqeg,1346
25
+ klaude_code/command/prompt-deslop.md,sha256=Qpd8P_iVNv6DMg5EZotSivDaxx3SDqOUhMsIFCE3sdo,1309
27
26
  klaude_code/command/prompt-dev-docs-update.md,sha256=g1IWIWIa-3qlNOw5mBA4N9H1_nvYcw8AKo7XoQw_AZQ,1855
28
27
  klaude_code/command/prompt-dev-docs.md,sha256=PU9iT6XdUEH6grfSjHVma7xKOQcA__ZTKlEDkbbO0hA,1783
29
28
  klaude_code/command/prompt-handoff.md,sha256=RXIeXNwOpSpkwAyNFSvQFoo077TVkbj11fqQ2r8aCh4,1638
@@ -35,11 +34,11 @@ klaude_code/command/registry.py,sha256=avTjsoyLv11SsLsY_qb3OpsRjsSyxIlu7uwJI0Nq6
35
34
  klaude_code/command/release_notes_cmd.py,sha256=FIrBRfKTlXEp8mBh15buNjgOrl_GMX7FeeMWxYYBn1o,2674
36
35
  klaude_code/command/status_cmd.py,sha256=MnHtNdXTMqCJfB4_seHXDJEHYo99D-4ITZcGDCX2ilA,5359
37
36
  klaude_code/command/terminal_setup_cmd.py,sha256=SivM1gX_anGY_8DCQNFZ5VblFqt4sVgCMEWPRlo6K5w,10911
38
- klaude_code/command/thinking_cmd.py,sha256=KSLHXRS0gE5-Z9g1RgHprmDlpqXEp_lu1Z2MrdKcG-o,8493
37
+ klaude_code/command/thinking_cmd.py,sha256=XDyq0q8eb3Os4FyWjr-moiKjmzGIaNhOC9h89y1AZ84,8854
39
38
  klaude_code/config/__init__.py,sha256=Qrqvi8nizkj6N77h2vDj0r4rbgCiqxvz2HLBPFuWulA,120
40
39
  klaude_code/config/config.py,sha256=2jvM6a8zoC-TdRFaLIw3OW5paxxeXC6l-o05ds4RysA,7263
41
40
  klaude_code/config/select_model.py,sha256=KCdFjaoHXyO9QidNna_OGdDrvlEXtRUXKfG-F8kdNLk,5188
42
- klaude_code/const/__init__.py,sha256=MWm6fppZfAd8EGPdQgdEvRzrMRJ1Ei6Ma11eoNg4es0,4256
41
+ klaude_code/const/__init__.py,sha256=XGiZnUaQgNyYiWNNLipVovK035kgDrMmYRuNivvL-q8,4225
43
42
  klaude_code/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
43
  klaude_code/core/agent.py,sha256=bWm-UFX_0-KAy5j_YHH8X8o3MJT4-40Ni2EaDP2SL5k,5819
45
44
  klaude_code/core/executor.py,sha256=HmKtj0ZremRcLCGHkPckR_k0FSZh6FZ52-LMRSMZK9c,24882
@@ -47,42 +46,40 @@ klaude_code/core/manager/__init__.py,sha256=hdIbpnYj6i18byiWjtJIm5l7NYYDQMvafw8f
47
46
  klaude_code/core/manager/llm_clients.py,sha256=X2oMFWgJcP0tK8GEtMMDYR3HyR6_H8FuyCqpzWF5x2k,871
48
47
  klaude_code/core/manager/llm_clients_builder.py,sha256=pPZ_xBh-_ipV66L-9a1fnwNos4iik82Zkq0E0y3WrfI,1521
49
48
  klaude_code/core/manager/sub_agent_manager.py,sha256=GC54N5xErSa-ATLNQphnfZfKlt089uwuvYY9rTeh8Uk,4866
50
- klaude_code/core/prompt.py,sha256=c4tDx7WjESgMcPBEUso50BYz_4El5VDjWfw2v2sqst4,3728
49
+ klaude_code/core/prompt.py,sha256=piHP0Cu_u583_RHPjhVvTkdp5GgHQ29y3IM2QFVHITY,3869
51
50
  klaude_code/core/prompts/prompt-claude-code.md,sha256=uuWBv6GrG63mdmBedAHT5U9yOpbHSKFYbbS2xBnUzOE,8290
52
51
  klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md,sha256=SW-y8AmR99JL_9j26k9YVAOQuZ18vR12aT5CWHkZDc4,11741
53
- klaude_code/core/prompts/prompt-codex-gpt-5-1.md,sha256=jNi593_4L3EoMvjS0TwltF2b684gtDBsYHa9npxO34A,24239
52
+ klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md,sha256=GA1pIIF6JuAl4P3FIW4tVJ6zL_5iZ8MY_PF0DW9hBuU,11719
53
+ klaude_code/core/prompts/prompt-codex.md,sha256=ybL6CXrGnK6Cw9KuEicQg4kKllIcVa2NwUyuUWitPzk,21672
54
54
  klaude_code/core/prompts/prompt-gemini.md,sha256=JjE1tHSByGKJzjn4Gpj1zekT7ry1Yqbwx5qx3fZy2gE,3901
55
55
  klaude_code/core/prompts/prompt-minimal.md,sha256=6-ZmQQkE3f92W_3V2wS7ocB13wLog1_UojCjZG0K4v8,1559
56
56
  klaude_code/core/prompts/prompt-sub-agent-explore.md,sha256=21kFodjhvN0L-c_ZFo4yVhJOyzfgES-Dty9Vz_Ew9q8,2629
57
57
  klaude_code/core/prompts/prompt-sub-agent-oracle.md,sha256=1PLI3snvxnenCOPVrL0IxZnBl5b2xxGhlufHAyLyf60,1376
58
58
  klaude_code/core/prompts/prompt-sub-agent-web.md,sha256=ewS7-h8_u4QZftFpqrZWpht9Ap08s7zF9D4k4md8oD8,2360
59
59
  klaude_code/core/prompts/prompt-sub-agent.md,sha256=dmmdsOenbAOfqG6FmdR88spOLZkXmntDBs-cmZ9DN_g,897
60
- klaude_code/core/reminders.py,sha256=d7q4tba_oso2QOJwIeDG_buueSUYotka4RJdjnQidCk,20407
60
+ klaude_code/core/reminders.py,sha256=uSsHjWoGb1kNlBeKE8NiiF5iH79MGu-bC1ycrwmKHfI,21872
61
61
  klaude_code/core/task.py,sha256=_wvxo1YrfhhNI0BCsaA2JG-mAj3myd82vkn7ulENfgQ,10460
62
- klaude_code/core/tool/__init__.py,sha256=NzAaNEk_kJgcARm0YWBkOkrNqeaDFPpqHEsw9MoyCaI,2194
62
+ klaude_code/core/tool/__init__.py,sha256=OY4ma0ym_m4Dl6wOh9SF_4hGxgWVb_-TFzuAg3_zy8M,2024
63
63
  klaude_code/core/tool/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
- klaude_code/core/tool/file/_utils.py,sha256=LLmdEOJnC7MgdiAlWtBPZR0h23oM9wD_UsKqU1Nfkbs,774
64
+ klaude_code/core/tool/file/_utils.py,sha256=OG4BE9WyJqzH8ilVCL3D9yvAcHk-r-L9snd-E8gO_io,967
65
65
  klaude_code/core/tool/file/apply_patch.py,sha256=LZd3pYQ9ow_TxiFnqYuzD216HmvkLX6lW6BoMd9iQRs,17080
66
66
  klaude_code/core/tool/file/apply_patch_tool.md,sha256=KVDsjUiLDa97gym0NrZNVG4jA1_zN-2i-B3upVQyOhU,59
67
- klaude_code/core/tool/file/apply_patch_tool.py,sha256=7NDFdTKBz1wpYURyjGDU_z38wboz5hsKgcZBgy1b4BE,8345
67
+ klaude_code/core/tool/file/apply_patch_tool.py,sha256=Wp5x3auJZx99am4NiR4daRj1XJJtDpfBcsRlNXgucCE,6974
68
+ klaude_code/core/tool/file/diff_builder.py,sha256=b53S4Ebw7aPmd9AZ97Pxu6MRcnUIdVD36MJ2yTDrAjI,5872
68
69
  klaude_code/core/tool/file/edit_tool.md,sha256=rEcUjJuPC46t1nXWjTDxplDcxWDbzTWsr6_bYt5_aRI,1110
69
- klaude_code/core/tool/file/edit_tool.py,sha256=afRA6ujeAFOdt8ub3iq1nkO71K_vyEyFkjzf8CKLMeI,10010
70
- klaude_code/core/tool/file/multi_edit_tool.md,sha256=4G39b-jGdJdGGbBTj7R6tcUfcui9tXn6CLQ5uaX5y1M,2485
71
- klaude_code/core/tool/file/multi_edit_tool.py,sha256=OHLBzsPGj-7Kd0BWscbWmbCKpjkOeOAZ0iENieWT6L8,7090
70
+ klaude_code/core/tool/file/edit_tool.py,sha256=wuXISJRTWobNCKjkQ01UpoA13I0Zxcg1r7hXErAQxnQ,10897
72
71
  klaude_code/core/tool/file/read_tool.md,sha256=74SLSl1tq3L0por73M0QV_ws41MRIvGXQpfLb8dmtp0,1351
73
- klaude_code/core/tool/file/read_tool.py,sha256=Hz0belgVrPPzo5L2DBEquEJ4U4kxtoPG_SyL-oQVboE,14348
72
+ klaude_code/core/tool/file/read_tool.py,sha256=d51eVmDWNVnrhoHyL5_crB8cvCpBNH6S00VAzJfyLgg,12076
74
73
  klaude_code/core/tool/file/write_tool.md,sha256=CNnYgtieUasuHdpXLDpTEsqe492Pf7v75M4RQ3oIer8,613
75
- klaude_code/core/tool/file/write_tool.py,sha256=wSMx_cRwnPKq8wYq9dO_WsYBcEaN1s3B1bBZoY7fNJs,4531
74
+ klaude_code/core/tool/file/write_tool.py,sha256=YLQca640fUNA1P95qX52A7I-PaJuReEOLlxRnDcSe44,5414
76
75
  klaude_code/core/tool/memory/__init__.py,sha256=oeBpjUXcAilCtYXhiXDzavw5-BE_pbB2ZxsFJKTTcdc,143
77
- klaude_code/core/tool/memory/memory_tool.md,sha256=qWytU7k0kQQbne_8n4cei2-dDTNpHesmbI2PYza3XR0,1299
78
- klaude_code/core/tool/memory/memory_tool.py,sha256=xasFf1IQp1LFaL4ctDIQt5jfrd_z62ZsBeE42QIKAKs,18949
79
76
  klaude_code/core/tool/memory/skill_loader.py,sha256=WGC3svsasEIM8yDAjFmFId1HQuEhuD3KFEvgoXcHmD4,8870
80
77
  klaude_code/core/tool/memory/skill_tool.md,sha256=UfjJK5EGAd3mf7ym5rIrRdPyV3kBTxNnwzOjNnHOBrE,973
81
78
  klaude_code/core/tool/memory/skill_tool.py,sha256=8SC4asNZSKfExuhzbyGz4f2cr78PgCpNkut_31IHePw,3602
82
79
  klaude_code/core/tool/report_back_tool.py,sha256=KRZzQAIxniwXe58SDJcfK_DCf9TFFAx8XC75wPEjmpY,3246
83
80
  klaude_code/core/tool/shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
81
  klaude_code/core/tool/shell/bash_tool.md,sha256=ILKpnRCBTkU2uSDEdZQjNYo1l6hsM4TO-3RD5zWC61c,3935
85
- klaude_code/core/tool/shell/bash_tool.py,sha256=PEcpBiWPclaLa87SM8iIZF3EK0QGWIV9XNkkixCnaXs,8409
82
+ klaude_code/core/tool/shell/bash_tool.py,sha256=NSWXU_MzKqJBtkljOPNWGTwtAZwxMrCF-y8GC5MysFI,14465
86
83
  klaude_code/core/tool/shell/command_safety.py,sha256=bGsooLovuzq8WmLcZ2v24AVBDj3bZv2p4GSL0IlixvM,13192
87
84
  klaude_code/core/tool/sub_agent_tool.py,sha256=5n0HDxv4cUzuwBhYiAe3gIJ0s3QgV4GSV12CIIkD_g0,3190
88
85
  klaude_code/core/tool/todo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -97,7 +94,7 @@ klaude_code/core/tool/tool_registry.py,sha256=VFB0Z4BWtYKIWtpZVGhjSLcgfK244gow_U
97
94
  klaude_code/core/tool/tool_runner.py,sha256=A_RviRFr8kF6TjdGOzdgCIingeyJLBxNbZpcTnzT64E,10695
98
95
  klaude_code/core/tool/truncation.py,sha256=YPKzelOM45rHW_OkcfX5_Ojg_pPi8yDZu7lSnwl9b_k,7334
99
96
  klaude_code/core/tool/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
- klaude_code/core/tool/web/mermaid_tool.md,sha256=Ketpxpr7lz8238p5Q7ZzcyWchWd4dU68u708-dxaZds,978
97
+ klaude_code/core/tool/web/mermaid_tool.md,sha256=gyNkf1rSra-_4EyDFJOyoLBg8CoADVkEcWEuJFFz2VM,1564
101
98
  klaude_code/core/tool/web/mermaid_tool.py,sha256=Ok0A27oHLnV1c__74bheUuy3wpqDJ1zaXUSxuuqsNPI,2630
102
99
  klaude_code/core/tool/web/web_fetch_tool.md,sha256=jIrW-EAmfl50bBevcfioQ3Vrg8kWlHSut8ze_sRgRGw,486
103
100
  klaude_code/core/tool/web/web_fetch_tool.py,sha256=7WzOsAHdfijROOlk3JixtHUV1D2Cuwnf-AhoznrqATw,8551
@@ -127,10 +124,10 @@ klaude_code/llm/responses/client.py,sha256=XEsVehevQJ0WFbEVxIkI-su7VwIcaeq0P9eSr
127
124
  klaude_code/llm/responses/input.py,sha256=qr61LmQJdcb_f-ofrAz06WpK_k4PEcI36XsyuZAXbKk,6805
128
125
  klaude_code/llm/usage.py,sha256=cq6yZNSKBhRVVjFqBYJQrK3mw9ZSLXaTpbDeal-BjBQ,4205
129
126
  klaude_code/protocol/__init__.py,sha256=aGUgzhYqvhuT3Mk2vj7lrHGriH4h9TSbqV1RsRFAZjQ,194
130
- klaude_code/protocol/commands.py,sha256=z9Ckp_YGC403axcqIkB-xTUM-gtlTtd-1lNx83AQriE,662
127
+ klaude_code/protocol/commands.py,sha256=GN6GX9fo7YYtfumrBTpOmOvZofsnzZN2SAxP2X0BjTk,644
131
128
  klaude_code/protocol/events.py,sha256=KUMf1rLNdHQO9cZiQ9Pa1VsKkP1PTMbUkp18bu_jGy8,3935
132
129
  klaude_code/protocol/llm_param.py,sha256=cb4ubLq21PIsMOC8WJb0aid12z_sT1b7FsbNJMr-jLg,4255
133
- klaude_code/protocol/model.py,sha256=rKgsj0ePPjL1Icdw0AeJ2h_EEeHpPhmUQvYx7gn0YI0,12800
130
+ klaude_code/protocol/model.py,sha256=yKm2lk80cOLhYB4twD79bfKlTPL2rSY_tNFHJcZ1tPU,13330
134
131
  klaude_code/protocol/op.py,sha256=X3UeXxBOLf_jkEaYXhQSTpjtUV2u1Ot5f4bbPWNdQp4,4241
135
132
  klaude_code/protocol/op_handler.py,sha256=qaWrm2rlskSyF7ukPtxFAKf8brgLsUaVzg6035N-7w0,1565
136
133
  klaude_code/protocol/sub_agent/__init__.py,sha256=Abap5lPLgnSCQsVD3axfeqnj2UtxOcDLGX8e9HugfSU,3964
@@ -138,14 +135,14 @@ klaude_code/protocol/sub_agent/explore.py,sha256=Z4M7i98XBLew38ClXiW-hJteSYjMUu2
138
135
  klaude_code/protocol/sub_agent/oracle.py,sha256=0cbuutKQcvwaM--Q15mbkCdbpZMF4YjxDN1jkuGVKp4,3344
139
136
  klaude_code/protocol/sub_agent/task.py,sha256=fvj4i1vfWXivStQ-9urDS40wTWkmNRvl6D-A0exExJg,3608
140
137
  klaude_code/protocol/sub_agent/web.py,sha256=Z5vUM367kz8CIexN6UVPG4XxzVOaaRek-Ga64NvcZdk,3043
141
- klaude_code/protocol/tools.py,sha256=QvFtVAGkA5elef3HWnSs7FcxcI0FOn4N_toCLT-S6Rw,401
138
+ klaude_code/protocol/tools.py,sha256=OpGCr47ARKaCHcIlljhEN-p-2h4djgbP5jtfTIoKB-A,359
142
139
  klaude_code/session/__init__.py,sha256=oXcDA5w-gJCbzmlF8yuWy3ezIW9DgFBNUs-gJHUJ-Rc,121
143
140
  klaude_code/session/codec.py,sha256=ummbqT7t6uHHXtaS9lOkyhi1h0YpMk7SNSms8DyGAHU,2015
144
- klaude_code/session/export.py,sha256=NmIG2SI1ec6HpXglb3zI2xeN8A80rMlPWP_zJGw4uFs,27147
141
+ klaude_code/session/export.py,sha256=EvfmZMTScKdaZ43rT4xqQpi8s1_QZ_anUeW-XQ6l15E,29297
145
142
  klaude_code/session/selector.py,sha256=gijwWQkSV20XYP3Fxr27mFXuqP4ChY2DQm_YuBOTQKw,2888
146
- klaude_code/session/session.py,sha256=P3vkPtlTitS6_01ZAPLG9hDmeyoBQx6RrZmrQZNGgYc,15772
143
+ klaude_code/session/session.py,sha256=LDrRlxv35_jJGwxv6Lv_WdBYNY2UZZAZ4vippq8pN_o,16016
147
144
  klaude_code/session/store.py,sha256=MLNTE-HiwmE3foM7n3AUtPvsf_hdyMHYA66ludTR8KU,6872
148
- klaude_code/session/templates/export_session.html,sha256=VM3uSEIEKU4ewBeaTS-iO3iBswbyiEGfC9KuikGpka4,119113
145
+ klaude_code/session/templates/export_session.html,sha256=bA27AkcC7DQRoWmcMBeaR8WOx1z76hezEDf0aYH-0HQ,119780
149
146
  klaude_code/trace/__init__.py,sha256=q8uOYhWr2_Mia1aEK_vkqx91YAfFM25ItIB6J8n3_pM,352
150
147
  klaude_code/trace/log.py,sha256=0H_RqkytSpt6AAIFDg-MV_8vA9zsR9BB1UqT6moTTTg,9134
151
148
  klaude_code/ui/__init__.py,sha256=XuEQsFUkJet8HI04cRmNLwnHOUqaPCRy4hF7PJnIfCY,2737
@@ -168,24 +165,24 @@ klaude_code/ui/modes/repl/key_bindings.py,sha256=Fxz9Ey2SnOHvfleMeSYVduxuofY0Yo-
168
165
  klaude_code/ui/modes/repl/renderer.py,sha256=YJAF3Cx2fmyrmGOHLVRvUlXvlRR8ptYZtngR50Vg7uY,11639
169
166
  klaude_code/ui/renderers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
170
167
  klaude_code/ui/renderers/assistant.py,sha256=Dxy6v4pX28RyWhnrjBteY8_NvDIi_jQa-j0mWt-eqWY,569
171
- klaude_code/ui/renderers/common.py,sha256=3wVTOX2UR575yA_vmqheM4cmNOoFu_UKMrrD7kzwPUw,1787
172
- klaude_code/ui/renderers/developer.py,sha256=_xpsLZE6NhSZmmpHUmJMS3uZlOniNhLwf6M9er00d44,6876
173
- klaude_code/ui/renderers/diffs.py,sha256=iPQxxZW1JGPwtTdCKMEkDlNb5trLm9tdWjfMRmtj6yQ,7616
168
+ klaude_code/ui/renderers/common.py,sha256=5RdXC3ngtlhfmxRlbwOPtHtbXhAoWwbHoGX78tZcaTc,2284
169
+ klaude_code/ui/renderers/developer.py,sha256=r7Rb2OQKfDiv5pMszgN8PcdW9k7KbOLTXzL4rT6uc7s,6536
170
+ klaude_code/ui/renderers/diffs.py,sha256=8NlIySQqN1smIwLePdWzdFeiny-trCY14RQZ1VDlkA0,10407
174
171
  klaude_code/ui/renderers/errors.py,sha256=o9pOHRqZ0nxMPwkmV4CG81X7taW1-izvga6RY3fFRMI,516
175
172
  klaude_code/ui/renderers/metadata.py,sha256=K0D9nx02rulZb2Sz_yOasFvlSFxG5U-VI3mIKD0JqbE,8290
176
173
  klaude_code/ui/renderers/sub_agent.py,sha256=grQ_9G_7iYHdCpxrM0qDAKEMQfXcxBv0bI0GPe3s0lE,4961
177
174
  klaude_code/ui/renderers/thinking.py,sha256=hUIJzgsjvpK2cM_C2103cG4gz785FUt1PtQsQtgji9s,1764
178
- klaude_code/ui/renderers/tools.py,sha256=Ch-Ads0Bj4qI3enZ7-A6j7QTY8mp9-tHMO7Eev_82z4,23690
175
+ klaude_code/ui/renderers/tools.py,sha256=jG5mCatTMxHMAkiwxVif5hkioPZ178c4YxI-rT_sQA8,20532
179
176
  klaude_code/ui/renderers/user_input.py,sha256=rDdOYvbgJ6oePQAtyTCK-KhARfLKytpTZboZ-cFIuJQ,2603
180
177
  klaude_code/ui/rich/__init__.py,sha256=zEZjnHR3Fnv_sFMxwIMjoJfwDoC4GRGv3lHJzAGRq_o,236
181
178
  klaude_code/ui/rich/cjk_wrap.py,sha256=ncmifgTwF6q95iayHQyazGbntt7BRQb_Ed7aXc8JU6Y,7551
182
179
  klaude_code/ui/rich/code_panel.py,sha256=MdUP4QSaQJQxX0MQJT0pvrkhpGYZx3gWdIRbZT_Uj_I,3938
183
180
  klaude_code/ui/rich/live.py,sha256=Uid0QAZG7mHb4KrCF8p9c9n1nHLHzW75xSqcLZ4bLWY,2098
184
- klaude_code/ui/rich/markdown.py,sha256=cL1irwj4OuEdUvOSUjUopiuf_VMHKd52yXHIsl3JINA,10787
181
+ klaude_code/ui/rich/markdown.py,sha256=8YgQyX_sQmrXgbDYRyiH4YHKV3zZ5PMz8yfA2hQDAJI,11156
185
182
  klaude_code/ui/rich/quote.py,sha256=tZcxN73SfDBHF_qk0Jkh9gWBqPBn8VLp9RF36YRdKEM,1123
186
183
  klaude_code/ui/rich/searchable_text.py,sha256=DCVZgEFv7_ergAvT2v7XrfQAUXUzhmAwuVAchlIx8RY,2448
187
- klaude_code/ui/rich/status.py,sha256=1sp3ss0ftskIi7xfeGPayMiELTTWJ5CR-oWmaS9Td_U,9735
188
- klaude_code/ui/rich/theme.py,sha256=5m4xDw436GLVecsnKo94lRo44pPuFPXHV-dcxrdHQAI,10723
184
+ klaude_code/ui/rich/status.py,sha256=KvBZwPX2EkPaxDadj6AQzFs2Wmze13wQpK_kLIB35e8,9622
185
+ klaude_code/ui/rich/theme.py,sha256=l6EDIK50W00YHk4tCUSJA54J9PlTHnfZhNfeZSzTcu0,11160
189
186
  klaude_code/ui/terminal/__init__.py,sha256=GIMnsEcIAGT_vBHvTlWEdyNmAEpruyscUA6M_j3GQZU,1412
190
187
  klaude_code/ui/terminal/color.py,sha256=M-i09DVlLAhAyhQjfeAi7OipoGi1p_OVkaZxeRfykY0,7135
191
188
  klaude_code/ui/terminal/control.py,sha256=6SGNwxorP3jMW9xqnZy2BC0OsJd4DSrS13O3t6YlZzs,4916
@@ -193,7 +190,7 @@ klaude_code/ui/terminal/notifier.py,sha256=wkRM66d98Oh6PujnN4bB7NiQxIYEHqQXverMK
193
190
  klaude_code/ui/terminal/progress_bar.py,sha256=MDnhPbqCnN4GDgLOlxxOEVZPDwVC_XL2NM5sl1MFNcQ,2133
194
191
  klaude_code/ui/utils/__init__.py,sha256=YEsCLjbCPaPza-UXTPUMTJTrc9BmNBUP5CbFWlshyOQ,15
195
192
  klaude_code/ui/utils/common.py,sha256=tqHqwgLtAyP805kwRFyoAL4EgMutcNb3Y-GAXJ4IeuM,2263
196
- klaude_code-1.2.20.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
197
- klaude_code-1.2.20.dist-info/entry_points.txt,sha256=7CWKjolvs6dZiYHpelhA_FRJ-sVDh43eu3iWuOhKc_w,53
198
- klaude_code-1.2.20.dist-info/METADATA,sha256=k01_8Ew_jz0ALaq8oMtAcp-nspPH-YHHre2deCPXFMY,7636
199
- klaude_code-1.2.20.dist-info/RECORD,,
193
+ klaude_code-1.2.22.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
194
+ klaude_code-1.2.22.dist-info/entry_points.txt,sha256=7CWKjolvs6dZiYHpelhA_FRJ-sVDh43eu3iWuOhKc_w,53
195
+ klaude_code-1.2.22.dist-info/METADATA,sha256=8oDw2_kjHPrLuNM1B3JmcyEQ4Vxd3YBVvsr1GQ0fTnc,7678
196
+ klaude_code-1.2.22.dist-info/RECORD,,
@@ -1,136 +0,0 @@
1
- import subprocess
2
- from pathlib import Path
3
-
4
- from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
5
- from klaude_code.protocol import commands, events, model
6
-
7
-
8
- class DiffCommand(CommandABC):
9
- """Show git diff for the current repository."""
10
-
11
- @property
12
- def name(self) -> commands.CommandName:
13
- return commands.CommandName.DIFF
14
-
15
- @property
16
- def summary(self) -> str:
17
- return "Show git diff"
18
-
19
- async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
20
- del user_input # unused
21
- try:
22
- # Check if current directory is in a git repository
23
- git_check = subprocess.run(
24
- ["git", "rev-parse", "--is-inside-work-tree"],
25
- cwd=Path.cwd(),
26
- capture_output=True,
27
- text=True,
28
- timeout=5.0,
29
- )
30
-
31
- if git_check.returncode != 0:
32
- # Not in a git repository
33
- event = events.DeveloperMessageEvent(
34
- session_id=agent.session.id,
35
- item=model.DeveloperMessageItem(
36
- content="No in a git repo",
37
- command_output=model.CommandOutput(command_name=self.name, is_error=True),
38
- ),
39
- )
40
- return CommandResult(events=[event])
41
-
42
- # Run git diff in current directory
43
- result = subprocess.run(
44
- ["git", "diff", "HEAD"],
45
- cwd=Path.cwd(),
46
- capture_output=True,
47
- text=True,
48
- timeout=10.0,
49
- )
50
-
51
- if result.returncode != 0:
52
- # Git command failed
53
- error_msg = result.stderr.strip() or "git diff command failed"
54
- event = events.DeveloperMessageEvent(
55
- session_id=agent.session.id,
56
- item=model.DeveloperMessageItem(
57
- content=f"Error: {error_msg}",
58
- command_output=model.CommandOutput(command_name=self.name, is_error=True),
59
- ),
60
- )
61
- return CommandResult(events=[event])
62
-
63
- diff_output = result.stdout.strip()
64
-
65
- # Get untracked files
66
- untracked_result = subprocess.run(
67
- ["git", "ls-files", "--others", "--exclude-standard"],
68
- cwd=Path.cwd(),
69
- capture_output=True,
70
- text=True,
71
- timeout=10.0,
72
- )
73
-
74
- untracked_files = untracked_result.stdout.strip()
75
-
76
- # Combine diff output and untracked files
77
- output_parts: list[str] = []
78
-
79
- if diff_output:
80
- output_parts.append(diff_output)
81
-
82
- if untracked_files:
83
- untracked_lines = untracked_files.split("\n")
84
- untracked_section = "git ls-files --others --exclude-standard\n" + "\n".join(
85
- f"{file}" for file in untracked_lines
86
- )
87
- output_parts.append(untracked_section)
88
-
89
- if not output_parts:
90
- # No changes and no untracked files
91
- event = events.DeveloperMessageEvent(
92
- session_id=agent.session.id,
93
- item=model.DeveloperMessageItem(
94
- content="", command_output=model.CommandOutput(command_name=self.name)
95
- ),
96
- )
97
- return CommandResult(events=[event])
98
-
99
- # Has changes or untracked files
100
- combined_output = "\n\n".join(output_parts)
101
- event = events.DeveloperMessageEvent(
102
- session_id=agent.session.id,
103
- item=model.DeveloperMessageItem(
104
- content=combined_output,
105
- command_output=model.CommandOutput(command_name=self.name),
106
- ),
107
- )
108
- return CommandResult(events=[event])
109
-
110
- except subprocess.TimeoutExpired:
111
- event = events.DeveloperMessageEvent(
112
- session_id=agent.session.id,
113
- item=model.DeveloperMessageItem(
114
- content="Error: git diff command timeout",
115
- command_output=model.CommandOutput(command_name=self.name, is_error=True),
116
- ),
117
- )
118
- return CommandResult(events=[event])
119
- except FileNotFoundError:
120
- event = events.DeveloperMessageEvent(
121
- session_id=agent.session.id,
122
- item=model.DeveloperMessageItem(
123
- content="Error: git command not found",
124
- command_output=model.CommandOutput(command_name=self.name, is_error=True),
125
- ),
126
- )
127
- return CommandResult(events=[event])
128
- except Exception as e:
129
- event = events.DeveloperMessageEvent(
130
- session_id=agent.session.id,
131
- item=model.DeveloperMessageItem(
132
- content=f"Error:{e}",
133
- command_output=model.CommandOutput(command_name=self.name, is_error=True),
134
- ),
135
- )
136
- return CommandResult(events=[event])
@@ -1,42 +0,0 @@
1
- This is a tool for making multiple edits to a single file in one operation. It is built on top of the Edit tool and allows you to perform multiple find-and-replace operations efficiently. Prefer this tool over the Edit tool when you need to make multiple edits to the same file.
2
-
3
- Before using this tool:
4
-
5
- 1. Use the Read tool to understand the file's contents and context
6
- 2. Verify the directory path is correct
7
-
8
- To make multiple file edits, provide the following:
9
- 1. file_path: The absolute path to the file to modify (must be absolute, not relative)
10
- 2. edits: An array of edit operations to perform, where each edit contains:
11
- - old_string: The text to replace (must match the file contents exactly, including all whitespace and indentation)
12
- - new_string: The edited text to replace the old_string
13
- - replace_all: Replace all occurences of old_string. This parameter is optional and defaults to false.
14
-
15
- IMPORTANT:
16
- - All edits are applied in sequence, in the order they are provided
17
- - Each edit operates on the result of the previous edit
18
- - All edits must be valid for the operation to succeed - if any edit fails, none will be applied
19
- - This tool is ideal when you need to make several changes to different parts of the same file
20
- - For Jupyter notebooks (.ipynb files), use the NotebookEdit instead
21
-
22
- CRITICAL REQUIREMENTS:
23
- 1. All edits follow the same requirements as the single Edit tool
24
- 2. The edits are atomic - either all succeed or none are applied
25
- 3. Plan your edits carefully to avoid conflicts between sequential operations
26
-
27
- WARNING:
28
- - The tool will fail if edits.old_string doesn't match the file contents exactly (including whitespace)
29
- - The tool will fail if edits.old_string and edits.new_string are the same
30
- - Since edits are applied in sequence, ensure that earlier edits don't affect the text that later edits are trying to find
31
-
32
- When making edits:
33
- - Ensure all edits result in idiomatic, correct code
34
- - Do not leave the code in a broken state
35
- - Always use absolute file paths (starting with /)
36
- - Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.
37
- - Use replace_all for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.
38
-
39
- If you want to create a new file, use:
40
- - A new file path, including dir name if needed
41
- - First edit: empty old_string and the new file's contents as new_string
42
- - Subsequent edits: normal edit operations on the created content
@@ -1,175 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import asyncio
4
- import contextlib
5
- import difflib
6
- import os
7
- from pathlib import Path
8
-
9
- from pydantic import BaseModel, Field
10
-
11
- from klaude_code.core.tool.file._utils import file_exists, is_directory, read_text, write_text
12
- from klaude_code.core.tool.file.edit_tool import EditTool
13
- from klaude_code.core.tool.tool_abc import ToolABC, load_desc
14
- from klaude_code.core.tool.tool_context import get_current_file_tracker
15
- from klaude_code.core.tool.tool_registry import register
16
- from klaude_code.protocol import llm_param, model, tools
17
-
18
-
19
- @register(tools.MULTI_EDIT)
20
- class MultiEditTool(ToolABC):
21
- class MultiEditEditItem(BaseModel):
22
- old_string: str
23
- new_string: str
24
- replace_all: bool = Field(default=False)
25
-
26
- class MultiEditArguments(BaseModel):
27
- file_path: str
28
- edits: list[MultiEditTool.MultiEditEditItem]
29
-
30
- @classmethod
31
- def schema(cls) -> llm_param.ToolSchema:
32
- return llm_param.ToolSchema(
33
- name=tools.MULTI_EDIT,
34
- type="function",
35
- description=load_desc(Path(__file__).parent / "multi_edit_tool.md"),
36
- parameters={
37
- "type": "object",
38
- "properties": {
39
- "file_path": {
40
- "type": "string",
41
- "description": "The absolute path to the file to modify",
42
- },
43
- "edits": {
44
- "type": "array",
45
- "items": {
46
- "type": "object",
47
- "properties": {
48
- "old_string": {
49
- "type": "string",
50
- "description": "The text to replace",
51
- },
52
- "new_string": {
53
- "type": "string",
54
- "description": "The text to replace it with",
55
- },
56
- "replace_all": {
57
- "type": "boolean",
58
- "default": False,
59
- "description": "Replace all occurences of old_string (default false).",
60
- },
61
- },
62
- "required": ["old_string", "new_string"],
63
- "additionalProperties": False,
64
- },
65
- "minItems": 1,
66
- "description": "Array of edit operations to perform sequentially on the file",
67
- },
68
- },
69
- "required": ["file_path", "edits"],
70
- "additionalProperties": False,
71
- },
72
- )
73
-
74
- @classmethod
75
- async def call(cls, arguments: str) -> model.ToolResultItem:
76
- try:
77
- args = MultiEditTool.MultiEditArguments.model_validate_json(arguments)
78
- except Exception as e: # pragma: no cover - defensive
79
- return model.ToolResultItem(status="error", output=f"Invalid arguments: {e}")
80
-
81
- file_path = os.path.abspath(args.file_path)
82
-
83
- # Directory error first
84
- if is_directory(file_path):
85
- return model.ToolResultItem(
86
- status="error",
87
- output="<tool_use_error>Illegal operation on a directory. multi_edit</tool_use_error>",
88
- )
89
-
90
- file_tracker = get_current_file_tracker()
91
-
92
- # FileTracker check:
93
- if file_exists(file_path):
94
- if file_tracker is not None:
95
- tracked_status = file_tracker.get(file_path)
96
- if tracked_status is None:
97
- return model.ToolResultItem(
98
- status="error",
99
- output=("File has not been read yet. Read it first before writing to it."),
100
- )
101
- try:
102
- current_mtime = Path(file_path).stat().st_mtime
103
- except Exception:
104
- current_mtime = tracked_status.mtime
105
- if current_mtime != tracked_status.mtime:
106
- return model.ToolResultItem(
107
- status="error",
108
- output=(
109
- "File has been modified externally. Either by user or a linter. Read it first before writing to it."
110
- ),
111
- )
112
- else:
113
- # Allow creation only if first edit is creating content (old_string == "")
114
- if not args.edits or args.edits[0].old_string != "":
115
- return model.ToolResultItem(
116
- status="error",
117
- output=("File has not been read yet. Read it first before writing to it."),
118
- )
119
-
120
- # Load initial content (empty for new file case)
121
- if file_exists(file_path):
122
- before = await asyncio.to_thread(read_text, file_path)
123
- else:
124
- before = ""
125
-
126
- # Validate all edits atomically against staged content
127
- staged = before
128
- for edit in args.edits:
129
- err = EditTool.valid(
130
- content=staged,
131
- old_string=edit.old_string,
132
- new_string=edit.new_string,
133
- replace_all=edit.replace_all,
134
- )
135
- if err is not None:
136
- return model.ToolResultItem(status="error", output=err)
137
- # Apply to staged content
138
- staged = EditTool.execute(
139
- content=staged,
140
- old_string=edit.old_string,
141
- new_string=edit.new_string,
142
- replace_all=edit.replace_all,
143
- )
144
-
145
- # All edits valid; write to disk
146
- try:
147
- await asyncio.to_thread(write_text, file_path, staged)
148
- except Exception as e: # pragma: no cover
149
- return model.ToolResultItem(status="error", output=f"<tool_use_error>{e}</tool_use_error>")
150
-
151
- # Prepare UI extra: unified diff
152
- diff_lines = list(
153
- difflib.unified_diff(
154
- before.splitlines(),
155
- staged.splitlines(),
156
- fromfile=file_path,
157
- tofile=file_path,
158
- n=3,
159
- )
160
- )
161
- diff_text = "\n".join(diff_lines)
162
- ui_extra = model.DiffTextUIExtra(diff_text=diff_text)
163
-
164
- # Update tracker
165
- if file_tracker is not None:
166
- with contextlib.suppress(Exception):
167
- existing = file_tracker.get(file_path)
168
- is_mem = existing.is_memory if existing else False
169
- file_tracker[file_path] = model.FileStatus(mtime=Path(file_path).stat().st_mtime, is_memory=is_mem)
170
-
171
- # Build output message
172
- lines = [f"Applied {len(args.edits)} edits to {file_path}:"]
173
- for i, edit in enumerate(args.edits, start=1):
174
- lines.append(f'{i}. Replaced "{edit.old_string}" with "{edit.new_string}"')
175
- return model.ToolResultItem(status="success", output="\n".join(lines), ui_extra=ui_extra)