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.
Files changed (110) hide show
  1. code_puppy/__init__.py +3 -1
  2. code_puppy/agents/agent_code_puppy.py +5 -4
  3. code_puppy/agents/agent_creator_agent.py +22 -18
  4. code_puppy/agents/agent_manager.py +2 -2
  5. code_puppy/agents/base_agent.py +496 -102
  6. code_puppy/callbacks.py +8 -0
  7. code_puppy/chatgpt_codex_client.py +283 -0
  8. code_puppy/cli_runner.py +795 -0
  9. code_puppy/command_line/add_model_menu.py +19 -16
  10. code_puppy/command_line/attachments.py +10 -5
  11. code_puppy/command_line/autosave_menu.py +269 -41
  12. code_puppy/command_line/colors_menu.py +515 -0
  13. code_puppy/command_line/command_handler.py +10 -24
  14. code_puppy/command_line/config_commands.py +106 -25
  15. code_puppy/command_line/core_commands.py +32 -20
  16. code_puppy/command_line/mcp/add_command.py +3 -16
  17. code_puppy/command_line/mcp/base.py +0 -3
  18. code_puppy/command_line/mcp/catalog_server_installer.py +15 -15
  19. code_puppy/command_line/mcp/custom_server_form.py +66 -5
  20. code_puppy/command_line/mcp/custom_server_installer.py +17 -17
  21. code_puppy/command_line/mcp/edit_command.py +15 -22
  22. code_puppy/command_line/mcp/handler.py +7 -2
  23. code_puppy/command_line/mcp/help_command.py +2 -2
  24. code_puppy/command_line/mcp/install_command.py +10 -14
  25. code_puppy/command_line/mcp/install_menu.py +2 -6
  26. code_puppy/command_line/mcp/list_command.py +2 -2
  27. code_puppy/command_line/mcp/logs_command.py +174 -65
  28. code_puppy/command_line/mcp/remove_command.py +2 -2
  29. code_puppy/command_line/mcp/restart_command.py +7 -2
  30. code_puppy/command_line/mcp/search_command.py +16 -10
  31. code_puppy/command_line/mcp/start_all_command.py +16 -6
  32. code_puppy/command_line/mcp/start_command.py +12 -10
  33. code_puppy/command_line/mcp/status_command.py +4 -5
  34. code_puppy/command_line/mcp/stop_all_command.py +5 -1
  35. code_puppy/command_line/mcp/stop_command.py +6 -4
  36. code_puppy/command_line/mcp/test_command.py +2 -2
  37. code_puppy/command_line/mcp/wizard_utils.py +20 -16
  38. code_puppy/command_line/model_settings_menu.py +53 -7
  39. code_puppy/command_line/motd.py +1 -1
  40. code_puppy/command_line/pin_command_completion.py +82 -7
  41. code_puppy/command_line/prompt_toolkit_completion.py +32 -9
  42. code_puppy/command_line/session_commands.py +11 -4
  43. code_puppy/config.py +217 -53
  44. code_puppy/error_logging.py +118 -0
  45. code_puppy/gemini_code_assist.py +385 -0
  46. code_puppy/keymap.py +126 -0
  47. code_puppy/main.py +5 -745
  48. code_puppy/mcp_/__init__.py +17 -0
  49. code_puppy/mcp_/blocking_startup.py +63 -36
  50. code_puppy/mcp_/captured_stdio_server.py +1 -1
  51. code_puppy/mcp_/config_wizard.py +4 -4
  52. code_puppy/mcp_/dashboard.py +15 -6
  53. code_puppy/mcp_/managed_server.py +25 -5
  54. code_puppy/mcp_/manager.py +65 -0
  55. code_puppy/mcp_/mcp_logs.py +224 -0
  56. code_puppy/mcp_/registry.py +6 -6
  57. code_puppy/messaging/__init__.py +184 -2
  58. code_puppy/messaging/bus.py +610 -0
  59. code_puppy/messaging/commands.py +167 -0
  60. code_puppy/messaging/markdown_patches.py +57 -0
  61. code_puppy/messaging/message_queue.py +3 -3
  62. code_puppy/messaging/messages.py +470 -0
  63. code_puppy/messaging/renderers.py +43 -141
  64. code_puppy/messaging/rich_renderer.py +900 -0
  65. code_puppy/messaging/spinner/console_spinner.py +39 -2
  66. code_puppy/model_factory.py +292 -53
  67. code_puppy/model_utils.py +57 -48
  68. code_puppy/models.json +19 -5
  69. code_puppy/plugins/__init__.py +152 -10
  70. code_puppy/plugins/chatgpt_oauth/config.py +20 -12
  71. code_puppy/plugins/chatgpt_oauth/oauth_flow.py +5 -6
  72. code_puppy/plugins/chatgpt_oauth/register_callbacks.py +3 -3
  73. code_puppy/plugins/chatgpt_oauth/test_plugin.py +30 -13
  74. code_puppy/plugins/chatgpt_oauth/utils.py +180 -65
  75. code_puppy/plugins/claude_code_oauth/config.py +15 -11
  76. code_puppy/plugins/claude_code_oauth/register_callbacks.py +28 -0
  77. code_puppy/plugins/claude_code_oauth/utils.py +6 -1
  78. code_puppy/plugins/example_custom_command/register_callbacks.py +2 -2
  79. code_puppy/plugins/oauth_puppy_html.py +3 -0
  80. code_puppy/plugins/shell_safety/agent_shell_safety.py +1 -134
  81. code_puppy/plugins/shell_safety/command_cache.py +156 -0
  82. code_puppy/plugins/shell_safety/register_callbacks.py +77 -3
  83. code_puppy/prompts/codex_system_prompt.md +310 -0
  84. code_puppy/pydantic_patches.py +131 -0
  85. code_puppy/session_storage.py +2 -1
  86. code_puppy/status_display.py +7 -5
  87. code_puppy/terminal_utils.py +126 -0
  88. code_puppy/tools/agent_tools.py +131 -70
  89. code_puppy/tools/browser/browser_control.py +10 -14
  90. code_puppy/tools/browser/browser_interactions.py +20 -28
  91. code_puppy/tools/browser/browser_locators.py +27 -29
  92. code_puppy/tools/browser/browser_navigation.py +9 -9
  93. code_puppy/tools/browser/browser_screenshot.py +12 -14
  94. code_puppy/tools/browser/browser_scripts.py +17 -29
  95. code_puppy/tools/browser/browser_workflows.py +24 -25
  96. code_puppy/tools/browser/camoufox_manager.py +22 -26
  97. code_puppy/tools/command_runner.py +410 -88
  98. code_puppy/tools/common.py +51 -38
  99. code_puppy/tools/file_modifications.py +98 -24
  100. code_puppy/tools/file_operations.py +113 -202
  101. code_puppy/version_checker.py +28 -13
  102. {code_puppy-0.0.287.data → code_puppy-0.0.323.data}/data/code_puppy/models.json +19 -5
  103. {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/METADATA +3 -8
  104. code_puppy-0.0.323.dist-info/RECORD +168 -0
  105. code_puppy/tui_state.py +0 -55
  106. code_puppy-0.0.287.dist-info/RECORD +0 -153
  107. {code_puppy-0.0.287.data → code_puppy-0.0.323.data}/data/code_puppy/models_dev_api.json +0 -0
  108. {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/WHEEL +0 -0
  109. {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/entry_points.txt +0 -0
  110. {code_puppy-0.0.287.dist-info → code_puppy-0.0.323.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,167 @@
1
+ """Command models for User → Agent communication in Code Puppy's messaging system.
2
+
3
+ This module defines Pydantic models for commands that flow FROM the UI TO the Agent.
4
+ This is the opposite direction of messages.py (which flows Agent → UI).
5
+
6
+ Commands are used for:
7
+ - Controlling agent execution (cancel, interrupt)
8
+ - Responding to agent requests for user input
9
+ - Providing confirmations and selections
10
+
11
+ The UI layer creates these commands and sends them to the agent/runtime.
12
+ The agent processes them and may emit messages in response.
13
+
14
+ ┌─────────┐ Commands ┌─────────┐
15
+ │ UI │ ────────────> │ Agent │
16
+ │ (User) │ │ │
17
+ │ │ <──────────── │ │
18
+ └─────────┘ Messages └─────────┘
19
+
20
+ NO Rich markup or formatting should be embedded in any string fields.
21
+ """
22
+
23
+ from datetime import datetime, timezone
24
+ from typing import Optional, Union
25
+ from uuid import uuid4
26
+
27
+ from pydantic import BaseModel, Field
28
+
29
+ # =============================================================================
30
+ # Base Command
31
+ # =============================================================================
32
+
33
+
34
+ class BaseCommand(BaseModel):
35
+ """Base class for all commands with auto-generated id and timestamp."""
36
+
37
+ id: str = Field(
38
+ default_factory=lambda: str(uuid4()),
39
+ description="Unique identifier for this command instance",
40
+ )
41
+ timestamp: datetime = Field(
42
+ default_factory=lambda: datetime.now(timezone.utc),
43
+ description="When this command was created (UTC)",
44
+ )
45
+
46
+ model_config = {"frozen": False, "extra": "forbid"}
47
+
48
+
49
+ # =============================================================================
50
+ # Agent Control Commands
51
+ # =============================================================================
52
+
53
+
54
+ class CancelAgentCommand(BaseCommand):
55
+ """Signals the agent to stop current execution gracefully.
56
+
57
+ The agent should finish any in-progress atomic operation, clean up,
58
+ and return control to the user. This is a soft cancellation.
59
+ """
60
+
61
+ reason: Optional[str] = Field(
62
+ default=None,
63
+ description="Optional reason for cancellation (for logging/debugging)",
64
+ )
65
+
66
+
67
+ class InterruptShellCommand(BaseCommand):
68
+ """Signals to interrupt a currently running shell command.
69
+
70
+ This is equivalent to pressing Ctrl+C in a terminal. The shell process
71
+ should receive SIGINT and terminate. Use this when a command is taking
72
+ too long or producing unwanted output.
73
+ """
74
+
75
+ command_id: Optional[str] = Field(
76
+ default=None,
77
+ description="ID of the specific shell command to interrupt (None = current)",
78
+ )
79
+
80
+
81
+ # =============================================================================
82
+ # User Interaction Responses
83
+ # =============================================================================
84
+
85
+
86
+ class UserInputResponse(BaseCommand):
87
+ """Response to a UserInputRequest from the agent.
88
+
89
+ The prompt_id must match the prompt_id from the original UserInputRequest
90
+ so the agent can correlate the response with the request.
91
+ """
92
+
93
+ prompt_id: str = Field(
94
+ description="ID of the prompt this responds to (must match request)"
95
+ )
96
+ value: str = Field(description="The user's input value")
97
+
98
+
99
+ class ConfirmationResponse(BaseCommand):
100
+ """Response to a ConfirmationRequest from the agent.
101
+
102
+ The user can confirm or deny, and optionally provide feedback text
103
+ if the original request had allow_feedback=True.
104
+ """
105
+
106
+ prompt_id: str = Field(
107
+ description="ID of the prompt this responds to (must match request)"
108
+ )
109
+ confirmed: bool = Field(
110
+ description="Whether the user confirmed (True) or denied (False)"
111
+ )
112
+ feedback: Optional[str] = Field(
113
+ default=None,
114
+ description="Optional feedback text from the user",
115
+ )
116
+
117
+
118
+ class SelectionResponse(BaseCommand):
119
+ """Response to a SelectionRequest from the agent.
120
+
121
+ Contains both the index and the value for convenience and validation.
122
+ The agent can verify that selected_value matches options[selected_index].
123
+ """
124
+
125
+ prompt_id: str = Field(
126
+ description="ID of the prompt this responds to (must match request)"
127
+ )
128
+ selected_index: int = Field(
129
+ ge=0,
130
+ description="Zero-based index of the selected option",
131
+ )
132
+ selected_value: str = Field(description="The value of the selected option")
133
+
134
+
135
+ # =============================================================================
136
+ # Union Type for Type Checking
137
+ # =============================================================================
138
+
139
+
140
+ # All concrete command types (excludes BaseCommand itself)
141
+ AnyCommand = Union[
142
+ CancelAgentCommand,
143
+ InterruptShellCommand,
144
+ UserInputResponse,
145
+ ConfirmationResponse,
146
+ SelectionResponse,
147
+ ]
148
+ """Union of all command types for type checking."""
149
+
150
+
151
+ # =============================================================================
152
+ # Export all public symbols
153
+ # =============================================================================
154
+
155
+ __all__ = [
156
+ # Base
157
+ "BaseCommand",
158
+ # Agent control
159
+ "CancelAgentCommand",
160
+ "InterruptShellCommand",
161
+ # User interaction responses
162
+ "UserInputResponse",
163
+ "ConfirmationResponse",
164
+ "SelectionResponse",
165
+ # Union type
166
+ "AnyCommand",
167
+ ]
@@ -0,0 +1,57 @@
1
+ """Patches for Rich's Markdown rendering.
2
+
3
+ This module provides customizations to Rich's default Markdown rendering,
4
+ particularly for header justification which is hardcoded to center in Rich.
5
+ """
6
+
7
+ from rich import box
8
+ from rich.markdown import Heading, Markdown
9
+ from rich.panel import Panel
10
+ from rich.text import Text
11
+
12
+
13
+ class LeftJustifiedHeading(Heading):
14
+ """A heading that left-justifies text instead of centering.
15
+
16
+ Rich's default Heading class hardcodes `text.justify = 'center'`,
17
+ which can look odd in a CLI context. This subclass overrides that
18
+ to use left justification instead.
19
+ """
20
+
21
+ def __rich_console__(self, console, options):
22
+ """Render the heading with left justification."""
23
+ text = self.text
24
+ text.justify = "left" # Override Rich's default 'center'
25
+
26
+ if self.tag == "h1":
27
+ # Draw a border around h1s (same as Rich default)
28
+ yield Panel(
29
+ text,
30
+ box=box.HEAVY,
31
+ style="markdown.h1.border",
32
+ )
33
+ else:
34
+ # Styled text for h2 and beyond (same as Rich default)
35
+ if self.tag == "h2":
36
+ yield Text("")
37
+ yield text
38
+
39
+
40
+ _patched = False
41
+
42
+
43
+ def patch_markdown_headings():
44
+ """Patch Rich's Markdown to use left-justified headings.
45
+
46
+ This function is idempotent - calling it multiple times has no effect
47
+ after the first call.
48
+ """
49
+ global _patched
50
+ if _patched:
51
+ return
52
+
53
+ Markdown.elements["heading_open"] = LeftJustifiedHeading
54
+ _patched = True
55
+
56
+
57
+ __all__ = ["patch_markdown_headings", "LeftJustifiedHeading"]
@@ -227,7 +227,7 @@ class MessageQueue:
227
227
 
228
228
  if timeout and (time.time() - start_time) > timeout:
229
229
  raise TimeoutError(
230
- f"No response received for prompt {prompt_id} within {timeout} seconds"
230
+ f"No response for prompt {prompt_id} within {timeout}s"
231
231
  )
232
232
 
233
233
  time.sleep(sleep_interval)
@@ -322,7 +322,7 @@ def emit_system_message(content: Any, **metadata):
322
322
  emit_message(MessageType.SYSTEM, content, **metadata)
323
323
 
324
324
 
325
- def emit_divider(content: str = "[dim]" + "─" * 100 + "\n" + "[/dim]", **metadata):
325
+ def emit_divider(content: str = "─" * 100 + "\n", **metadata):
326
326
  """Emit a divider line"""
327
327
  # TUI mode has been removed, always emit dividers
328
328
  emit_message(MessageType.DIVIDER, content, **metadata)
@@ -335,7 +335,7 @@ def emit_prompt(prompt_text: str, timeout: float = None) -> str:
335
335
  # Emit the prompt as a message for display
336
336
  from code_puppy.messaging import emit_info
337
337
 
338
- emit_info(f"[yellow]{prompt_text}[/yellow]")
338
+ emit_info(prompt_text)
339
339
 
340
340
  # Get input directly
341
341
  try: