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
|
@@ -8,11 +8,11 @@ appropriately for their respective interfaces.
|
|
|
8
8
|
import asyncio
|
|
9
9
|
import threading
|
|
10
10
|
from abc import ABC, abstractmethod
|
|
11
|
-
from io import StringIO
|
|
12
11
|
from typing import Optional
|
|
13
12
|
|
|
14
13
|
from rich.console import Console
|
|
15
14
|
from rich.markdown import Markdown
|
|
15
|
+
from rich.markup import escape as escape_rich_markup
|
|
16
16
|
|
|
17
17
|
from .message_queue import MessageQueue, MessageType, UIMessage
|
|
18
18
|
|
|
@@ -64,7 +64,10 @@ class MessageRenderer(ABC):
|
|
|
64
64
|
break
|
|
65
65
|
except Exception as e:
|
|
66
66
|
# Log error but continue processing
|
|
67
|
-
|
|
67
|
+
# Note: Using sys.stderr - can't use messaging in renderer
|
|
68
|
+
import sys
|
|
69
|
+
|
|
70
|
+
sys.stderr.write(f"Error rendering message: {e}\n")
|
|
68
71
|
|
|
69
72
|
|
|
70
73
|
class InteractiveRenderer(MessageRenderer):
|
|
@@ -103,10 +106,18 @@ class InteractiveRenderer(MessageRenderer):
|
|
|
103
106
|
# Special handling for agent responses - they'll be rendered as markdown
|
|
104
107
|
style = None
|
|
105
108
|
elif message.type == MessageType.SYSTEM:
|
|
106
|
-
style =
|
|
109
|
+
style = "dim"
|
|
107
110
|
else:
|
|
108
111
|
style = None
|
|
109
112
|
|
|
113
|
+
# Make version messages dim regardless of message type
|
|
114
|
+
if isinstance(message.content, str):
|
|
115
|
+
if (
|
|
116
|
+
"Current version:" in message.content
|
|
117
|
+
or "Latest version:" in message.content
|
|
118
|
+
):
|
|
119
|
+
style = "dim"
|
|
120
|
+
|
|
110
121
|
# Render the content
|
|
111
122
|
if isinstance(message.content, str):
|
|
112
123
|
if message.type == MessageType.AGENT_RESPONSE:
|
|
@@ -116,11 +127,15 @@ class InteractiveRenderer(MessageRenderer):
|
|
|
116
127
|
self.console.print(markdown)
|
|
117
128
|
except Exception:
|
|
118
129
|
# Fallback to plain text if markdown parsing fails
|
|
119
|
-
|
|
130
|
+
safe_content = escape_rich_markup(message.content)
|
|
131
|
+
self.console.print(safe_content)
|
|
120
132
|
elif style:
|
|
121
|
-
|
|
133
|
+
# Escape Rich markup to prevent crashes from malformed tags
|
|
134
|
+
safe_content = escape_rich_markup(message.content)
|
|
135
|
+
self.console.print(safe_content, style=style)
|
|
122
136
|
else:
|
|
123
|
-
|
|
137
|
+
safe_content = escape_rich_markup(message.content)
|
|
138
|
+
self.console.print(safe_content)
|
|
124
139
|
else:
|
|
125
140
|
# For complex Rich objects (Tables, Markdown, Text, etc.)
|
|
126
141
|
self.console.print(message.content)
|
|
@@ -135,139 +150,12 @@ class InteractiveRenderer(MessageRenderer):
|
|
|
135
150
|
# This renderer is not currently used in practice, but if it were:
|
|
136
151
|
# We would need async input handling here
|
|
137
152
|
# For now, just render as a system message
|
|
138
|
-
|
|
153
|
+
safe_content = escape_rich_markup(str(message.content))
|
|
154
|
+
self.console.print(f"[bold cyan]INPUT REQUESTED:[/bold cyan] {safe_content}")
|
|
139
155
|
if hasattr(self.console.file, "flush"):
|
|
140
156
|
self.console.file.flush()
|
|
141
157
|
|
|
142
158
|
|
|
143
|
-
class TUIRenderer(MessageRenderer):
|
|
144
|
-
"""Renderer for TUI mode that adds messages to the chat view."""
|
|
145
|
-
|
|
146
|
-
def __init__(self, queue: MessageQueue, tui_app=None):
|
|
147
|
-
super().__init__(queue)
|
|
148
|
-
self.tui_app = tui_app
|
|
149
|
-
|
|
150
|
-
def set_tui_app(self, app):
|
|
151
|
-
"""Set the TUI app reference."""
|
|
152
|
-
self.tui_app = app
|
|
153
|
-
|
|
154
|
-
async def render_message(self, message: UIMessage):
|
|
155
|
-
"""Render a message in the TUI chat view."""
|
|
156
|
-
if not self.tui_app:
|
|
157
|
-
return
|
|
158
|
-
|
|
159
|
-
# Handle human input requests
|
|
160
|
-
if message.type == MessageType.HUMAN_INPUT_REQUEST:
|
|
161
|
-
await self._handle_human_input_request(message)
|
|
162
|
-
return
|
|
163
|
-
|
|
164
|
-
# Extract group_id from message metadata (fixing the key name)
|
|
165
|
-
group_id = message.metadata.get("message_group") if message.metadata else None
|
|
166
|
-
|
|
167
|
-
# For INFO messages with Rich objects (like Markdown), preserve them for proper rendering
|
|
168
|
-
if message.type == MessageType.INFO and hasattr(
|
|
169
|
-
message.content, "__rich_console__"
|
|
170
|
-
):
|
|
171
|
-
# Pass the Rich object directly to maintain markdown formatting
|
|
172
|
-
self.tui_app.add_system_message_rich(
|
|
173
|
-
message.content, message_group=group_id
|
|
174
|
-
)
|
|
175
|
-
return
|
|
176
|
-
|
|
177
|
-
# Convert content to string for TUI display (for all other cases)
|
|
178
|
-
if hasattr(message.content, "__rich_console__"):
|
|
179
|
-
# For Rich objects, render to plain text using a Console
|
|
180
|
-
string_io = StringIO()
|
|
181
|
-
# Use markup=False to prevent interpretation of square brackets as markup
|
|
182
|
-
temp_console = Console(
|
|
183
|
-
file=string_io, width=80, legacy_windows=False, markup=False
|
|
184
|
-
)
|
|
185
|
-
temp_console.print(message.content)
|
|
186
|
-
content_str = string_io.getvalue().rstrip("\n")
|
|
187
|
-
else:
|
|
188
|
-
content_str = str(message.content)
|
|
189
|
-
|
|
190
|
-
# Map message types to TUI message types - ALL get group_id now
|
|
191
|
-
if message.type in (MessageType.ERROR,):
|
|
192
|
-
self.tui_app.add_error_message(content_str, message_group=group_id)
|
|
193
|
-
elif message.type in (
|
|
194
|
-
MessageType.SYSTEM,
|
|
195
|
-
MessageType.INFO,
|
|
196
|
-
MessageType.WARNING,
|
|
197
|
-
MessageType.SUCCESS,
|
|
198
|
-
):
|
|
199
|
-
self.tui_app.add_system_message(content_str, message_group=group_id)
|
|
200
|
-
elif message.type == MessageType.AGENT_REASONING:
|
|
201
|
-
# Agent reasoning messages should use the dedicated method
|
|
202
|
-
self.tui_app.add_agent_reasoning_message(
|
|
203
|
-
content_str, message_group=group_id
|
|
204
|
-
)
|
|
205
|
-
elif message.type == MessageType.PLANNED_NEXT_STEPS:
|
|
206
|
-
# Agent reasoning messages should use the dedicated method
|
|
207
|
-
self.tui_app.add_planned_next_steps_message(
|
|
208
|
-
content_str, message_group=group_id
|
|
209
|
-
)
|
|
210
|
-
elif message.type in (
|
|
211
|
-
MessageType.TOOL_OUTPUT,
|
|
212
|
-
MessageType.COMMAND_OUTPUT,
|
|
213
|
-
MessageType.AGENT_RESPONSE,
|
|
214
|
-
):
|
|
215
|
-
# These are typically agent/tool outputs
|
|
216
|
-
self.tui_app.add_agent_message(content_str, message_group=group_id)
|
|
217
|
-
else:
|
|
218
|
-
# Default to system message
|
|
219
|
-
self.tui_app.add_system_message(content_str, message_group=group_id)
|
|
220
|
-
|
|
221
|
-
async def _handle_human_input_request(self, message: UIMessage):
|
|
222
|
-
"""Handle a human input request in TUI mode."""
|
|
223
|
-
try:
|
|
224
|
-
# Check if tui_app is available
|
|
225
|
-
if not self.tui_app:
|
|
226
|
-
prompt_id = (
|
|
227
|
-
message.metadata.get("prompt_id") if message.metadata else None
|
|
228
|
-
)
|
|
229
|
-
if prompt_id:
|
|
230
|
-
from code_puppy.messaging import provide_prompt_response
|
|
231
|
-
|
|
232
|
-
provide_prompt_response(prompt_id, "")
|
|
233
|
-
return
|
|
234
|
-
|
|
235
|
-
prompt_id = message.metadata.get("prompt_id") if message.metadata else None
|
|
236
|
-
if not prompt_id:
|
|
237
|
-
self.tui_app.add_error_message("Error: Invalid human input request")
|
|
238
|
-
return
|
|
239
|
-
|
|
240
|
-
# For now, use a simple fallback instead of modal to avoid crashes
|
|
241
|
-
self.tui_app.add_system_message(
|
|
242
|
-
f"[yellow]INPUT NEEDED:[/yellow] {str(message.content)}"
|
|
243
|
-
)
|
|
244
|
-
self.tui_app.add_system_message(
|
|
245
|
-
"[dim]This would normally show a modal, but using fallback to prevent crashes[/dim]"
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
# Provide empty response for now to unblock the waiting thread
|
|
249
|
-
from code_puppy.messaging import provide_prompt_response
|
|
250
|
-
|
|
251
|
-
provide_prompt_response(prompt_id, "")
|
|
252
|
-
|
|
253
|
-
except Exception as e:
|
|
254
|
-
print(f"Exception in _handle_human_input_request: {e}")
|
|
255
|
-
import traceback
|
|
256
|
-
|
|
257
|
-
traceback.print_exc()
|
|
258
|
-
# Last resort - provide empty response to prevent hanging
|
|
259
|
-
try:
|
|
260
|
-
prompt_id = (
|
|
261
|
-
message.metadata.get("prompt_id") if message.metadata else None
|
|
262
|
-
)
|
|
263
|
-
if prompt_id:
|
|
264
|
-
from code_puppy.messaging import provide_prompt_response
|
|
265
|
-
|
|
266
|
-
provide_prompt_response(prompt_id, "")
|
|
267
|
-
except Exception:
|
|
268
|
-
pass # Can't do anything more
|
|
269
|
-
|
|
270
|
-
|
|
271
159
|
class SynchronousInteractiveRenderer:
|
|
272
160
|
"""
|
|
273
161
|
Synchronous renderer for interactive mode that doesn't require async.
|
|
@@ -350,10 +238,18 @@ class SynchronousInteractiveRenderer:
|
|
|
350
238
|
# Special handling for agent responses - they'll be rendered as markdown
|
|
351
239
|
style = None
|
|
352
240
|
elif message.type == MessageType.SYSTEM:
|
|
353
|
-
style =
|
|
241
|
+
style = "dim"
|
|
354
242
|
else:
|
|
355
243
|
style = None
|
|
356
244
|
|
|
245
|
+
# Make version messages dim regardless of message type
|
|
246
|
+
if isinstance(message.content, str):
|
|
247
|
+
if (
|
|
248
|
+
"Current version:" in message.content
|
|
249
|
+
or "Latest version:" in message.content
|
|
250
|
+
):
|
|
251
|
+
style = "dim"
|
|
252
|
+
|
|
357
253
|
# Render the content
|
|
358
254
|
if isinstance(message.content, str):
|
|
359
255
|
if message.type == MessageType.AGENT_RESPONSE:
|
|
@@ -363,11 +259,16 @@ class SynchronousInteractiveRenderer:
|
|
|
363
259
|
self.console.print(markdown)
|
|
364
260
|
except Exception:
|
|
365
261
|
# Fallback to plain text if markdown parsing fails
|
|
366
|
-
|
|
262
|
+
safe_content = escape_rich_markup(message.content)
|
|
263
|
+
self.console.print(safe_content)
|
|
367
264
|
elif style:
|
|
368
|
-
|
|
265
|
+
# Escape Rich markup to prevent crashes from malformed tags
|
|
266
|
+
# in shell output or other user-provided content
|
|
267
|
+
safe_content = escape_rich_markup(message.content)
|
|
268
|
+
self.console.print(safe_content, style=style)
|
|
369
269
|
else:
|
|
370
|
-
|
|
270
|
+
safe_content = escape_rich_markup(message.content)
|
|
271
|
+
self.console.print(safe_content)
|
|
371
272
|
else:
|
|
372
273
|
# For complex Rich objects (Tables, Markdown, Text, etc.)
|
|
373
274
|
self.console.print(message.content)
|
|
@@ -386,8 +287,9 @@ class SynchronousInteractiveRenderer:
|
|
|
386
287
|
)
|
|
387
288
|
return
|
|
388
289
|
|
|
389
|
-
# Display the prompt
|
|
390
|
-
|
|
290
|
+
# Display the prompt - escape to prevent markup injection
|
|
291
|
+
safe_content = escape_rich_markup(str(message.content))
|
|
292
|
+
self.console.print(f"[bold cyan]{safe_content}[/bold cyan]")
|
|
391
293
|
if hasattr(self.console.file, "flush"):
|
|
392
294
|
self.console.file.flush()
|
|
393
295
|
|