code-puppy 0.0.341__py3-none-any.whl → 0.0.361__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 (86) hide show
  1. code_puppy/agents/__init__.py +2 -0
  2. code_puppy/agents/agent_manager.py +49 -0
  3. code_puppy/agents/agent_pack_leader.py +383 -0
  4. code_puppy/agents/agent_qa_kitten.py +12 -7
  5. code_puppy/agents/agent_terminal_qa.py +323 -0
  6. code_puppy/agents/base_agent.py +34 -252
  7. code_puppy/agents/event_stream_handler.py +350 -0
  8. code_puppy/agents/pack/__init__.py +34 -0
  9. code_puppy/agents/pack/bloodhound.py +304 -0
  10. code_puppy/agents/pack/husky.py +321 -0
  11. code_puppy/agents/pack/retriever.py +393 -0
  12. code_puppy/agents/pack/shepherd.py +348 -0
  13. code_puppy/agents/pack/terrier.py +287 -0
  14. code_puppy/agents/pack/watchdog.py +367 -0
  15. code_puppy/agents/subagent_stream_handler.py +276 -0
  16. code_puppy/api/__init__.py +13 -0
  17. code_puppy/api/app.py +169 -0
  18. code_puppy/api/main.py +21 -0
  19. code_puppy/api/pty_manager.py +446 -0
  20. code_puppy/api/routers/__init__.py +12 -0
  21. code_puppy/api/routers/agents.py +36 -0
  22. code_puppy/api/routers/commands.py +217 -0
  23. code_puppy/api/routers/config.py +74 -0
  24. code_puppy/api/routers/sessions.py +232 -0
  25. code_puppy/api/templates/terminal.html +361 -0
  26. code_puppy/api/websocket.py +154 -0
  27. code_puppy/callbacks.py +73 -0
  28. code_puppy/claude_cache_client.py +249 -34
  29. code_puppy/cli_runner.py +4 -3
  30. code_puppy/command_line/add_model_menu.py +8 -9
  31. code_puppy/command_line/core_commands.py +85 -0
  32. code_puppy/command_line/mcp/catalog_server_installer.py +5 -6
  33. code_puppy/command_line/mcp/custom_server_form.py +54 -19
  34. code_puppy/command_line/mcp/custom_server_installer.py +8 -9
  35. code_puppy/command_line/mcp/handler.py +0 -2
  36. code_puppy/command_line/mcp/help_command.py +1 -5
  37. code_puppy/command_line/mcp/start_command.py +36 -18
  38. code_puppy/command_line/onboarding_slides.py +0 -1
  39. code_puppy/command_line/prompt_toolkit_completion.py +16 -10
  40. code_puppy/command_line/utils.py +54 -0
  41. code_puppy/config.py +66 -62
  42. code_puppy/mcp_/async_lifecycle.py +35 -4
  43. code_puppy/mcp_/managed_server.py +49 -20
  44. code_puppy/mcp_/manager.py +81 -52
  45. code_puppy/messaging/__init__.py +15 -0
  46. code_puppy/messaging/message_queue.py +11 -23
  47. code_puppy/messaging/messages.py +27 -0
  48. code_puppy/messaging/queue_console.py +1 -1
  49. code_puppy/messaging/rich_renderer.py +36 -1
  50. code_puppy/messaging/spinner/__init__.py +20 -2
  51. code_puppy/messaging/subagent_console.py +461 -0
  52. code_puppy/model_utils.py +54 -0
  53. code_puppy/plugins/antigravity_oauth/antigravity_model.py +90 -19
  54. code_puppy/plugins/antigravity_oauth/transport.py +1 -0
  55. code_puppy/plugins/frontend_emitter/__init__.py +25 -0
  56. code_puppy/plugins/frontend_emitter/emitter.py +121 -0
  57. code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
  58. code_puppy/prompts/antigravity_system_prompt.md +1 -0
  59. code_puppy/status_display.py +6 -2
  60. code_puppy/tools/__init__.py +37 -1
  61. code_puppy/tools/agent_tools.py +139 -36
  62. code_puppy/tools/browser/__init__.py +37 -0
  63. code_puppy/tools/browser/browser_control.py +6 -6
  64. code_puppy/tools/browser/browser_interactions.py +21 -20
  65. code_puppy/tools/browser/browser_locators.py +9 -9
  66. code_puppy/tools/browser/browser_navigation.py +7 -7
  67. code_puppy/tools/browser/browser_screenshot.py +78 -140
  68. code_puppy/tools/browser/browser_scripts.py +15 -13
  69. code_puppy/tools/browser/camoufox_manager.py +226 -64
  70. code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
  71. code_puppy/tools/browser/terminal_command_tools.py +521 -0
  72. code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
  73. code_puppy/tools/browser/terminal_tools.py +525 -0
  74. code_puppy/tools/command_runner.py +292 -101
  75. code_puppy/tools/common.py +176 -1
  76. code_puppy/tools/display.py +84 -0
  77. code_puppy/tools/subagent_context.py +158 -0
  78. {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/METADATA +13 -11
  79. {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/RECORD +84 -53
  80. code_puppy/command_line/mcp/add_command.py +0 -170
  81. code_puppy/tools/browser/vqa_agent.py +0 -90
  82. {code_puppy-0.0.341.data → code_puppy-0.0.361.data}/data/code_puppy/models.json +0 -0
  83. {code_puppy-0.0.341.data → code_puppy-0.0.361.data}/data/code_puppy/models_dev_api.json +0 -0
  84. {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/WHEEL +0 -0
  85. {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/entry_points.txt +0 -0
  86. {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,25 @@
1
+ """Frontend emitter plugin for Code Puppy.
2
+
3
+ This plugin provides event emission capabilities for frontend integration,
4
+ allowing WebSocket handlers to subscribe to real-time events from the
5
+ agent system including tool calls, streaming events, and agent invocations.
6
+
7
+ Usage:
8
+ from code_puppy.plugins.frontend_emitter.emitter import (
9
+ emit_event,
10
+ subscribe,
11
+ unsubscribe,
12
+ get_recent_events,
13
+ )
14
+
15
+ # Subscribe to events
16
+ queue = subscribe()
17
+
18
+ # Process events in your WebSocket handler
19
+ while True:
20
+ event = await queue.get()
21
+ await websocket.send_json(event)
22
+
23
+ # Clean up
24
+ unsubscribe(queue)
25
+ """
@@ -0,0 +1,121 @@
1
+ """Event emitter for frontend integration.
2
+
3
+ Provides a global event queue that WebSocket handlers can subscribe to.
4
+ Events are JSON-serializable dicts with type, timestamp, and data.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ from datetime import datetime, timezone
10
+ from typing import Any, Dict, List, Set
11
+ from uuid import uuid4
12
+
13
+ from code_puppy.config import (
14
+ get_frontend_emitter_enabled,
15
+ get_frontend_emitter_max_recent_events,
16
+ get_frontend_emitter_queue_size,
17
+ )
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # Global state for event distribution
22
+ _subscribers: Set[asyncio.Queue[Dict[str, Any]]] = set()
23
+ _recent_events: List[Dict[str, Any]] = [] # Keep last N events for new subscribers
24
+
25
+
26
+ def emit_event(event_type: str, data: Any = None) -> None:
27
+ """Emit an event to all subscribers.
28
+
29
+ Creates a structured event dict with unique ID, type, timestamp, and data,
30
+ then broadcasts it to all active subscriber queues.
31
+
32
+ Args:
33
+ event_type: Type of event (e.g., "tool_call_start", "stream_token")
34
+ data: Event data payload - should be JSON-serializable
35
+ """
36
+ # Early return if emitter is disabled
37
+ if not get_frontend_emitter_enabled():
38
+ return
39
+
40
+ event: Dict[str, Any] = {
41
+ "id": str(uuid4()),
42
+ "type": event_type,
43
+ "timestamp": datetime.now(timezone.utc).isoformat(),
44
+ "data": data or {},
45
+ }
46
+
47
+ # Store in recent events for replay to new subscribers
48
+ max_recent = get_frontend_emitter_max_recent_events()
49
+ _recent_events.append(event)
50
+ if len(_recent_events) > max_recent:
51
+ _recent_events.pop(0)
52
+
53
+ # Broadcast to all active subscribers
54
+ for subscriber_queue in _subscribers.copy():
55
+ try:
56
+ subscriber_queue.put_nowait(event)
57
+ except asyncio.QueueFull:
58
+ logger.warning(f"Subscriber queue full, dropping event: {event_type}")
59
+ except Exception as e:
60
+ logger.error(f"Failed to emit event to subscriber: {e}")
61
+
62
+
63
+ def subscribe() -> asyncio.Queue[Dict[str, Any]]:
64
+ """Subscribe to events.
65
+
66
+ Creates and returns a new async queue that will receive all future events.
67
+ The queue has a configurable max size (via frontend_emitter_queue_size)
68
+ to prevent unbounded memory growth if the subscriber is slow to process events.
69
+
70
+ Returns:
71
+ An asyncio.Queue that will receive event dictionaries.
72
+ """
73
+ queue_size = get_frontend_emitter_queue_size()
74
+ queue: asyncio.Queue[Dict[str, Any]] = asyncio.Queue(maxsize=queue_size)
75
+ _subscribers.add(queue)
76
+ logger.debug(f"New subscriber added, total subscribers: {len(_subscribers)}")
77
+ return queue
78
+
79
+
80
+ def unsubscribe(queue: asyncio.Queue[Dict[str, Any]]) -> None:
81
+ """Unsubscribe from events.
82
+
83
+ Removes the queue from the subscriber set. Safe to call even if the queue
84
+ was never subscribed or already unsubscribed.
85
+
86
+ Args:
87
+ queue: The queue returned from subscribe()
88
+ """
89
+ _subscribers.discard(queue)
90
+ logger.debug(f"Subscriber removed, remaining subscribers: {len(_subscribers)}")
91
+
92
+
93
+ def get_recent_events() -> List[Dict[str, Any]]:
94
+ """Get recent events for new subscribers.
95
+
96
+ Returns a copy of the most recent events (up to frontend_emitter_max_recent_events).
97
+ Useful for allowing new WebSocket connections to "catch up" on
98
+ recent activity.
99
+
100
+ Returns:
101
+ A list of recent event dictionaries.
102
+ """
103
+ return _recent_events.copy()
104
+
105
+
106
+ def get_subscriber_count() -> int:
107
+ """Get the current number of active subscribers.
108
+
109
+ Returns:
110
+ Number of active subscriber queues.
111
+ """
112
+ return len(_subscribers)
113
+
114
+
115
+ def clear_recent_events() -> None:
116
+ """Clear the recent events buffer.
117
+
118
+ Useful for testing or resetting state.
119
+ """
120
+ _recent_events.clear()
121
+ logger.debug("Recent events cleared")
@@ -0,0 +1,261 @@
1
+ """Callback registration for frontend event emission.
2
+
3
+ This module registers callbacks for various agent events and emits them
4
+ to subscribed WebSocket handlers via the emitter module.
5
+ """
6
+
7
+ import logging
8
+ import time
9
+ from typing import Any, Dict, Optional
10
+
11
+ from code_puppy.callbacks import register_callback
12
+ from code_puppy.plugins.frontend_emitter.emitter import emit_event
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ async def on_pre_tool_call(
18
+ tool_name: str, tool_args: Dict[str, Any], context: Any = None
19
+ ) -> None:
20
+ """Emit an event when a tool call starts.
21
+
22
+ Args:
23
+ tool_name: Name of the tool being called
24
+ tool_args: Arguments being passed to the tool
25
+ context: Optional context data for the tool call
26
+ """
27
+ try:
28
+ emit_event(
29
+ "tool_call_start",
30
+ {
31
+ "tool_name": tool_name,
32
+ "tool_args": _sanitize_args(tool_args),
33
+ "start_time": time.time(),
34
+ },
35
+ )
36
+ logger.debug(f"Emitted tool_call_start for {tool_name}")
37
+ except Exception as e:
38
+ logger.error(f"Failed to emit pre_tool_call event: {e}")
39
+
40
+
41
+ async def on_post_tool_call(
42
+ tool_name: str,
43
+ tool_args: Dict[str, Any],
44
+ result: Any,
45
+ duration_ms: float,
46
+ context: Any = None,
47
+ ) -> None:
48
+ """Emit an event when a tool call completes.
49
+
50
+ Args:
51
+ tool_name: Name of the tool that was called
52
+ tool_args: Arguments that were passed to the tool
53
+ result: The result returned by the tool
54
+ duration_ms: Execution time in milliseconds
55
+ context: Optional context data for the tool call
56
+ """
57
+ try:
58
+ emit_event(
59
+ "tool_call_complete",
60
+ {
61
+ "tool_name": tool_name,
62
+ "tool_args": _sanitize_args(tool_args),
63
+ "duration_ms": duration_ms,
64
+ "success": _is_successful_result(result),
65
+ "result_summary": _summarize_result(result),
66
+ },
67
+ )
68
+ logger.debug(
69
+ f"Emitted tool_call_complete for {tool_name} ({duration_ms:.2f}ms)"
70
+ )
71
+ except Exception as e:
72
+ logger.error(f"Failed to emit post_tool_call event: {e}")
73
+
74
+
75
+ async def on_stream_event(
76
+ event_type: str, event_data: Any, agent_session_id: Optional[str] = None
77
+ ) -> None:
78
+ """Emit streaming events from the agent.
79
+
80
+ Args:
81
+ event_type: Type of the streaming event
82
+ event_data: Data associated with the event
83
+ agent_session_id: Optional session ID of the agent emitting the event
84
+ """
85
+ try:
86
+ emit_event(
87
+ "stream_event",
88
+ {
89
+ "event_type": event_type,
90
+ "event_data": _sanitize_event_data(event_data),
91
+ "agent_session_id": agent_session_id,
92
+ },
93
+ )
94
+ logger.debug(f"Emitted stream_event: {event_type}")
95
+ except Exception as e:
96
+ logger.error(f"Failed to emit stream_event: {e}")
97
+
98
+
99
+ async def on_invoke_agent(*args: Any, **kwargs: Any) -> None:
100
+ """Emit an event when an agent is invoked.
101
+
102
+ Args:
103
+ *args: Positional arguments from the invoke_agent callback
104
+ **kwargs: Keyword arguments from the invoke_agent callback
105
+ """
106
+ try:
107
+ # Extract relevant info from args/kwargs
108
+ agent_info = {
109
+ "agent_name": kwargs.get("agent_name") or (args[0] if args else None),
110
+ "session_id": kwargs.get("session_id"),
111
+ "prompt_preview": _truncate_string(
112
+ kwargs.get("prompt") or (args[1] if len(args) > 1 else None),
113
+ max_length=200,
114
+ ),
115
+ }
116
+ emit_event("agent_invoked", agent_info)
117
+ logger.debug(f"Emitted agent_invoked: {agent_info.get('agent_name')}")
118
+ except Exception as e:
119
+ logger.error(f"Failed to emit invoke_agent event: {e}")
120
+
121
+
122
+ def _sanitize_args(args: Dict[str, Any]) -> Dict[str, Any]:
123
+ """Sanitize tool arguments for safe emission.
124
+
125
+ Truncates large values and removes potentially sensitive data.
126
+
127
+ Args:
128
+ args: The raw tool arguments
129
+
130
+ Returns:
131
+ Sanitized arguments safe for emission
132
+ """
133
+ if not isinstance(args, dict):
134
+ return {}
135
+
136
+ sanitized: Dict[str, Any] = {}
137
+ for key, value in args.items():
138
+ if isinstance(value, str):
139
+ sanitized[key] = _truncate_string(value, max_length=500)
140
+ elif isinstance(value, (int, float, bool, type(None))):
141
+ sanitized[key] = value
142
+ elif isinstance(value, (list, dict)):
143
+ # Just indicate the type and length for complex types
144
+ sanitized[key] = f"<{type(value).__name__}[{len(value)}]>"
145
+ else:
146
+ sanitized[key] = f"<{type(value).__name__}>"
147
+
148
+ return sanitized
149
+
150
+
151
+ def _sanitize_event_data(data: Any) -> Any:
152
+ """Sanitize event data for safe emission.
153
+
154
+ Args:
155
+ data: The raw event data
156
+
157
+ Returns:
158
+ Sanitized data safe for emission
159
+ """
160
+ if data is None:
161
+ return None
162
+
163
+ if isinstance(data, str):
164
+ return _truncate_string(data, max_length=1000)
165
+
166
+ if isinstance(data, (int, float, bool)):
167
+ return data
168
+
169
+ if isinstance(data, dict):
170
+ return {k: _sanitize_event_data(v) for k, v in list(data.items())[:20]}
171
+
172
+ if isinstance(data, (list, tuple)):
173
+ return [_sanitize_event_data(item) for item in data[:20]]
174
+
175
+ return f"<{type(data).__name__}>"
176
+
177
+
178
+ def _is_successful_result(result: Any) -> bool:
179
+ """Determine if a tool result indicates success.
180
+
181
+ Args:
182
+ result: The tool result
183
+
184
+ Returns:
185
+ True if the result appears successful
186
+ """
187
+ if result is None:
188
+ return True # No result often means success
189
+
190
+ if isinstance(result, dict):
191
+ # Check for error indicators
192
+ if result.get("error"):
193
+ return False
194
+ if result.get("success") is False:
195
+ return False
196
+ return True
197
+
198
+ if isinstance(result, bool):
199
+ return result
200
+
201
+ return True # Default to success
202
+
203
+
204
+ def _summarize_result(result: Any) -> str:
205
+ """Create a brief summary of a tool result.
206
+
207
+ Args:
208
+ result: The tool result
209
+
210
+ Returns:
211
+ A string summary of the result
212
+ """
213
+ if result is None:
214
+ return "<no result>"
215
+
216
+ if isinstance(result, str):
217
+ return _truncate_string(result, max_length=200)
218
+
219
+ if isinstance(result, dict):
220
+ if "error" in result:
221
+ return f"Error: {_truncate_string(str(result['error']), max_length=100)}"
222
+ if "message" in result:
223
+ return _truncate_string(str(result["message"]), max_length=100)
224
+ return f"<dict with {len(result)} keys>"
225
+
226
+ if isinstance(result, (list, tuple)):
227
+ return f"<{type(result).__name__}[{len(result)}]>"
228
+
229
+ return _truncate_string(str(result), max_length=200)
230
+
231
+
232
+ def _truncate_string(value: Any, max_length: int = 100) -> Optional[str]:
233
+ """Truncate a string value if it exceeds max_length.
234
+
235
+ Args:
236
+ value: The value to truncate (will be converted to str)
237
+ max_length: Maximum length before truncation
238
+
239
+ Returns:
240
+ Truncated string or None if value is None
241
+ """
242
+ if value is None:
243
+ return None
244
+
245
+ s = str(value)
246
+ if len(s) > max_length:
247
+ return s[: max_length - 3] + "..."
248
+ return s
249
+
250
+
251
+ def register() -> None:
252
+ """Register all frontend emitter callbacks."""
253
+ register_callback("pre_tool_call", on_pre_tool_call)
254
+ register_callback("post_tool_call", on_post_tool_call)
255
+ register_callback("stream_event", on_stream_event)
256
+ register_callback("invoke_agent", on_invoke_agent)
257
+ logger.debug("Frontend emitter callbacks registered")
258
+
259
+
260
+ # Auto-register callbacks when this module is imported
261
+ register()
@@ -0,0 +1 @@
1
+ <identity>\nYou are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.\nYou are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.\nThe USER will send you requests, which you must always prioritize addressing. Along with each USER request, we will attach additional metadata about their current state, such as what files they have open and where their cursor is.\nThis information may or may not be relevant to the coding task, it is up for you to decide.\n</identity>\n\n<tool_calling>\nCall tools as you normally would. The following list provides additional guidance to help you avoid errors:\n - **Absolute paths only**. When using tools that accept file path arguments, ALWAYS use the absolute file path.\n</tool_calling>\n\n<web_application_development>\n## Technology Stack,\nYour web applications should be built using the following technologies:,\n1. **Core**: Use HTML for structure and Javascript for logic.\n2. **Styling (CSS)**: Use Vanilla CSS for maximum flexibility and control. Avoid using TailwindCSS unless the USER explicitly requests it; in this case, first confirm which TailwindCSS version to use.\n3. **Web App**: If the USER specifies that they want a more complex web app, use a framework like Next.js or Vite. Only do this if the USER explicitly requests a web app.\n4. **New Project Creation**: If you need to use a framework for a new app, use `npx` with the appropriate script, but there are some rules to follow:,\n - Use `npx -y` to automatically install the script and its dependencies\n - You MUST run the command with `--help` flag to see all available options first, \n - Initialize the app in the current directory with `./` (example: `npx -y create-vite-app@latest ./`),\n - You should run in non-interactive mode so that the user doesn't need to input anything,\n5. **Running Locally**: When running locally, use `npm run dev` or equivalent dev server. Only build the production bundle if the USER explicitly requests it or you are validating the code for correctness.\n\n# Design Aesthetics,\n1. **Use Rich Aesthetics**: The USER should be wowed at first glance by the design. Use best practices in modern web design (e.g. vibrant colors, dark modes, glassmorphism, and dynamic animations) to create a stunning first impression. Failure to do this is UNACCEPTABLE.\n2. **Prioritize Visual Excellence**: Implement designs that will WOW the user and feel extremely premium:\n\t\t- Avoid generic colors (plain red, blue, green). Use curated, harmonious color palettes (e.g., HSL tailored colors, sleek dark modes).\n - Using modern typography (e.g., from Google Fonts like Inter, Roboto, or Outfit) instead of browser defaults.\n\t\t- Use smooth gradients,\n\t\t- Add subtle micro-animations for enhanced user experience,\n3. **Use a Dynamic Design**: An interface that feels responsive and alive encourages interaction. Achieve this with hover effects and interactive elements. Micro-animations, in particular, are highly effective for improving user engagement.\n4. **Premium Designs**. Make a design that feels premium and state of the art. Avoid creating simple minimum viable products.\n4. **Don't use placeholders**. If you need an image, use your generate_image tool to create a working demonstration.,\n\n## Implementation Workflow,\nFollow this systematic approach when building web applications:,\n1. **Plan and Understand**:,\n\t\t- Fully understand the user's requirements,\n\t\t- Draw inspiration from modern, beautiful, and dynamic web designs,\n\t\t- Outline the features needed for the initial version,\n2. **Build the Foundation**:,\n\t\t- Start by creating/modifying `index.css`,\n\t\t- Implement the core design system with all tokens and utilities,\n3. **Create Components**:,\n\t\t- Build necessary components using your design system,\n\t\t- Ensure all components use predefined styles, not ad-hoc utilities,\n\t\t- Keep components focused and reusable,\n4. **Assemble Pages**:,\n\t\t- Update the main application to incorporate your design and components,\n\t\t- Ensure proper routing and navigation,\n\t\t- Implement responsive layouts,\n5. **Polish and Optimize**:,\n\t\t- Review the overall user experience,\n\t\t- Ensure smooth interactions and transitions,\n\t\t- Optimize performance where needed,\n\n## SEO Best Practices,\nAutomatically implement SEO best practices on every page:,\n- **Title Tags**: Include proper, descriptive title tags for each page,\n- **Meta Descriptions**: Add compelling meta descriptions that accurately summarize page content,\n- **Heading Structure**: Use a single `<h1>` per page with proper heading hierarchy,\n- **Semantic HTML**: Use appropriate HTML5 semantic elements,\n- **Unique IDs**: Ensure all interactive elements have unique, descriptive IDs for browser testing,\n- **Performance**: Ensure fast page load times through optimization,\nCRITICAL REMINDER: AESTHETICS ARE VERY IMPORTANT. If your web app looks simple and basic then you have FAILED!\n</web_application_development>\n<ephemeral_message>\nThere will be an <EPHEMERAL_MESSAGE> appearing in the conversation at times. This is not coming from the user, but instead injected by the system as important information to pay attention to. \nDo not respond to nor acknowledge those messages, but do follow them strictly.\n</ephemeral_message>\n\n\n<communication_style>\n- **Formatting**. Format your responses in github-style markdown to make your responses easier for the USER to parse. For example, use headers to organize your responses and bolded or italicized text to highlight important keywords. Use backticks to format file, directory, function, and class names. If providing a URL to the user, format this in markdown as well, for example `[label](example.com)`.\n- **Proactiveness**. As an agent, you are allowed to be proactive, but only in the course of completing the user's task. For example, if the user asks you to add a new component, you can edit the code, verify build and test statuses, and take any other obvious follow-up actions, such as performing additional research. However, avoid surprising the user. For example, if the user asks HOW to approach something, you should answer their question and instead of jumping into editing a file.\n- **Helpfulness**. Respond like a helpful software engineer who is explaining your work to a friendly collaborator on the project. Acknowledge mistakes or any backtracking you do as a result of new information.\n- **Ask for clarification**. If you are unsure about the USER's intent, always ask for clarification rather than making assumptions.\n</communication_style>
@@ -7,8 +7,6 @@ from rich.panel import Panel
7
7
  from rich.spinner import Spinner
8
8
  from rich.text import Text
9
9
 
10
- from code_puppy.messaging import emit_info
11
-
12
10
  # Global variable to track current token per second rate
13
11
  CURRENT_TOKEN_RATE = 0.0
14
12
 
@@ -186,6 +184,9 @@ class StatusDisplay:
186
184
 
187
185
  async def _update_display(self) -> None:
188
186
  """Update the display continuously while active using Rich Live display"""
187
+ # Lazy import to avoid circular dependency during module initialization
188
+ from code_puppy.messaging import emit_info
189
+
189
190
  # Add a newline to ensure we're below the blue bar
190
191
  emit_info("")
191
192
 
@@ -214,6 +215,9 @@ class StatusDisplay:
214
215
 
215
216
  def stop(self) -> None:
216
217
  """Stop the status display"""
218
+ # Lazy import to avoid circular dependency during module initialization
219
+ from code_puppy.messaging import emit_info
220
+
217
221
  if self.is_active:
218
222
  self.is_active = False
219
223
  if self.task:
@@ -55,10 +55,32 @@ from code_puppy.tools.browser.browser_workflows import (
55
55
  register_read_workflow,
56
56
  register_save_workflow,
57
57
  )
58
+ from code_puppy.tools.browser.terminal_command_tools import (
59
+ register_run_terminal_command,
60
+ register_send_terminal_keys,
61
+ register_wait_terminal_output,
62
+ )
63
+ from code_puppy.tools.browser.terminal_screenshot_tools import (
64
+ register_load_image,
65
+ register_terminal_compare_mockup,
66
+ register_terminal_read_output,
67
+ register_terminal_screenshot,
68
+ )
69
+
70
+ # Terminal automation tools
71
+ from code_puppy.tools.browser.terminal_tools import (
72
+ register_check_terminal_server,
73
+ register_close_terminal,
74
+ register_open_terminal,
75
+ register_start_api_server,
76
+ )
58
77
  from code_puppy.tools.command_runner import (
59
78
  register_agent_run_shell_command,
60
79
  register_agent_share_your_reasoning,
61
80
  )
81
+ from code_puppy.tools.display import (
82
+ display_non_streamed_result as display_non_streamed_result,
83
+ )
62
84
  from code_puppy.tools.file_modifications import register_delete_file, register_edit_file
63
85
  from code_puppy.tools.file_operations import (
64
86
  register_grep,
@@ -121,12 +143,26 @@ TOOL_REGISTRY = {
121
143
  "browser_wait_for_element": register_wait_for_element,
122
144
  "browser_highlight_element": register_browser_highlight_element,
123
145
  "browser_clear_highlights": register_browser_clear_highlights,
124
- # Browser Screenshots and VQA
146
+ # Browser Screenshots
125
147
  "browser_screenshot_analyze": register_take_screenshot_and_analyze,
126
148
  # Browser Workflows
127
149
  "browser_save_workflow": register_save_workflow,
128
150
  "browser_list_workflows": register_list_workflows,
129
151
  "browser_read_workflow": register_read_workflow,
152
+ # Terminal Connection Tools
153
+ "terminal_check_server": register_check_terminal_server,
154
+ "terminal_open": register_open_terminal,
155
+ "terminal_close": register_close_terminal,
156
+ "start_api_server": register_start_api_server,
157
+ # Terminal Command Execution Tools
158
+ "terminal_run_command": register_run_terminal_command,
159
+ "terminal_send_keys": register_send_terminal_keys,
160
+ "terminal_wait_output": register_wait_terminal_output,
161
+ # Terminal Screenshot Tools
162
+ "terminal_screenshot_analyze": register_terminal_screenshot,
163
+ "terminal_read_output": register_terminal_read_output,
164
+ "terminal_compare_mockup": register_terminal_compare_mockup,
165
+ "load_image_for_analysis": register_load_image,
130
166
  }
131
167
 
132
168