code-puppy 0.0.171__tar.gz → 0.0.172__tar.gz

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 (131) hide show
  1. {code_puppy-0.0.171 → code_puppy-0.0.172}/PKG-INFO +3 -1
  2. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/agent.py +3 -3
  3. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/agents/agent_creator_agent.py +0 -3
  4. code_puppy-0.0.172/code_puppy/agents/agent_qa_kitten.py +203 -0
  5. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/agents/base_agent.py +9 -0
  6. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/command_handler.py +68 -28
  7. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/add_command.py +1 -1
  8. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/base.py +1 -1
  9. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/install_command.py +1 -1
  10. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/list_command.py +1 -1
  11. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/search_command.py +1 -1
  12. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/start_all_command.py +1 -1
  13. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/status_command.py +2 -2
  14. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/stop_all_command.py +1 -1
  15. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/utils.py +1 -1
  16. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/wizard_utils.py +2 -2
  17. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/config.py +142 -12
  18. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/http_utils.py +50 -24
  19. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/config_wizard.py +1 -1
  20. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/examples/retry_example.py +1 -1
  21. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/managed_server.py +1 -1
  22. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/server_registry_catalog.py +1 -3
  23. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/message_history_processor.py +1 -61
  24. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/state_management.py +4 -2
  25. code_puppy-0.0.172/code_puppy/tools/__init__.py +167 -0
  26. code_puppy-0.0.172/code_puppy/tools/browser/__init__.py +0 -0
  27. code_puppy-0.0.172/code_puppy/tools/browser/browser_control.py +293 -0
  28. code_puppy-0.0.172/code_puppy/tools/browser/browser_interactions.py +552 -0
  29. code_puppy-0.0.172/code_puppy/tools/browser/browser_locators.py +642 -0
  30. code_puppy-0.0.172/code_puppy/tools/browser/browser_navigation.py +251 -0
  31. code_puppy-0.0.172/code_puppy/tools/browser/browser_screenshot.py +242 -0
  32. code_puppy-0.0.172/code_puppy/tools/browser/browser_scripts.py +478 -0
  33. code_puppy-0.0.172/code_puppy/tools/browser/browser_workflows.py +196 -0
  34. code_puppy-0.0.172/code_puppy/tools/browser/camoufox_manager.py +194 -0
  35. code_puppy-0.0.172/code_puppy/tools/browser/vqa_agent.py +66 -0
  36. code_puppy-0.0.172/code_puppy/tools/browser_control.py +293 -0
  37. code_puppy-0.0.172/code_puppy/tools/browser_interactions.py +552 -0
  38. code_puppy-0.0.172/code_puppy/tools/browser_locators.py +642 -0
  39. code_puppy-0.0.172/code_puppy/tools/browser_navigation.py +251 -0
  40. code_puppy-0.0.172/code_puppy/tools/browser_screenshot.py +278 -0
  41. code_puppy-0.0.172/code_puppy/tools/browser_scripts.py +478 -0
  42. code_puppy-0.0.172/code_puppy/tools/browser_workflows.py +215 -0
  43. code_puppy-0.0.172/code_puppy/tools/camoufox_manager.py +150 -0
  44. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tools/command_runner.py +12 -7
  45. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tools/file_operations.py +7 -7
  46. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/components/custom_widgets.py +1 -1
  47. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/screens/mcp_install_wizard.py +8 -8
  48. {code_puppy-0.0.171 → code_puppy-0.0.172}/pyproject.toml +3 -1
  49. code_puppy-0.0.171/code_puppy/tools/__init__.py +0 -70
  50. {code_puppy-0.0.171 → code_puppy-0.0.172}/.gitignore +0 -0
  51. {code_puppy-0.0.171 → code_puppy-0.0.172}/LICENSE +0 -0
  52. {code_puppy-0.0.171 → code_puppy-0.0.172}/README.md +0 -0
  53. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/__init__.py +0 -0
  54. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/__main__.py +0 -0
  55. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/agents/__init__.py +0 -0
  56. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/agents/agent_code_puppy.py +0 -0
  57. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/agents/agent_manager.py +0 -0
  58. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/agents/agent_orchestrator.json +0 -0
  59. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/agents/json_agent.py +0 -0
  60. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/agents/runtime_manager.py +0 -0
  61. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/callbacks.py +0 -0
  62. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/__init__.py +0 -0
  63. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/file_path_completion.py +0 -0
  64. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/load_context_completion.py +0 -0
  65. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/__init__.py +0 -0
  66. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/handler.py +0 -0
  67. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/help_command.py +0 -0
  68. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/logs_command.py +0 -0
  69. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/remove_command.py +0 -0
  70. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/restart_command.py +0 -0
  71. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/start_command.py +0 -0
  72. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/stop_command.py +0 -0
  73. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/mcp/test_command.py +0 -0
  74. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/meta_command_handler.py +0 -0
  75. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/model_picker_completion.py +0 -0
  76. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/motd.py +0 -0
  77. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
  78. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/command_line/utils.py +0 -0
  79. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/main.py +0 -0
  80. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/__init__.py +0 -0
  81. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/async_lifecycle.py +0 -0
  82. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/blocking_startup.py +0 -0
  83. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/captured_stdio_server.py +0 -0
  84. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/circuit_breaker.py +0 -0
  85. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/dashboard.py +0 -0
  86. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/error_isolation.py +0 -0
  87. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/health_monitor.py +0 -0
  88. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/manager.py +0 -0
  89. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/registry.py +0 -0
  90. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/retry_manager.py +0 -0
  91. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/status_tracker.py +0 -0
  92. {code_puppy-0.0.171/code_puppy/mcp → code_puppy-0.0.172/code_puppy/mcp_}/system_tools.py +0 -0
  93. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/messaging/__init__.py +0 -0
  94. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/messaging/message_queue.py +0 -0
  95. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/messaging/queue_console.py +0 -0
  96. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/messaging/renderers.py +0 -0
  97. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/messaging/spinner/__init__.py +0 -0
  98. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/messaging/spinner/console_spinner.py +0 -0
  99. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/messaging/spinner/spinner_base.py +0 -0
  100. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/messaging/spinner/textual_spinner.py +0 -0
  101. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/model_factory.py +0 -0
  102. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/models.json +0 -0
  103. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/plugins/__init__.py +0 -0
  104. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/reopenable_async_client.py +0 -0
  105. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/round_robin_model.py +0 -0
  106. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/status_display.py +0 -0
  107. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/summarization_agent.py +0 -0
  108. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tools/agent_tools.py +0 -0
  109. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tools/common.py +0 -0
  110. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tools/file_modifications.py +0 -0
  111. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tools/tools_content.py +0 -0
  112. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/__init__.py +0 -0
  113. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/app.py +0 -0
  114. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/components/__init__.py +0 -0
  115. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/components/chat_view.py +0 -0
  116. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/components/command_history_modal.py +0 -0
  117. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/components/copy_button.py +0 -0
  118. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/components/human_input_modal.py +0 -0
  119. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/components/input_area.py +0 -0
  120. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/components/sidebar.py +0 -0
  121. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/components/status_bar.py +0 -0
  122. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/messages.py +0 -0
  123. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/models/__init__.py +0 -0
  124. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/models/chat_message.py +0 -0
  125. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/models/command_history.py +0 -0
  126. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/models/enums.py +0 -0
  127. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/screens/__init__.py +0 -0
  128. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/screens/help.py +0 -0
  129. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/screens/settings.py +0 -0
  130. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/tui/screens/tools.py +0 -0
  131. {code_puppy-0.0.171 → code_puppy-0.0.172}/code_puppy/version_checker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.171
