code-puppy 0.0.287__py3-none-any.whl → 0.0.323__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.
- code_puppy/__init__.py +3 -1
- code_puppy/agents/agent_code_puppy.py +5 -4
- code_puppy/agents/agent_creator_agent.py +22 -18
- code_puppy/agents/agent_manager.py +2 -2
- code_puppy/agents/base_agent.py +496 -102
- code_puppy/callbacks.py +8 -0
- code_puppy/chatgpt_codex_client.py +283 -0
- code_puppy/cli_runner.py +795 -0
- code_puppy/command_line/add_model_menu.py +19 -16
- code_puppy/command_line/attachments.py +10 -5
- code_puppy/command_line/autosave_menu.py +269 -41
- code_puppy/command_line/colors_menu.py +515 -0
- code_puppy/command_line/command_handler.py +10 -24
- code_puppy/command_line/config_commands.py +106 -25
- code_puppy/command_line/core_commands.py +32 -20
- code_puppy/command_line/mcp/add_command.py +3 -16
- code_puppy/command_line/mcp/base.py +0 -3
- code_puppy/command_line/mcp/catalog_server_installer.py +15 -15
- code_puppy/command_line/mcp/custom_server_form.py +66 -5
- code_puppy/command_line/mcp/custom_server_installer.py +17 -17
- code_puppy/command_line/mcp/edit_command.py +15 -22
- code_puppy/command_line/mcp/handler.py +7 -2
- code_puppy/command_line/mcp/help_command.py +2 -2
- code_puppy/command_line/mcp/install_command.py +10 -14
- code_puppy/command_line/mcp/install_menu.py +2 -6
- code_puppy/command_line/mcp/list_command.py +2 -2
- code_puppy/command_line/mcp/logs_command.py +174 -65
- code_puppy/command_line/mcp/remove_command.py +2 -2
- code_puppy/command_line/mcp/restart_command.py +7 -2
- code_puppy/command_line/mcp/search_command.py +16 -10
- code_puppy/command_line/mcp/start_all_command.py +16 -6
- code_puppy/command_line/mcp/start_command.py +12 -10
- code_puppy/command_line/mcp/status_command.py +4 -5
- code_puppy/command_line/mcp/stop_all_command.py +5 -1
- code_puppy/command_line/mcp/stop_command.py +6 -4
- code_puppy/command_line/mcp/test_command.py +2 -2
- code_puppy/command_line/mcp/wizard_utils.py +20 -16
- code_puppy/command_line/model_settings_menu.py +53 -7
- code_puppy/command_line/motd.py +1 -1
- code_puppy/command_line/pin_command_completion.py +82 -7
- code_puppy/command_line/prompt_toolkit_completion.py +32 -9
- code_puppy/command_line/session_commands.py +11 -4
- code_puppy/config.py +217 -53
- code_puppy/error_logging.py +118 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/keymap.py +126 -0
- code_puppy/main.py +5 -745
- code_puppy/mcp_/__init__.py +17 -0
- code_puppy/mcp_/blocking_startup.py +63 -36
- code_puppy/mcp_/captured_stdio_server.py +1 -1
- code_puppy/mcp_/config_wizard.py +4 -4
- code_puppy/mcp_/dashboard.py +15 -6
- code_puppy/mcp_/managed_server.py +25 -5
- code_puppy/mcp_/manager.py +65 -0
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/mcp_/registry.py +6 -6
- code_puppy/messaging/__init__.py +184 -2
- code_puppy/messaging/bus.py +610 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +3 -3
- code_puppy/messaging/messages.py +470 -0
- code_puppy/messaging/renderers.py +43 -141
- code_puppy/messaging/rich_renderer.py +900 -0
- code_puppy/messaging/spinner/console_spinner.py +39 -2
- code_puppy/model_factory.py +292 -53
- code_puppy/model_utils.py +57 -48
- code_puppy/models.json +19 -5
- code_puppy/plugins/__init__.py +152 -10
- code_puppy/plugins/chatgpt_oauth/config.py +20 -12
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +5 -6
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +3 -3
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +30 -13
- code_puppy/plugins/chatgpt_oauth/utils.py +180 -65
- code_puppy/plugins/claude_code_oauth/config.py +15 -11
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +28 -0
- code_puppy/plugins/claude_code_oauth/utils.py +6 -1
- code_puppy/plugins/example_custom_command/register_callbacks.py +2 -2
- code_puppy/plugins/oauth_puppy_html.py +3 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +1 -134
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +77 -3
- code_puppy/prompts/codex_system_prompt.md +310 -0
- code_puppy/pydantic_patches.py +131 -0
- code_puppy/session_storage.py +2 -1
- code_puppy/status_display.py +7 -5
- code_puppy/terminal_utils.py +126 -0
- code_puppy/tools/agent_tools.py +131 -70
- code_puppy/tools/browser/browser_control.py +10 -14
- code_puppy/tools/browser/browser_interactions.py +20 -28
- code_puppy/tools/browser/browser_locators.py +27 -29
- code_puppy/tools/browser/browser_navigation.py +9 -9
- code_puppy/tools/browser/browser_screenshot.py +12 -14
- code_puppy/tools/browser/browser_scripts.py +17 -29
- code_puppy/tools/browser/browser_workflows.py +24 -25
- code_puppy/tools/browser/camoufox_manager.py +22 -26
- code_puppy/tools/command_runner.py +410 -88
- code_puppy/tools/common.py +51 -38
- code_puppy/tools/file_modifications.py +98 -24
- code_puppy/tools/file_operations.py +113 -202
- code_puppy/version_checker.py +28 -13
- {code_puppy-0.0.287.data → code_puppy-0.0.323.data}/data/code_puppy/models.json +19 -5
- {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/METADATA +3 -8
- code_puppy-0.0.323.dist-info/RECORD +168 -0
- code_puppy/tui_state.py +0 -55
- code_puppy-0.0.287.dist-info/RECORD +0 -153
- {code_puppy-0.0.287.data → code_puppy-0.0.323.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/licenses/LICENSE +0 -0
code_puppy/tools/common.py
CHANGED
|
@@ -29,7 +29,13 @@ except ImportError:
|
|
|
29
29
|
|
|
30
30
|
# Import our queue-based console system
|
|
31
31
|
try:
|
|
32
|
-
from code_puppy.messaging import
|
|
32
|
+
from code_puppy.messaging import (
|
|
33
|
+
emit_error,
|
|
34
|
+
emit_info,
|
|
35
|
+
emit_success,
|
|
36
|
+
emit_warning,
|
|
37
|
+
get_queue_console,
|
|
38
|
+
)
|
|
33
39
|
|
|
34
40
|
# Use queue console by default, but allow fallback
|
|
35
41
|
NO_COLOR = bool(int(os.environ.get("CODE_PUPPY_NO_COLOR", "0")))
|
|
@@ -42,6 +48,19 @@ except ImportError:
|
|
|
42
48
|
NO_COLOR = bool(int(os.environ.get("CODE_PUPPY_NO_COLOR", "0")))
|
|
43
49
|
console = Console(no_color=NO_COLOR)
|
|
44
50
|
|
|
51
|
+
# Provide fallback emit functions
|
|
52
|
+
def emit_error(msg: str) -> None:
|
|
53
|
+
console.print(f"[bold red]{msg}[/bold red]")
|
|
54
|
+
|
|
55
|
+
def emit_info(msg: str) -> None:
|
|
56
|
+
console.print(msg)
|
|
57
|
+
|
|
58
|
+
def emit_success(msg: str) -> None:
|
|
59
|
+
console.print(f"[bold green]{msg}[/bold green]")
|
|
60
|
+
|
|
61
|
+
def emit_warning(msg: str) -> None:
|
|
62
|
+
console.print(f"[bold yellow]{msg}[/bold yellow]")
|
|
63
|
+
|
|
45
64
|
|
|
46
65
|
def should_suppress_browser() -> bool:
|
|
47
66
|
"""Check if browsers should be suppressed (headless mode).
|
|
@@ -782,9 +801,7 @@ def format_diff_with_colors(diff_text: str) -> Text:
|
|
|
782
801
|
|
|
783
802
|
# Always use beautiful syntax highlighting!
|
|
784
803
|
if not PYGMENTS_AVAILABLE:
|
|
785
|
-
|
|
786
|
-
"[yellow]Warning: Pygments not available, diffs will look plain[/yellow]"
|
|
787
|
-
)
|
|
804
|
+
emit_warning("Pygments not available, diffs will look plain")
|
|
788
805
|
# Return plain text as fallback
|
|
789
806
|
return Text(diff_text)
|
|
790
807
|
|
|
@@ -1088,10 +1105,10 @@ def get_user_approval(
|
|
|
1088
1105
|
time.sleep(0.3) # Let spinners fully stop
|
|
1089
1106
|
|
|
1090
1107
|
# Display panel
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1108
|
+
local_console = Console()
|
|
1109
|
+
emit_info("")
|
|
1110
|
+
local_console.print(panel)
|
|
1111
|
+
emit_info("")
|
|
1095
1112
|
|
|
1096
1113
|
# Flush and buffer before selector
|
|
1097
1114
|
sys.stdout.flush()
|
|
@@ -1122,8 +1139,8 @@ def get_user_approval(
|
|
|
1122
1139
|
else:
|
|
1123
1140
|
# User wants to provide feedback
|
|
1124
1141
|
confirmed = False
|
|
1125
|
-
|
|
1126
|
-
|
|
1142
|
+
emit_info("")
|
|
1143
|
+
emit_info(f"Tell {puppy_name} what to change:")
|
|
1127
1144
|
user_feedback = Prompt.ask(
|
|
1128
1145
|
"[bold green]➤[/bold green]",
|
|
1129
1146
|
default="",
|
|
@@ -1133,7 +1150,7 @@ def get_user_approval(
|
|
|
1133
1150
|
user_feedback = None
|
|
1134
1151
|
|
|
1135
1152
|
except (KeyboardInterrupt, EOFError):
|
|
1136
|
-
|
|
1153
|
+
emit_error("Cancelled by user")
|
|
1137
1154
|
confirmed = False
|
|
1138
1155
|
|
|
1139
1156
|
finally:
|
|
@@ -1142,9 +1159,9 @@ def get_user_approval(
|
|
|
1142
1159
|
# Force Rich console to reset display state to prevent artifacts
|
|
1143
1160
|
try:
|
|
1144
1161
|
# Clear Rich's internal display state to prevent artifacts
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1162
|
+
local_console.file.write("\r") # Return to start of line
|
|
1163
|
+
local_console.file.write("\x1b[K") # Clear current line
|
|
1164
|
+
local_console.file.flush()
|
|
1148
1165
|
except Exception:
|
|
1149
1166
|
pass
|
|
1150
1167
|
|
|
@@ -1153,17 +1170,15 @@ def get_user_approval(
|
|
|
1153
1170
|
sys.stderr.flush()
|
|
1154
1171
|
|
|
1155
1172
|
# Show result BEFORE resuming spinners (no puppy litter!)
|
|
1156
|
-
|
|
1173
|
+
emit_info("")
|
|
1157
1174
|
if not confirmed:
|
|
1158
1175
|
if user_feedback:
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
f'[bold yellow]📝 Telling {puppy_name}: "{user_feedback}"[/bold yellow]'
|
|
1162
|
-
)
|
|
1176
|
+
emit_error("Rejected with feedback!")
|
|
1177
|
+
emit_warning(f'Telling {puppy_name}: "{user_feedback}"')
|
|
1163
1178
|
else:
|
|
1164
|
-
|
|
1179
|
+
emit_error("Rejected.")
|
|
1165
1180
|
else:
|
|
1166
|
-
|
|
1181
|
+
emit_success("Approved!")
|
|
1167
1182
|
|
|
1168
1183
|
# NOW resume spinners after showing the result
|
|
1169
1184
|
try:
|
|
@@ -1258,10 +1273,10 @@ async def get_user_approval_async(
|
|
|
1258
1273
|
await asyncio.sleep(0.3) # Let spinners fully stop
|
|
1259
1274
|
|
|
1260
1275
|
# Display panel
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1276
|
+
local_console = Console()
|
|
1277
|
+
emit_info("")
|
|
1278
|
+
local_console.print(panel)
|
|
1279
|
+
emit_info("")
|
|
1265
1280
|
|
|
1266
1281
|
# Flush and buffer before selector
|
|
1267
1282
|
sys.stdout.flush()
|
|
@@ -1292,8 +1307,8 @@ async def get_user_approval_async(
|
|
|
1292
1307
|
else:
|
|
1293
1308
|
# User wants to provide feedback
|
|
1294
1309
|
confirmed = False
|
|
1295
|
-
|
|
1296
|
-
|
|
1310
|
+
emit_info("")
|
|
1311
|
+
emit_info(f"Tell {puppy_name} what to change:")
|
|
1297
1312
|
user_feedback = Prompt.ask(
|
|
1298
1313
|
"[bold green]➤[/bold green]",
|
|
1299
1314
|
default="",
|
|
@@ -1303,7 +1318,7 @@ async def get_user_approval_async(
|
|
|
1303
1318
|
user_feedback = None
|
|
1304
1319
|
|
|
1305
1320
|
except (KeyboardInterrupt, EOFError):
|
|
1306
|
-
|
|
1321
|
+
emit_error("Cancelled by user")
|
|
1307
1322
|
confirmed = False
|
|
1308
1323
|
|
|
1309
1324
|
finally:
|
|
@@ -1312,9 +1327,9 @@ async def get_user_approval_async(
|
|
|
1312
1327
|
# Force Rich console to reset display state to prevent artifacts
|
|
1313
1328
|
try:
|
|
1314
1329
|
# Clear Rich's internal display state to prevent artifacts
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1330
|
+
local_console.file.write("\r") # Return to start of line
|
|
1331
|
+
local_console.file.write("\x1b[K") # Clear current line
|
|
1332
|
+
local_console.file.flush()
|
|
1318
1333
|
except Exception:
|
|
1319
1334
|
pass
|
|
1320
1335
|
|
|
@@ -1323,17 +1338,15 @@ async def get_user_approval_async(
|
|
|
1323
1338
|
sys.stderr.flush()
|
|
1324
1339
|
|
|
1325
1340
|
# Show result BEFORE resuming spinners (no puppy litter!)
|
|
1326
|
-
|
|
1341
|
+
emit_info("")
|
|
1327
1342
|
if not confirmed:
|
|
1328
1343
|
if user_feedback:
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
f'[bold yellow]📝 Telling {puppy_name}: "{user_feedback}"[/bold yellow]'
|
|
1332
|
-
)
|
|
1344
|
+
emit_error("Rejected with feedback!")
|
|
1345
|
+
emit_warning(f'Telling {puppy_name}: "{user_feedback}"')
|
|
1333
1346
|
else:
|
|
1334
|
-
|
|
1347
|
+
emit_error("Rejected.")
|
|
1335
1348
|
else:
|
|
1336
|
-
|
|
1349
|
+
emit_success("Approved!")
|
|
1337
1350
|
|
|
1338
1351
|
# NOW resume spinners after showing the result
|
|
1339
1352
|
try:
|
|
@@ -21,9 +21,14 @@ from pydantic import BaseModel
|
|
|
21
21
|
from pydantic_ai import RunContext
|
|
22
22
|
|
|
23
23
|
from code_puppy.callbacks import on_delete_file, on_edit_file
|
|
24
|
-
from code_puppy.messaging import
|
|
24
|
+
from code_puppy.messaging import ( # Structured messaging types
|
|
25
|
+
DiffLine,
|
|
26
|
+
DiffMessage,
|
|
27
|
+
emit_error,
|
|
28
|
+
emit_warning,
|
|
29
|
+
get_message_bus,
|
|
30
|
+
)
|
|
25
31
|
from code_puppy.tools.common import _find_best_window, generate_group_id
|
|
26
|
-
from code_puppy.tools.common import format_diff_with_colors as _colorize_diff
|
|
27
32
|
|
|
28
33
|
|
|
29
34
|
def _create_rejection_response(file_path: str) -> Dict[str, Any]:
|
|
@@ -91,10 +96,78 @@ class ContentPayload(BaseModel):
|
|
|
91
96
|
EditFilePayload = Union[DeleteSnippetPayload, ReplacementsPayload, ContentPayload]
|
|
92
97
|
|
|
93
98
|
|
|
94
|
-
def
|
|
95
|
-
"""
|
|
99
|
+
def _parse_diff_lines(diff_text: str) -> List[DiffLine]:
|
|
100
|
+
"""Parse unified diff text into structured DiffLine objects.
|
|
96
101
|
|
|
97
|
-
|
|
102
|
+
Args:
|
|
103
|
+
diff_text: Raw unified diff text
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
List of DiffLine objects with line numbers and types
|
|
107
|
+
"""
|
|
108
|
+
if not diff_text or not diff_text.strip():
|
|
109
|
+
return []
|
|
110
|
+
|
|
111
|
+
diff_lines = []
|
|
112
|
+
line_number = 0
|
|
113
|
+
|
|
114
|
+
for line in diff_text.splitlines():
|
|
115
|
+
# Determine line type based on diff markers
|
|
116
|
+
if line.startswith("+") and not line.startswith("+++"):
|
|
117
|
+
line_type = "add"
|
|
118
|
+
line_number += 1
|
|
119
|
+
content = line[1:] # Remove the + prefix
|
|
120
|
+
elif line.startswith("-") and not line.startswith("---"):
|
|
121
|
+
line_type = "remove"
|
|
122
|
+
line_number += 1
|
|
123
|
+
content = line[1:] # Remove the - prefix
|
|
124
|
+
elif line.startswith("@@"):
|
|
125
|
+
# Parse hunk header to get line number
|
|
126
|
+
# Format: @@ -start,count +start,count @@
|
|
127
|
+
import re
|
|
128
|
+
|
|
129
|
+
match = re.search(r"@@ -\d+(?:,\d+)? \+(\d+)", line)
|
|
130
|
+
if match:
|
|
131
|
+
line_number = (
|
|
132
|
+
int(match.group(1)) - 1
|
|
133
|
+
) # Will be incremented on next line
|
|
134
|
+
line_type = "context"
|
|
135
|
+
content = line
|
|
136
|
+
elif line.startswith("---") or line.startswith("+++"):
|
|
137
|
+
# File headers - treat as context
|
|
138
|
+
line_type = "context"
|
|
139
|
+
content = line
|
|
140
|
+
else:
|
|
141
|
+
line_type = "context"
|
|
142
|
+
line_number += 1
|
|
143
|
+
content = line
|
|
144
|
+
|
|
145
|
+
diff_lines.append(
|
|
146
|
+
DiffLine(
|
|
147
|
+
line_number=max(1, line_number),
|
|
148
|
+
type=line_type,
|
|
149
|
+
content=content,
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return diff_lines
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _emit_diff_message(
|
|
157
|
+
file_path: str,
|
|
158
|
+
operation: str,
|
|
159
|
+
diff_text: str,
|
|
160
|
+
old_content: str | None = None,
|
|
161
|
+
new_content: str | None = None,
|
|
162
|
+
) -> None:
|
|
163
|
+
"""Emit a structured DiffMessage for UI display.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
file_path: Path to the file being modified
|
|
167
|
+
operation: One of 'create', 'modify', 'delete'
|
|
168
|
+
diff_text: Raw unified diff text
|
|
169
|
+
old_content: Original file content (optional)
|
|
170
|
+
new_content: New file content (optional)
|
|
98
171
|
"""
|
|
99
172
|
# Check if diff was already shown during permission prompt
|
|
100
173
|
try:
|
|
@@ -108,22 +181,21 @@ def _print_diff(diff_text: str, message_group: str | None = None) -> None:
|
|
|
108
181
|
clear_diff_shown_flag()
|
|
109
182
|
return
|
|
110
183
|
except ImportError:
|
|
111
|
-
pass # Permission handler not available,
|
|
112
|
-
|
|
113
|
-
emit_info(
|
|
114
|
-
"[bold cyan]\n── DIFF ────────────────────────────────────────────────[/bold cyan]",
|
|
115
|
-
message_group=message_group,
|
|
116
|
-
)
|
|
184
|
+
pass # Permission handler not available, emit anyway
|
|
117
185
|
|
|
118
|
-
|
|
119
|
-
|
|
186
|
+
if not diff_text or not diff_text.strip():
|
|
187
|
+
return
|
|
120
188
|
|
|
121
|
-
|
|
189
|
+
diff_lines = _parse_diff_lines(diff_text)
|
|
122
190
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
191
|
+
diff_msg = DiffMessage(
|
|
192
|
+
path=file_path,
|
|
193
|
+
operation=operation,
|
|
194
|
+
old_content=old_content,
|
|
195
|
+
new_content=new_content,
|
|
196
|
+
diff_lines=diff_lines,
|
|
126
197
|
)
|
|
198
|
+
get_message_bus().emit(diff_msg)
|
|
127
199
|
|
|
128
200
|
|
|
129
201
|
def _log_error(
|
|
@@ -339,7 +411,7 @@ def delete_snippet_from_file(
|
|
|
339
411
|
)
|
|
340
412
|
diff = res.get("diff", "")
|
|
341
413
|
if diff:
|
|
342
|
-
|
|
414
|
+
_emit_diff_message(file_path, "modify", diff)
|
|
343
415
|
return res
|
|
344
416
|
|
|
345
417
|
|
|
@@ -369,7 +441,9 @@ def write_to_file(
|
|
|
369
441
|
)
|
|
370
442
|
diff = res.get("diff", "")
|
|
371
443
|
if diff:
|
|
372
|
-
|
|
444
|
+
# Determine operation type based on whether file existed
|
|
445
|
+
operation = "modify" if overwrite else "create"
|
|
446
|
+
_emit_diff_message(path, operation, diff, new_content=content)
|
|
373
447
|
return res
|
|
374
448
|
|
|
375
449
|
|
|
@@ -396,7 +470,7 @@ def replace_in_file(
|
|
|
396
470
|
res = _replace_in_file(context, path, replacements, message_group=message_group)
|
|
397
471
|
diff = res.get("diff", "")
|
|
398
472
|
if diff:
|
|
399
|
-
|
|
473
|
+
_emit_diff_message(path, "modify", diff)
|
|
400
474
|
return res
|
|
401
475
|
|
|
402
476
|
|
|
@@ -440,9 +514,6 @@ def _edit_file(
|
|
|
440
514
|
if group_id is None:
|
|
441
515
|
group_id = generate_group_id("edit_file", file_path)
|
|
442
516
|
|
|
443
|
-
emit_info(
|
|
444
|
-
"\n[bold white on blue] EDIT FILE [/bold white on blue]", message_group=group_id
|
|
445
|
-
)
|
|
446
517
|
try:
|
|
447
518
|
if isinstance(payload, DeleteSnippetPayload):
|
|
448
519
|
return delete_snippet_from_file(
|
|
@@ -548,7 +619,10 @@ def _delete_file(
|
|
|
548
619
|
except Exception as exc:
|
|
549
620
|
_log_error("Unhandled exception in delete_file", exc)
|
|
550
621
|
res = {"error": str(exc), "diff": ""}
|
|
551
|
-
|
|
622
|
+
|
|
623
|
+
diff = res.get("diff", "")
|
|
624
|
+
if diff:
|
|
625
|
+
_emit_diff_message(file_path, "delete", diff)
|
|
552
626
|
return res
|
|
553
627
|
|
|
554
628
|
|