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.
- klaude_code/cli/debug.py +8 -10
- klaude_code/cli/main.py +23 -0
- klaude_code/cli/runtime.py +13 -1
- klaude_code/command/__init__.py +0 -3
- klaude_code/command/prompt-deslop.md +1 -1
- klaude_code/command/thinking_cmd.py +10 -0
- klaude_code/const/__init__.py +2 -5
- klaude_code/core/prompt.py +5 -2
- klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +117 -0
- klaude_code/core/prompts/{prompt-codex-gpt-5-1.md → prompt-codex.md} +9 -42
- klaude_code/core/reminders.py +36 -2
- klaude_code/core/tool/__init__.py +0 -5
- klaude_code/core/tool/file/_utils.py +6 -0
- klaude_code/core/tool/file/apply_patch_tool.py +30 -72
- klaude_code/core/tool/file/diff_builder.py +151 -0
- klaude_code/core/tool/file/edit_tool.py +35 -18
- klaude_code/core/tool/file/read_tool.py +45 -86
- klaude_code/core/tool/file/write_tool.py +40 -30
- klaude_code/core/tool/shell/bash_tool.py +151 -3
- klaude_code/core/tool/web/mermaid_tool.md +26 -0
- klaude_code/protocol/commands.py +0 -1
- klaude_code/protocol/model.py +29 -10
- klaude_code/protocol/tools.py +1 -2
- klaude_code/session/export.py +75 -20
- klaude_code/session/session.py +7 -0
- klaude_code/session/templates/export_session.html +28 -0
- klaude_code/ui/renderers/common.py +26 -11
- klaude_code/ui/renderers/developer.py +0 -5
- klaude_code/ui/renderers/diffs.py +84 -0
- klaude_code/ui/renderers/tools.py +19 -98
- klaude_code/ui/rich/markdown.py +11 -1
- klaude_code/ui/rich/status.py +8 -11
- klaude_code/ui/rich/theme.py +14 -4
- {klaude_code-1.2.20.dist-info → klaude_code-1.2.22.dist-info}/METADATA +2 -1
- {klaude_code-1.2.20.dist-info → klaude_code-1.2.22.dist-info}/RECORD +37 -40
- klaude_code/command/diff_cmd.py +0 -136
- klaude_code/core/tool/file/multi_edit_tool.md +0 -42
- klaude_code/core/tool/file/multi_edit_tool.py +0 -175
- klaude_code/core/tool/memory/memory_tool.md +0 -20
- klaude_code/core/tool/memory/memory_tool.py +0 -456
- {klaude_code-1.2.20.dist-info → klaude_code-1.2.22.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.20.dist-info → klaude_code-1.2.22.dist-info}/entry_points.txt +0 -0
klaude_code/ui/rich/status.py
CHANGED
|
@@ -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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
klaude_code/ui/rich/theme.py
CHANGED
|
@@ -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 #
|
|
44
|
-
|
|
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 #
|
|
64
|
-
|
|
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.
|
|
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=
|
|
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=
|
|
14
|
-
klaude_code/cli/runtime.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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-
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
172
|
-
klaude_code/ui/renderers/developer.py,sha256=
|
|
173
|
-
klaude_code/ui/renderers/diffs.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
188
|
-
klaude_code/ui/rich/theme.py,sha256=
|
|
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.
|
|
197
|
-
klaude_code-1.2.
|
|
198
|
-
klaude_code-1.2.
|
|
199
|
-
klaude_code-1.2.
|
|
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,,
|
klaude_code/command/diff_cmd.py
DELETED
|
@@ -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)
|