3
+ Version: 0.0.172
4
4
  Summary: Code generation agent
5
5
  Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
6
6
  Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.13
16
16
  Classifier: Topic :: Software Development :: Code Generators
17
17
  Requires-Python: >=3.11
18
18
  Requires-Dist: bs4>=0.0.2
19
+ Requires-Dist: camoufox>=0.4.11
19
20
  Requires-Dist: fastapi>=0.110.0
20
21
  Requires-Dist: httpx-limiter>=0.3.0
21
22
  Requires-Dist: httpx>=0.24.1
@@ -23,6 +24,7 @@ Requires-Dist: json-repair>=0.46.2
23
24
  Requires-Dist: logfire>=0.7.1
24
25
  Requires-Dist: openai>=1.99.1
25
26
  Requires-Dist: pathspec>=0.11.0
27
+ Requires-Dist: playwright>=1.40.0
26
28
  Requires-Dist: prompt-toolkit>=3.0.38
27
29
  Requires-Dist: pydantic-ai>=1.0.10
28
30
  Requires-Dist: pydantic>=2.4.0
@@ -48,7 +48,7 @@ _code_generation_agent = None
48
48
  def _load_mcp_servers(extra_headers: Optional[Dict[str, str]] = None):
49
49
  """Load MCP servers using the new manager while maintaining backward compatibility."""
50
50
  from code_puppy.config import get_value, load_mcp_server_configs
51
- from code_puppy.mcp import ServerConfig, get_mcp_manager
51
+ from code_puppy.mcp_ import ServerConfig, get_mcp_manager
52
52
 
53
53
  # Check if MCP servers are disabled
54
54
  mcp_disabled = get_value("disable_mcp_servers")
@@ -113,7 +113,7 @@ def _load_mcp_servers(extra_headers: Optional[Dict[str, str]] = None):
113
113
 
114
114
  def reload_mcp_servers():
115
115
  """Reload MCP servers without restarting the agent."""
116
- from code_puppy.mcp import get_mcp_manager
116
+ from code_puppy.mcp_ import get_mcp_manager
117
117
 
118
118
  manager = get_mcp_manager()
119
119
  # Reload configurations
@@ -174,7 +174,7 @@ def reload_code_generation_agent(message_group: str | None):
174
174
 
175
175
  model_settings = ModelSettings(**model_settings_dict)
176
176
  if "gpt-5" in model_name:
177
- model_settings_dict["openai_reasoning_effort"] = "high"
177
+ model_settings_dict["openai_reasoning_effort"] = "off"
178
178
  model_settings_dict["extra_body"] = {
179
179
  "verbosity": "low"
180
180
  }
@@ -245,9 +245,6 @@ Best-practice guidelines for `invoke_agent`:
245
245
  Your solutions should be production-ready, maintainable, and follow best practices for the chosen language.
246
246
 
247
247
  Return your final response as a string output
248
- Walmart specific rules:
249
- - You are operating inside Walmart Global Tech! Yay!
250
- - Always use uv when working with python, and always use --index-url https://pypi.ci.artifacts.walmart.com/artifactory/api/pypi/external-pypi/simple
251
248
 
252
249
  ## Tool Templates:
253
250
 
@@ -0,0 +1,203 @@
1
+ """Quality Assurance Kitten - Playwright-powered browser automation agent."""
2
+
3
+ from .base_agent import BaseAgent
4
+
5
+
6
+ class QualityAssuranceKittenAgent(BaseAgent):
7
+ """Quality Assurance Kitten - Advanced browser automation with Playwright."""
8
+
9
+ @property
10
+ def name(self) -> str:
11
+ return "qa-kitten"
12
+
13
+ @property
14
+ def display_name(self) -> str:
15
+ return "Quality Assurance Kitten 🐱"
16
+
17
+ @property
18
+ def description(self) -> str:
19
+ return "Advanced web browser automation and quality assurance testing using Playwright with VQA capabilities"
20
+
21
+ def get_available_tools(self) -> list[str]:
22
+ """Get the list of tools available to Web Browser Puppy."""
23
+ return [
24
+ # Core agent tools
25
+ "agent_share_your_reasoning",
26
+ # Browser control and initialization
27
+ "browser_initialize",
28
+ "browser_close",
29
+ "browser_status",
30
+ "browser_new_page",
31
+ "browser_list_pages",
32
+ # Browser navigation
33
+ "browser_navigate",
34
+ "browser_get_page_info",
35
+ "browser_go_back",
36
+ "browser_go_forward",
37
+ "browser_reload",
38
+ "browser_wait_for_load",
39
+ # Element discovery (semantic locators preferred)
40
+ "browser_find_by_role",
41
+ "browser_find_by_text",
42
+ "browser_find_by_label",
43
+ "browser_find_by_placeholder",
44
+ "browser_find_by_test_id",
45
+ "browser_find_buttons",
46
+ "browser_find_links",
47
+ "browser_xpath_query", # Fallback when semantic locators fail
48
+ # Element interactions
49
+ "browser_click",
50
+ "browser_double_click",
51
+ "browser_hover",
52
+ "browser_set_text",
53
+ "browser_get_text",
54
+ "browser_get_value",
55
+ "browser_select_option",
56
+ "browser_check",
57
+ "browser_uncheck",
58
+ # Advanced features
59
+ "browser_execute_js",
60
+ "browser_scroll",
61
+ "browser_scroll_to_element",
62
+ "browser_set_viewport",
63
+ "browser_wait_for_element",
64
+ "browser_highlight_element",
65
+ "browser_clear_highlights",
66
+ # Screenshots and VQA
67
+ "browser_screenshot_analyze",
68
+ # Workflow management
69
+ "browser_save_workflow",
70
+ "browser_list_workflows",
71
+ "browser_read_workflow",
72
+ ]
73
+
74
+ def get_system_prompt(self) -> str:
75
+ """Get Web Browser Puppy's specialized system prompt."""
76
+ return """
77
+ You are Quality Assurance Kitten 🐱, an advanced autonomous browser automation and QA testing agent powered by Playwright!
78
+
79
+ You specialize in:
80
+ 🎯 **Quality Assurance Testing** - automated testing of web applications and user workflows
81
+ 👁️ **Visual verification** - taking screenshots and analyzing page content for bugs
82
+ 🔍 **Element discovery** - finding elements using semantic locators and accessibility best practices
83
+ 📝 **Data extraction** - scraping content and gathering information from web pages
84
+ 🧪 **Web automation** - filling forms, clicking buttons, navigating sites with precision
85
+ 🐛 **Bug detection** - identifying UI issues, broken functionality, and accessibility problems
86
+
87
+ ## Core Workflow Philosophy
88
+
89
+ For any browser task, follow this approach:
90
+ 1. **Check Existing Workflows**: Use browser_list_workflows to see if similar tasks have been solved before
91
+ 2. **Learn from History**: If relevant workflows exist, use browser_read_workflow to review proven strategies
92
+ 3. **Plan & Reason**: Use share_your_reasoning to break down complex tasks and explain your approach
93
+ 4. **Initialize**: Always start with browser_initialize if browser isn't running
94
+ 5. **Navigate**: Use browser_navigate to reach the target page
95
+ 6. **Discover**: Use semantic locators (PREFERRED) for element discovery
96
+ 7. **Verify**: Use highlighting and screenshots to confirm elements
97
+ 8. **Act**: Interact with elements through clicks, typing, etc.
98
+ 9. **Validate**: Take screenshots or query DOM to verify actions worked
99
+ 10. **Document Success**: Use browser_save_workflow to save successful patterns for future reuse
100
+
101
+ ## Tool Usage Guidelines
102
+
103
+ ### Browser Initialization
104
+ - **ALWAYS call browser_initialize first** before any other browser operations
105
+ - Choose appropriate settings: headless=False for debugging, headless=True for production
106
+ - Use browser_status to check current state
107
+
108
+ ### Element Discovery Best Practices (ACCESSIBILITY FIRST! 🌟)
109
+ - **PREFER semantic locators** - they're more reliable and follow accessibility standards
110
+ - Priority order:
111
+ 1. browser_find_by_role (button, link, textbox, heading, etc.)
112
+ 2. browser_find_by_label (for form inputs)
113
+ 3. browser_find_by_text (for visible text)
114
+ 4. browser_find_by_placeholder (for input hints)
115
+ 5. browser_find_by_test_id (for test-friendly elements)
116
+ 6. browser_xpath_query (ONLY as last resort)
117
+
118
+ ### Visual Verification Workflow
119
+ - **Before critical actions**: Use browser_highlight_element to visually confirm
120
+ - **After interactions**: Use browser_screenshot_analyze to verify results
121
+ - **VQA questions**: Ask specific, actionable questions like "Is the login button highlighted?"
122
+
123
+ ### Form Input Best Practices
124
+ - **ALWAYS check current values** with browser_get_value before typing
125
+ - Use browser_get_value after typing to verify success
126
+ - This prevents typing loops and gives clear visibility into form state
127
+ - Clear fields when appropriate before entering new text
128
+
129
+ ### Error Handling & Troubleshooting
130
+
131
+ **When Element Discovery Fails:**
132
+ 1. Try different semantic locators first
133
+ 2. Use browser_find_buttons or browser_find_links to see available elements
134
+ 3. Take a screenshot with browser_screenshot_analyze to understand the page layout
135
+ 4. Only use XPath as absolute last resort
136
+
137
+ **When Page Interactions Fail:**
138
+ 1. Check if element is visible with browser_wait_for_element
139
+ 2. Scroll element into view with browser_scroll_to_element
140
+ 3. Use browser_highlight_element to confirm element location
141
+ 4. Try browser_execute_js for complex interactions
142
+
143
+ ### JavaScript Execution
144
+ - Use browser_execute_js for:
145
+ - Complex page state checks
146
+ - Custom scrolling behavior
147
+ - Triggering events that standard tools can't handle
148
+ - Accessing browser APIs
149
+
150
+ ### Workflow Management 📋
151
+
152
+ **ALWAYS start new tasks by checking for existing workflows!**
153
+
154
+ **At the beginning of any automation task:**
155
+ 1. **browser_list_workflows** - Check what workflows are already available
156
+ 2. **browser_read_workflow** - If you find a relevant workflow, read it to understand the proven approach
157
+ 3. Adapt and apply the successful patterns from existing workflows
158
+
159
+ **When to save workflows:**
160
+ - After successfully completing a complex multi-step task
161
+ - When you discover a reliable pattern for a common website interaction
162
+ - After troubleshooting and finding working solutions for tricky elements
163
+ - Include both the successful steps AND the challenges/solutions you encountered
164
+
165
+ **Workflow naming conventions:**
166
+ - Use descriptive names like "search_and_atc_walmart", "login_to_github", "fill_contact_form"
167
+ - Include the website domain for clarity
168
+ - Focus on the main goal/outcome
169
+
170
+ **What to include in saved workflows:**
171
+ - Step-by-step tool usage with specific parameters
172
+ - Element discovery strategies that worked
173
+ - Common pitfalls and how to avoid them
174
+ - Alternative approaches for edge cases
175
+ - Tips for handling dynamic content
176
+
177
+ ### Performance & Best Practices
178
+ - Use appropriate timeouts for element discovery (default 10s is usually fine)
179
+ - Take screenshots strategically - not after every single action
180
+ - Use browser_wait_for_load when navigating to ensure pages are ready
181
+ - Clear highlights when done for clean visual state
182
+
183
+ ## Specialized Capabilities
184
+
185
+ 🌐 **WCAG 2.2 Level AA Compliance**: Always prioritize accessibility in element discovery
186
+ 📸 **Visual Question Answering**: Use browser_screenshot_analyze for intelligent page analysis
187
+ 🚀 **Semantic Web Navigation**: Prefer role-based and label-based element discovery
188
+ ⚡ **Playwright Power**: Full access to modern browser automation capabilities
189
+ 📋 **Workflow Management**: Save, load, and reuse automation patterns for consistency
190
+
191
+ ## Important Rules
192
+
193
+ - **ALWAYS check for existing workflows first** - Use browser_list_workflows at the start of new tasks
194
+ - **ALWAYS use browser_initialize before any browser operations**
195
+ - **PREFER semantic locators over XPath** - they're more maintainable and accessible
196
+ - **Use visual verification for critical actions** - highlight elements and take screenshots
197
+ - **Be explicit about your reasoning** - use share_your_reasoning for complex workflows
198
+ - **Handle errors gracefully** - provide helpful debugging information
199
+ - **Follow accessibility best practices** - your automation should work for everyone
200
+ - **Document your successes** - Save working patterns with browser_save_workflow for future reuse
201
+
202
+ Your browser automation should be reliable, maintainable, and accessible. You are a meticulous QA engineer who catches bugs before users do! 🐱✨
203
+ """
@@ -114,3 +114,12 @@ class BaseAgent(ABC):
114
114
  message_hash: Hash of a message that has been compacted/summarized.
115
115
  """
116
116
  self._compacted_message_hashes.add(message_hash)
117
+
118
+ def get_model_name(self) -> Optional[str]:
119
+ """Get pinned model name for this agent, if specified.
120
+
121
+ Returns:
122
+ Model name to use for this agent, or None to use global default.
123
+ """
124
+ from ..config import get_agent_pinned_model
125
+ return get_agent_pinned_model(self.name)
@@ -81,7 +81,9 @@ def get_commands_help():
81
81
  )
82
82
  help_lines.append(
83
83
  Text("/truncate", style="cyan")
84
- + Text(" <N> Truncate message history to N most recent messages (keeping system message)")
84
+ + Text(
85
+ " <N> Truncate message history to N most recent messages (keeping system message)"
86
+ )
85
87
  )
86
88
  help_lines.append(
87
89
  Text("/<unknown>", style="cyan")
@@ -409,23 +411,33 @@ def handle_command(command: str):
409
411
 
410
412
  if command.startswith("/pin_model"):
411
413
  # Handle agent model pinning
414
+ import json
415
+
412
416
  from code_puppy.agents.json_agent import discover_json_agents
413
417
  from code_puppy.command_line.model_picker_completion import load_model_names
414
- import json
415
418
 
416
419
  tokens = command.split()
417
420
 
418
421
  if len(tokens) != 3:
419
422
  emit_warning("Usage: /pin_model <agent-name> <model-name>")
420
423
 
421
- # Show available models and JSON agents
424
+ # Show available models and agents
422
425
  available_models = load_model_names()
423
426
  json_agents = discover_json_agents()
424
427
 
428
+ # Get built-in agents
429
+ from code_puppy.agents.agent_manager import get_agent_descriptions
430
+ builtin_agents = get_agent_descriptions()
431
+
425
432
  emit_info("Available models:")
426
433
  for model in available_models:
427
434
  emit_info(f" [cyan]{model}[/cyan]")
428
435
 
436
+ if builtin_agents:
437
+ emit_info("\nAvailable built-in agents:")
438
+ for agent_name, description in builtin_agents.items():
439
+ emit_info(f" [cyan]{agent_name}[/cyan] - {description}")
440
+
429
441
  if json_agents:
430
442
  emit_info("\nAvailable JSON agents:")
431
443
  for agent_name, agent_path in json_agents.items():
@@ -442,31 +454,51 @@ def handle_command(command: str):
442
454
  emit_warning(f"Available models: {', '.join(available_models)}")
443
455
  return True
444
456
 
445
- # Check that we're modifying a JSON agent (not a built-in Python agent)
457
+ # Check if this is a JSON agent or a built-in Python agent
446
458
  json_agents = discover_json_agents()
447
- if agent_name not in json_agents:
448
- emit_error(f"JSON agent '{agent_name}' not found")
449
459
 
450
- # Show available JSON agents
460
+ # Get list of available built-in agents
461
+ from code_puppy.agents.agent_manager import get_agent_descriptions
462
+ builtin_agents = get_agent_descriptions()
463
+
464
+ is_json_agent = agent_name in json_agents
465
+ is_builtin_agent = agent_name in builtin_agents
466
+
467
+ if not is_json_agent and not is_builtin_agent:
468
+ emit_error(f"Agent '{agent_name}' not found")
469
+
470
+ # Show available agents
471
+ if builtin_agents:
472
+ emit_info("Available built-in agents:")
473
+ for name, desc in builtin_agents.items():
474
+ emit_info(f" [cyan]{name}[/cyan] - {desc}")
475
+
451
476
  if json_agents:
452
- emit_info("Available JSON agents:")
477
+ emit_info("\nAvailable JSON agents:")
453
478
  for name, path in json_agents.items():
454
479
  emit_info(f" [cyan]{name}[/cyan] ({path})")
455
480
  return True
456
481
 
457
- agent_file_path = json_agents[agent_name]
458
-
459
- # Load, modify, and save the agent configuration
482
+ # Handle different agent types
460
483
  try:
461
- with open(agent_file_path, "r", encoding="utf-8") as f:
462
- agent_config = json.load(f)
484
+ if is_json_agent:
485
+ # Handle JSON agent - modify the JSON file
486
+ agent_file_path = json_agents[agent_name]
487
+
488
+ with open(agent_file_path, "r", encoding="utf-8") as f:
489
+ agent_config = json.load(f)
463
490
 
464
- # Set the model
465
- agent_config["model"] = model_name
491
+ # Set the model
492
+ agent_config["model"] = model_name
466
493
 
467
- # Save the updated configuration
468
- with open(agent_file_path, "w", encoding="utf-8") as f:
469
- json.dump(agent_config, f, indent=2, ensure_ascii=False)
494
+ # Save the updated configuration
495
+ with open(agent_file_path, "w", encoding="utf-8") as f:
496
+ json.dump(agent_config, f, indent=2, ensure_ascii=False)
497
+
498
+ else:
499
+ # Handle built-in Python agent - store in config
500
+ from code_puppy.config import set_agent_pinned_model
501
+ set_agent_pinned_model(agent_name, model_name)
470
502
 
471
503
  emit_success(f"Model '{model_name}' pinned to agent '{agent_name}'")
472
504
 
@@ -622,9 +654,11 @@ def handle_command(command: str):
622
654
  if command.startswith("/truncate"):
623
655
  tokens = command.split()
624
656
  if len(tokens) != 2:
625
- emit_error("Usage: /truncate <N> (where N is the number of messages to keep)")
657
+ emit_error(
658
+ "Usage: /truncate <N> (where N is the number of messages to keep)"
659
+ )
626
660
  return True
627
-
661
+
628
662
  try:
629
663
  n = int(tokens[1])
630
664
  if n < 1:
@@ -633,23 +667,29 @@ def handle_command(command: str):
633
667
  except ValueError:
634
668
  emit_error("N must be a valid integer")
635
669
  return True
636
-
670
+
637
671
  from code_puppy.state_management import get_message_history, set_message_history
638
-
672
+
639
673
  history = get_message_history()
640
674
  if not history:
641
675
  emit_warning("No history to truncate yet. Ask me something first!")
642
676
  return True
643
-
677
+
644
678
  if len(history) <= n:
645
- emit_info(f"History already has {len(history)} messages, which is <= {n}. Nothing to truncate.")
679
+ emit_info(
680
+ f"History already has {len(history)} messages, which is <= {n}. Nothing to truncate."
681
+ )
646
682
  return True
647
-
683
+
648
684
  # Always keep the first message (system message) and then keep the N-1 most recent messages
649
- truncated_history = [history[0]] + history[-(n-1):] if n > 1 else [history[0]]
650
-
685
+ truncated_history = (
686
+ [history[0]] + history[-(n - 1) :] if n > 1 else [history[0]]
687
+ )
688
+
651
689
  set_message_history(truncated_history)
652
- emit_success(f"Truncated message history from {len(history)} to {len(truncated_history)} messages (keeping system message and {n-1} most recent)")
690
+ emit_success(
691
+ f"Truncated message history from {len(history)} to {len(truncated_history)} messages (keeping system message and {n - 1} most recent)"
692
+ )
653
693
  return True
654
694
 
655
695
  if command in ("/exit", "/quit"):
@@ -130,7 +130,7 @@ class AddCommand(MCPCommandBase):
130
130
  """
131
131
  try:
132
132
  from code_puppy.config import MCP_SERVERS_FILE
133
- from code_puppy.mcp.managed_server import ServerConfig
133
+ from code_puppy.mcp_.managed_server import ServerConfig
134
134
 
135
135
  # Extract required fields
136
136
  name = config_dict.pop("name")
@@ -8,7 +8,7 @@ import logging
8
8
 
9
9
  from rich.console import Console
10
10
 
11
- from code_puppy.mcp.manager import get_mcp_manager
11
+ from code_puppy.mcp_.manager import get_mcp_manager
12
12
 
13
13
  # Configure logging
14
14
  logger = logging.getLogger(__name__)
@@ -76,7 +76,7 @@ class InstallCommand(MCPCommandBase):
76
76
  def _install_from_catalog(self, server_name_or_id: str, group_id: str) -> bool:
77
77
  """Install a server directly from the catalog by name or ID."""
78
78
  try:
79
- from code_puppy.mcp.server_registry_catalog import catalog
79
+ from code_puppy.mcp_.server_registry_catalog import catalog
80
80
  from code_puppy.messaging import emit_prompt
81
81
 
82
82
  from .utils import find_server_id_by_name
@@ -8,7 +8,7 @@ from typing import List, Optional
8
8
  from rich.table import Table
9
9
  from rich.text import Text
10
10
 
11
- from code_puppy.mcp.managed_server import ServerState
11
+ from code_puppy.mcp_.managed_server import ServerState
12
12
  from code_puppy.messaging import emit_info
13
13
 
14
14
  from .base import MCPCommandBase
@@ -34,7 +34,7 @@ class SearchCommand(MCPCommandBase):
34
34
  group_id = self.generate_group_id()
35
35
 
36
36
  try:
37
- from code_puppy.mcp.server_registry_catalog import catalog
37
+ from code_puppy.mcp_.server_registry_catalog import catalog
38
38
 
39
39
  if not args:
40
40
  # Show popular servers if no query
@@ -6,7 +6,7 @@ import logging
6
6
  import time
7
7
  from typing import List, Optional
8
8
 
9
- from code_puppy.mcp.managed_server import ServerState
9
+ from code_puppy.mcp_.managed_server import ServerState
10
10
  from code_puppy.messaging import emit_info
11
11
 
12
12
  from .base import MCPCommandBase
@@ -8,7 +8,7 @@ from typing import List, Optional
8
8
 
9
9
  from rich.panel import Panel
10
10
 
11
- from code_puppy.mcp.managed_server import ServerState
11
+ from code_puppy.mcp_.managed_server import ServerState
12
12
  from code_puppy.messaging import emit_info
13
13
 
14
14
  from .base import MCPCommandBase
@@ -117,7 +117,7 @@ class StatusCommand(MCPCommandBase):
117
117
 
118
118
  # Check async lifecycle manager status if available
119
119
  try:
120
- from code_puppy.mcp.async_lifecycle import get_lifecycle_manager
120
+ from code_puppy.mcp_.async_lifecycle import get_lifecycle_manager
121
121
 
122
122
  lifecycle_mgr = get_lifecycle_manager()
123
123
  if lifecycle_mgr.is_running(server_id):
@@ -6,7 +6,7 @@ import logging
6
6
  import time
7
7
  from typing import List, Optional
8
8
 
9
- from code_puppy.mcp.managed_server import ServerState
9
+ from code_puppy.mcp_.managed_server import ServerState
10
10
  from code_puppy.messaging import emit_info
11
11
 
12
12
  from .base import MCPCommandBase
@@ -8,7 +8,7 @@ from typing import Optional
8
8
 
9
9
  from rich.text import Text
10
10
 
11
- from code_puppy.mcp.managed_server import ServerState
11
+ from code_puppy.mcp_.managed_server import ServerState
12
12
 
13
13
 
14
14
  def format_state_indicator(state: ServerState) -> Text:
@@ -118,7 +118,7 @@ def interactive_server_selection(group_id: str):
118
118
  # This is a simplified version - the full implementation would have
119
119
  # category browsing, search, etc. For now, we'll just show popular servers
120
120
  try:
121
- from code_puppy.mcp.server_registry_catalog import catalog
121
+ from code_puppy.mcp_.server_registry_catalog import catalog
122
122
 
123
123
  servers = catalog.get_popular(10)
124
124
  if not servers:
@@ -256,7 +256,7 @@ def install_server_from_catalog(
256
256
  import os
257
257
 
258
258
  from code_puppy.config import MCP_SERVERS_FILE
259
- from code_puppy.mcp.managed_server import ServerConfig
259
+ from code_puppy.mcp_.managed_server import ServerConfig
260
260
 
261
261
  # Set environment variables in the current environment
262
262
  for var, value in env_vars.items():