code-puppy 0.0.337__tar.gz → 0.0.339__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.
- {code_puppy-0.0.337 → code_puppy-0.0.339}/PKG-INFO +19 -71
- {code_puppy-0.0.337 → code_puppy-0.0.339}/README.md +16 -70
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/base_agent.py +55 -30
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/claude_cache_client.py +46 -2
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/cli_runner.py +45 -32
- code_puppy-0.0.339/code_puppy/command_line/clipboard.py +527 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/core_commands.py +34 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/prompt_toolkit_completion.py +112 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/model_factory.py +16 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/models.json +2 -2
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/claude_code_oauth/utils.py +126 -7
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/terminal_utils.py +128 -1
- {code_puppy-0.0.337 → code_puppy-0.0.339}/pyproject.toml +3 -1
- {code_puppy-0.0.337 → code_puppy-0.0.339}/.gitignore +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/LICENSE +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/__main__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_c_reviewer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_code_puppy.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_code_reviewer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_cpp_reviewer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_creator_agent.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_golang_reviewer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_javascript_reviewer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_manager.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_planning.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_python_programmer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_python_reviewer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_qa_expert.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_qa_kitten.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_security_auditor.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/agent_typescript_reviewer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/json_agent.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/agents/prompt_reviewer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/callbacks.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/chatgpt_codex_client.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/add_model_menu.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/attachments.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/autosave_menu.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/colors_menu.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/command_handler.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/command_registry.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/config_commands.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/diff_menu.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/file_path_completion.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/load_context_completion.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/add_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/base.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/edit_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/handler.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/help_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/install_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/install_menu.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/list_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/logs_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/remove_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/restart_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/search_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/start_all_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/start_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/status_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/stop_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/test_command.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/utils.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/mcp_completion.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/model_picker_completion.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/model_settings_menu.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/motd.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/onboarding_slides.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/onboarding_wizard.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/pin_command_completion.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/session_commands.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/command_line/utils.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/config.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/error_logging.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/gemini_code_assist.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/http_utils.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/keymap.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/main.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/async_lifecycle.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/blocking_startup.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/captured_stdio_server.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/circuit_breaker.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/config_wizard.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/dashboard.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/error_isolation.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/examples/retry_example.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/health_monitor.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/managed_server.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/manager.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/mcp_logs.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/registry.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/retry_manager.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/server_registry_catalog.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/status_tracker.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/mcp_/system_tools.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/bus.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/commands.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/markdown_patches.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/message_queue.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/messages.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/queue_console.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/renderers.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/rich_renderer.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/spinner/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/spinner/console_spinner.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/messaging/spinner/spinner_base.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/model_utils.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/models_dev_parser.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/accounts.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/antigravity_model.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/config.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/constants.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/oauth.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/storage.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/token.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/transport.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/antigravity_oauth/utils.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/chatgpt_oauth/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/chatgpt_oauth/config.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/chatgpt_oauth/oauth_flow.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/chatgpt_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/chatgpt_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/chatgpt_oauth/utils.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/claude_code_oauth/README.md +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/claude_code_oauth/SETUP.md +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/claude_code_oauth/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/claude_code_oauth/config.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/claude_code_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/claude_code_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/customizable_commands/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/customizable_commands/register_callbacks.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/example_custom_command/README.md +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/file_permission_handler/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/file_permission_handler/register_callbacks.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/oauth_puppy_html.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/shell_safety/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/shell_safety/agent_shell_safety.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/shell_safety/command_cache.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/plugins/shell_safety/register_callbacks.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/prompts/codex_system_prompt.md +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/pydantic_patches.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/reopenable_async_client.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/round_robin_model.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/session_storage.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/status_display.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/summarization_agent.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/agent_tools.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/browser/__init__.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/browser/browser_control.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/browser/browser_interactions.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/browser/browser_locators.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/browser/browser_navigation.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/browser/browser_screenshot.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/browser/browser_scripts.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/browser/browser_workflows.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/browser/camoufox_manager.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/browser/vqa_agent.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/command_runner.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/common.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/file_modifications.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/file_operations.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/tools/tools_content.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/code_puppy/uvx_detection.py +0 -0
- {code_puppy-0.0.337 → code_puppy-0.0.339}/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.
|
|
3
|
+
Version: 0.0.339
|
|
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
|
|
@@ -22,6 +22,7 @@ Requires-Dist: httpx[http2]>=0.24.1
|
|
|
22
22
|
Requires-Dist: json-repair>=0.46.2
|
|
23
23
|
Requires-Dist: logfire>=0.7.1
|
|
24
24
|
Requires-Dist: openai>=1.99.1
|
|
25
|
+
Requires-Dist: pillow>=10.0.0
|
|
25
26
|
Requires-Dist: playwright>=1.40.0
|
|
26
27
|
Requires-Dist: prompt-toolkit>=3.0.52
|
|
27
28
|
Requires-Dist: pydantic-ai==1.25.0
|
|
@@ -34,6 +35,7 @@ Requires-Dist: rich>=13.4.2
|
|
|
34
35
|
Requires-Dist: ripgrep==14.1.0
|
|
35
36
|
Requires-Dist: ruff>=0.11.11
|
|
36
37
|
Requires-Dist: tenacity>=8.2.0
|
|
38
|
+
Requires-Dist: termflow-md>=0.1.6
|
|
37
39
|
Requires-Dist: uvicorn>=0.30.0
|
|
38
40
|
Description-Content-Type: text/markdown
|
|
39
41
|
|
|
@@ -106,12 +108,7 @@ uvx code-puppy -i
|
|
|
106
108
|
# Install UV if you don't have it
|
|
107
109
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
echo 'export UV_MANAGED_PYTHON=1' >> ~/.zshrc # or ~/.bashrc
|
|
111
|
-
source ~/.zshrc # or ~/.bashrc
|
|
112
|
-
|
|
113
|
-
# Install and run code-puppy
|
|
114
|
-
uvx code-puppy -i
|
|
111
|
+
uvx code-puppy
|
|
115
112
|
```
|
|
116
113
|
|
|
117
114
|
#### Windows
|
|
@@ -122,73 +119,15 @@ On Windows, we recommend installing code-puppy as a global tool for the best exp
|
|
|
122
119
|
# Install UV if you don't have it (run in PowerShell as Admin)
|
|
123
120
|
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
124
121
|
|
|
125
|
-
|
|
126
|
-
uv tool install code-puppy
|
|
127
|
-
|
|
128
|
-
# Run code-puppy
|
|
129
|
-
code-puppy -i
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**Why `uv tool install` on Windows?** Running with `uvx` creates an extra process layer that can interfere with keyboard signal handling (Ctrl+C, Ctrl+X). Installing as a tool runs code-puppy directly for reliable cancellation.
|
|
133
|
-
|
|
134
|
-
#### Upgrading
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
# Upgrade code-puppy to the latest version
|
|
138
|
-
uv tool upgrade code-puppy
|
|
139
|
-
|
|
140
|
-
# Or upgrade all installed tools
|
|
141
|
-
uv tool upgrade --all
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
UV will automatically download the latest compatible Python version (3.11+) if your system doesn't have one.
|
|
145
|
-
|
|
146
|
-
### pip (Alternative)
|
|
147
|
-
|
|
148
|
-
```bash
|
|
149
|
-
pip install code-puppy
|
|
122
|
+
uvx code-puppy
|
|
150
123
|
```
|
|
151
124
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
### Permanent Python Management
|
|
155
|
-
|
|
156
|
-
To make UV always use managed Python versions (recommended):
|
|
157
|
-
|
|
158
|
-
```bash
|
|
159
|
-
# Set environment variable permanently
|
|
160
|
-
echo 'export UV_MANAGED_PYTHON=1' >> ~/.zshrc # or ~/.bashrc
|
|
161
|
-
source ~/.zshrc # or ~/.bashrc
|
|
162
|
-
|
|
163
|
-
# Now all UV commands will prefer managed Python installations
|
|
164
|
-
uvx code-puppy # No need for --managed-python flag anymore
|
|
165
|
-
```
|
|
125
|
+
## Changelog (By Kittylog!)
|
|
166
126
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
```bash
|
|
170
|
-
# Check which Python UV will use
|
|
171
|
-
uv python find
|
|
172
|
-
|
|
173
|
-
# Or check the current project's Python
|
|
174
|
-
uv run python --version
|
|
175
|
-
```
|
|
127
|
+
[📋 View the full changelog on Kittylog](https://kittylog.app/c/mpfaffenberger/code_puppy)
|
|
176
128
|
|
|
177
129
|
## Usage
|
|
178
130
|
|
|
179
|
-
### Custom Commands
|
|
180
|
-
Create markdown files in `.claude/commands/`, `.github/prompts/`, or `.agents/commands/` to define custom slash commands. The filename becomes the command name and the content runs as a prompt.
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
# Create a custom command
|
|
184
|
-
echo "# Code Review
|
|
185
|
-
|
|
186
|
-
Please review this code for security issues." > .claude/commands/review.md
|
|
187
|
-
|
|
188
|
-
# Use it in Code Puppy
|
|
189
|
-
/review with focus on authentication
|
|
190
|
-
```
|
|
191
|
-
|
|
192
131
|
### Adding Models from models.dev 🆕
|
|
193
132
|
|
|
194
133
|
While there are several models configured right out of the box from providers like Synthetic, Cerebras, OpenAI, Google, and Anthropic, Code Puppy integrates with [models.dev](https://models.dev) to let you browse and add models from **65+ providers** with a single command:
|
|
@@ -256,6 +195,18 @@ The following environment variables control DBOS behavior:
|
|
|
256
195
|
- `DBOS_SYSTEM_DATABASE_URL`: Database URL used by DBOS. Can point to a local SQLite file or a Postgres instance. Example: `postgresql://postgres:dbos@localhost:5432/postgres`. Default: `dbos_store.sqlite` file in the config directory.
|
|
257
196
|
- `DBOS_APP_VERSION`: If set, Code Puppy uses it as the [DBOS application version](https://docs.dbos.dev/architecture#application-and-workflow-versions) and automatically tries to recover pending workflows for this version. Default: Code Puppy version + Unix timestamp in millisecond (disable automatic recovery).
|
|
258
197
|
|
|
198
|
+
### Custom Commands
|
|
199
|
+
Create markdown files in `.claude/commands/`, `.github/prompts/`, or `.agents/commands/` to define custom slash commands. The filename becomes the command name and the content runs as a prompt.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Create a custom command
|
|
203
|
+
echo "# Code Review
|
|
204
|
+
|
|
205
|
+
Please review this code for security issues." > .claude/commands/review.md
|
|
206
|
+
|
|
207
|
+
# Use it in Code Puppy
|
|
208
|
+
/review with focus on authentication
|
|
209
|
+
```
|
|
259
210
|
|
|
260
211
|
## Requirements
|
|
261
212
|
|
|
@@ -275,9 +226,6 @@ For examples and more information about agent rules, visit [https://agent.md](ht
|
|
|
275
226
|
|
|
276
227
|
Use the `/mcp` command to manage MCP (list, start, stop, status, etc.)
|
|
277
228
|
|
|
278
|
-
Watch this video for examples! https://www.youtube.com/watch?v=1t1zEetOqlo
|
|
279
|
-
|
|
280
|
-
|
|
281
229
|
## Round Robin Model Distribution
|
|
282
230
|
|
|
283
231
|
Code Puppy supports **Round Robin model distribution** to help you overcome rate limits and distribute load across multiple AI models. This feature automatically cycles through configured models with each request, maximizing your API usage while staying within rate limits.
|
|
@@ -67,12 +67,7 @@ uvx code-puppy -i
|
|
|
67
67
|
# Install UV if you don't have it
|
|
68
68
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
echo 'export UV_MANAGED_PYTHON=1' >> ~/.zshrc # or ~/.bashrc
|
|
72
|
-
source ~/.zshrc # or ~/.bashrc
|
|
73
|
-
|
|
74
|
-
# Install and run code-puppy
|
|
75
|
-
uvx code-puppy -i
|
|
70
|
+
uvx code-puppy
|
|
76
71
|
```
|
|
77
72
|
|
|
78
73
|
#### Windows
|
|
@@ -83,73 +78,15 @@ On Windows, we recommend installing code-puppy as a global tool for the best exp
|
|
|
83
78
|
# Install UV if you don't have it (run in PowerShell as Admin)
|
|
84
79
|
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
85
80
|
|
|
86
|
-
|
|
87
|
-
uv tool install code-puppy
|
|
88
|
-
|
|
89
|
-
# Run code-puppy
|
|
90
|
-
code-puppy -i
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
**Why `uv tool install` on Windows?** Running with `uvx` creates an extra process layer that can interfere with keyboard signal handling (Ctrl+C, Ctrl+X). Installing as a tool runs code-puppy directly for reliable cancellation.
|
|
94
|
-
|
|
95
|
-
#### Upgrading
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
# Upgrade code-puppy to the latest version
|
|
99
|
-
uv tool upgrade code-puppy
|
|
100
|
-
|
|
101
|
-
# Or upgrade all installed tools
|
|
102
|
-
uv tool upgrade --all
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
UV will automatically download the latest compatible Python version (3.11+) if your system doesn't have one.
|
|
106
|
-
|
|
107
|
-
### pip (Alternative)
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
pip install code-puppy
|
|
81
|
+
uvx code-puppy
|
|
111
82
|
```
|
|
112
83
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
### Permanent Python Management
|
|
116
|
-
|
|
117
|
-
To make UV always use managed Python versions (recommended):
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
# Set environment variable permanently
|
|
121
|
-
echo 'export UV_MANAGED_PYTHON=1' >> ~/.zshrc # or ~/.bashrc
|
|
122
|
-
source ~/.zshrc # or ~/.bashrc
|
|
123
|
-
|
|
124
|
-
# Now all UV commands will prefer managed Python installations
|
|
125
|
-
uvx code-puppy # No need for --managed-python flag anymore
|
|
126
|
-
```
|
|
84
|
+
## Changelog (By Kittylog!)
|
|
127
85
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
```bash
|
|
131
|
-
# Check which Python UV will use
|
|
132
|
-
uv python find
|
|
133
|
-
|
|
134
|
-
# Or check the current project's Python
|
|
135
|
-
uv run python --version
|
|
136
|
-
```
|
|
86
|
+
[📋 View the full changelog on Kittylog](https://kittylog.app/c/mpfaffenberger/code_puppy)
|
|
137
87
|
|
|
138
88
|
## Usage
|
|
139
89
|
|
|
140
|
-
### Custom Commands
|
|
141
|
-
Create markdown files in `.claude/commands/`, `.github/prompts/`, or `.agents/commands/` to define custom slash commands. The filename becomes the command name and the content runs as a prompt.
|
|
142
|
-
|
|
143
|
-
```bash
|
|
144
|
-
# Create a custom command
|
|
145
|
-
echo "# Code Review
|
|
146
|
-
|
|
147
|
-
Please review this code for security issues." > .claude/commands/review.md
|
|
148
|
-
|
|
149
|
-
# Use it in Code Puppy
|
|
150
|
-
/review with focus on authentication
|
|
151
|
-
```
|
|
152
|
-
|
|
153
90
|
### Adding Models from models.dev 🆕
|
|
154
91
|
|
|
155
92
|
While there are several models configured right out of the box from providers like Synthetic, Cerebras, OpenAI, Google, and Anthropic, Code Puppy integrates with [models.dev](https://models.dev) to let you browse and add models from **65+ providers** with a single command:
|
|
@@ -217,6 +154,18 @@ The following environment variables control DBOS behavior:
|
|
|
217
154
|
- `DBOS_SYSTEM_DATABASE_URL`: Database URL used by DBOS. Can point to a local SQLite file or a Postgres instance. Example: `postgresql://postgres:dbos@localhost:5432/postgres`. Default: `dbos_store.sqlite` file in the config directory.
|
|
218
155
|
- `DBOS_APP_VERSION`: If set, Code Puppy uses it as the [DBOS application version](https://docs.dbos.dev/architecture#application-and-workflow-versions) and automatically tries to recover pending workflows for this version. Default: Code Puppy version + Unix timestamp in millisecond (disable automatic recovery).
|
|
219
156
|
|
|
157
|
+
### Custom Commands
|
|
158
|
+
Create markdown files in `.claude/commands/`, `.github/prompts/`, or `.agents/commands/` to define custom slash commands. The filename becomes the command name and the content runs as a prompt.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Create a custom command
|
|
162
|
+
echo "# Code Review
|
|
163
|
+
|
|
164
|
+
Please review this code for security issues." > .claude/commands/review.md
|
|
165
|
+
|
|
166
|
+
# Use it in Code Puppy
|
|
167
|
+
/review with focus on authentication
|
|
168
|
+
```
|
|
220
169
|
|
|
221
170
|
## Requirements
|
|
222
171
|
|
|
@@ -236,9 +185,6 @@ For examples and more information about agent rules, visit [https://agent.md](ht
|
|
|
236
185
|
|
|
237
186
|
Use the `/mcp` command to manage MCP (list, start, stop, status, etc.)
|
|
238
187
|
|
|
239
|
-
Watch this video for examples! https://www.youtube.com/watch?v=1t1zEetOqlo
|
|
240
|
-
|
|
241
|
-
|
|
242
188
|
## Round Robin Model Distribution
|
|
243
189
|
|
|
244
190
|
Code Puppy supports **Round Robin model distribution** to help you overcome rate limits and distribute load across multiple AI models. This feature automatically cycles through configured models with each request, maximizing your API usage while staying within rate limits.
|
|
@@ -1353,7 +1353,6 @@ class BaseAgent(ABC):
|
|
|
1353
1353
|
ToolCallPartDelta,
|
|
1354
1354
|
)
|
|
1355
1355
|
from rich.console import Console
|
|
1356
|
-
from rich.markdown import Markdown
|
|
1357
1356
|
from rich.markup import escape
|
|
1358
1357
|
|
|
1359
1358
|
from code_puppy.messaging.spinner import pause_all_spinners
|
|
@@ -1375,10 +1374,17 @@ class BaseAgent(ABC):
|
|
|
1375
1374
|
text_parts: set[int] = set() # Track which parts are text
|
|
1376
1375
|
tool_parts: set[int] = set() # Track which parts are tool calls
|
|
1377
1376
|
banner_printed: set[int] = set() # Track if banner was already printed
|
|
1378
|
-
text_buffer: dict[int, list[str]] = {} # Buffer text for final markdown render
|
|
1379
1377
|
token_count: dict[int, int] = {} # Track token count per text/tool part
|
|
1380
1378
|
did_stream_anything = False # Track if we streamed any content
|
|
1381
1379
|
|
|
1380
|
+
# Termflow streaming state for text parts
|
|
1381
|
+
from termflow import Parser as TermflowParser
|
|
1382
|
+
from termflow import Renderer as TermflowRenderer
|
|
1383
|
+
|
|
1384
|
+
termflow_parsers: dict[int, TermflowParser] = {}
|
|
1385
|
+
termflow_renderers: dict[int, TermflowRenderer] = {}
|
|
1386
|
+
termflow_line_buffers: dict[int, str] = {} # Buffer incomplete lines
|
|
1387
|
+
|
|
1382
1388
|
def _print_thinking_banner() -> None:
|
|
1383
1389
|
"""Print the THINKING banner with spinner pause and line clear."""
|
|
1384
1390
|
nonlocal did_stream_anything
|
|
@@ -1437,13 +1443,17 @@ class BaseAgent(ABC):
|
|
|
1437
1443
|
elif isinstance(part, TextPart):
|
|
1438
1444
|
streaming_parts.add(event.index)
|
|
1439
1445
|
text_parts.add(event.index)
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1446
|
+
# Initialize termflow streaming for this text part
|
|
1447
|
+
termflow_parsers[event.index] = TermflowParser()
|
|
1448
|
+
termflow_renderers[event.index] = TermflowRenderer(
|
|
1449
|
+
output=console.file, width=console.width
|
|
1450
|
+
)
|
|
1451
|
+
termflow_line_buffers[event.index] = ""
|
|
1452
|
+
# Handle initial content if present
|
|
1443
1453
|
if part.content and part.content.strip():
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1454
|
+
_print_response_banner()
|
|
1455
|
+
banner_printed.add(event.index)
|
|
1456
|
+
termflow_line_buffers[event.index] = part.content
|
|
1447
1457
|
elif isinstance(part, ToolCallPart):
|
|
1448
1458
|
streaming_parts.add(event.index)
|
|
1449
1459
|
tool_parts.add(event.index)
|
|
@@ -1459,22 +1469,29 @@ class BaseAgent(ABC):
|
|
|
1459
1469
|
delta = event.delta
|
|
1460
1470
|
if isinstance(delta, (TextPartDelta, ThinkingPartDelta)):
|
|
1461
1471
|
if delta.content_delta:
|
|
1462
|
-
# For text parts,
|
|
1472
|
+
# For text parts, stream markdown with termflow
|
|
1463
1473
|
if event.index in text_parts:
|
|
1464
1474
|
# Print banner on first content
|
|
1465
1475
|
if event.index not in banner_printed:
|
|
1466
1476
|
_print_response_banner()
|
|
1467
1477
|
banner_printed.add(event.index)
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
# Update chunk counter in place (single line)
|
|
1473
|
-
count = token_count[event.index]
|
|
1474
|
-
console.print(
|
|
1475
|
-
f" ⏳ Receiving... {count} chunks ",
|
|
1476
|
-
end="\r",
|
|
1478
|
+
|
|
1479
|
+
# Add content to line buffer
|
|
1480
|
+
termflow_line_buffers[event.index] += (
|
|
1481
|
+
delta.content_delta
|
|
1477
1482
|
)
|
|
1483
|
+
|
|
1484
|
+
# Process complete lines
|
|
1485
|
+
parser = termflow_parsers[event.index]
|
|
1486
|
+
renderer = termflow_renderers[event.index]
|
|
1487
|
+
buffer = termflow_line_buffers[event.index]
|
|
1488
|
+
|
|
1489
|
+
while "\n" in buffer:
|
|
1490
|
+
line, buffer = buffer.split("\n", 1)
|
|
1491
|
+
events_to_render = parser.parse_line(line)
|
|
1492
|
+
renderer.render_all(events_to_render)
|
|
1493
|
+
|
|
1494
|
+
termflow_line_buffers[event.index] = buffer
|
|
1478
1495
|
else:
|
|
1479
1496
|
# For thinking parts, stream immediately (dim)
|
|
1480
1497
|
if event.index not in banner_printed:
|
|
@@ -1503,19 +1520,27 @@ class BaseAgent(ABC):
|
|
|
1503
1520
|
# PartEndEvent - finish the streaming with a newline
|
|
1504
1521
|
elif isinstance(event, PartEndEvent):
|
|
1505
1522
|
if event.index in streaming_parts:
|
|
1506
|
-
# For text parts,
|
|
1523
|
+
# For text parts, finalize termflow rendering
|
|
1507
1524
|
if event.index in text_parts:
|
|
1508
|
-
#
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1525
|
+
# Render any remaining buffered content
|
|
1526
|
+
if event.index in termflow_parsers:
|
|
1527
|
+
parser = termflow_parsers[event.index]
|
|
1528
|
+
renderer = termflow_renderers[event.index]
|
|
1529
|
+
remaining = termflow_line_buffers.get(event.index, "")
|
|
1530
|
+
|
|
1531
|
+
# Parse and render any remaining partial line
|
|
1532
|
+
if remaining.strip():
|
|
1533
|
+
events_to_render = parser.parse_line(remaining)
|
|
1534
|
+
renderer.render_all(events_to_render)
|
|
1535
|
+
|
|
1536
|
+
# Finalize the parser to close any open blocks
|
|
1537
|
+
final_events = parser.finalize()
|
|
1538
|
+
renderer.render_all(final_events)
|
|
1539
|
+
|
|
1540
|
+
# Clean up termflow state
|
|
1541
|
+
del termflow_parsers[event.index]
|
|
1542
|
+
del termflow_renderers[event.index]
|
|
1543
|
+
del termflow_line_buffers[event.index]
|
|
1519
1544
|
# For tool parts, clear the chunk counter line
|
|
1520
1545
|
elif event.index in tool_parts:
|
|
1521
1546
|
# Clear the chunk counter line by printing spaces and returning
|
|
@@ -10,7 +10,7 @@ serialization, avoiding httpx/Pydantic internals.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import json
|
|
13
|
-
from typing import Any, Callable
|
|
13
|
+
from typing import Any, Callable, MutableMapping
|
|
14
14
|
|
|
15
15
|
import httpx
|
|
16
16
|
|
|
@@ -56,7 +56,28 @@ class ClaudeCacheAsyncClient(httpx.AsyncClient):
|
|
|
56
56
|
except Exception:
|
|
57
57
|
# Swallow wrapper errors; do not break real calls.
|
|
58
58
|
pass
|
|
59
|
-
|
|
59
|
+
response = await super().send(request, *args, **kwargs)
|
|
60
|
+
try:
|
|
61
|
+
if response.status_code == 401 and not request.extensions.get(
|
|
62
|
+
"claude_oauth_refresh_attempted"
|
|
63
|
+
):
|
|
64
|
+
refreshed_token = self._refresh_claude_oauth_token()
|
|
65
|
+
if refreshed_token:
|
|
66
|
+
await response.aclose()
|
|
67
|
+
body_bytes = self._extract_body_bytes(request)
|
|
68
|
+
headers = dict(request.headers)
|
|
69
|
+
self._update_auth_headers(headers, refreshed_token)
|
|
70
|
+
retry_request = self.build_request(
|
|
71
|
+
method=request.method,
|
|
72
|
+
url=request.url,
|
|
73
|
+
headers=headers,
|
|
74
|
+
content=body_bytes,
|
|
75
|
+
)
|
|
76
|
+
retry_request.extensions["claude_oauth_refresh_attempted"] = True
|
|
77
|
+
return await super().send(retry_request, *args, **kwargs)
|
|
78
|
+
except Exception:
|
|
79
|
+
pass
|
|
80
|
+
return response
|
|
60
81
|
|
|
61
82
|
@staticmethod
|
|
62
83
|
def _extract_body_bytes(request: httpx.Request) -> bytes | None:
|
|
@@ -78,6 +99,29 @@ class ClaudeCacheAsyncClient(httpx.AsyncClient):
|
|
|
78
99
|
|
|
79
100
|
return None
|
|
80
101
|
|
|
102
|
+
@staticmethod
|
|
103
|
+
def _update_auth_headers(
|
|
104
|
+
headers: MutableMapping[str, str], access_token: str
|
|
105
|
+
) -> None:
|
|
106
|
+
bearer_value = f"Bearer {access_token}"
|
|
107
|
+
if "Authorization" in headers or "authorization" in headers:
|
|
108
|
+
headers["Authorization"] = bearer_value
|
|
109
|
+
elif "x-api-key" in headers or "X-API-Key" in headers:
|
|
110
|
+
headers["x-api-key"] = access_token
|
|
111
|
+
else:
|
|
112
|
+
headers["Authorization"] = bearer_value
|
|
113
|
+
|
|
114
|
+
def _refresh_claude_oauth_token(self) -> str | None:
|
|
115
|
+
try:
|
|
116
|
+
from code_puppy.plugins.claude_code_oauth.utils import refresh_access_token
|
|
117
|
+
|
|
118
|
+
refreshed_token = refresh_access_token(force=True)
|
|
119
|
+
if refreshed_token:
|
|
120
|
+
self._update_auth_headers(self.headers, refreshed_token)
|
|
121
|
+
return refreshed_token
|
|
122
|
+
except Exception:
|
|
123
|
+
return None
|
|
124
|
+
|
|
81
125
|
@staticmethod
|
|
82
126
|
def _inject_cache_control(body: bytes) -> bytes | None:
|
|
83
127
|
try:
|
|
@@ -17,14 +17,12 @@ import traceback
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
19
|
from dbos import DBOS, DBOSConfig
|
|
20
|
-
from rich.console import Console
|
|
21
|
-
from rich.markdown import CodeBlock, Markdown
|
|
22
|
-
from rich.syntax import Syntax
|
|
23
|
-
from rich.text import Text
|
|
20
|
+
from rich.console import Console
|
|
24
21
|
|
|
25
22
|
from code_puppy import __version__, callbacks, plugins
|
|
26
23
|
from code_puppy.agents import get_current_agent
|
|
27
24
|
from code_puppy.command_line.attachments import parse_prompt_attachments
|
|
25
|
+
from code_puppy.command_line.clipboard import get_clipboard_manager
|
|
28
26
|
from code_puppy.config import (
|
|
29
27
|
AUTOSAVE_DIR,
|
|
30
28
|
COMMAND_HISTORY_FILE,
|
|
@@ -43,6 +41,7 @@ from code_puppy.keymap import (
|
|
|
43
41
|
)
|
|
44
42
|
from code_puppy.messaging import emit_info
|
|
45
43
|
from code_puppy.terminal_utils import (
|
|
44
|
+
print_truecolor_warning,
|
|
46
45
|
reset_unix_terminal,
|
|
47
46
|
reset_windows_terminal_ansi,
|
|
48
47
|
reset_windows_terminal_full,
|
|
@@ -91,7 +90,6 @@ async def main():
|
|
|
91
90
|
"command", nargs="*", help="Run a single command (deprecated, use -p instead)"
|
|
92
91
|
)
|
|
93
92
|
args = parser.parse_args()
|
|
94
|
-
from rich.console import Console
|
|
95
93
|
|
|
96
94
|
from code_puppy.messaging import (
|
|
97
95
|
RichConsoleRenderer,
|
|
@@ -146,6 +144,9 @@ async def main():
|
|
|
146
144
|
except ImportError:
|
|
147
145
|
emit_system_message("🐶 Code Puppy is Loading...")
|
|
148
146
|
|
|
147
|
+
# Check for truecolor support and warn if not available
|
|
148
|
+
print_truecolor_warning(display_console)
|
|
149
|
+
|
|
149
150
|
available_port = find_available_port()
|
|
150
151
|
if available_port is None:
|
|
151
152
|
emit_error("No available ports in range 8090-9010!")
|
|
@@ -354,6 +355,13 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
354
355
|
emit_system_message(
|
|
355
356
|
"Type @ for path completion, or /model to pick a model. Toggle multiline with Alt+M or F2; newline: Ctrl+J."
|
|
356
357
|
)
|
|
358
|
+
emit_system_message("Paste images: Ctrl+V (even on Mac!), F3, or /paste command.")
|
|
359
|
+
import platform
|
|
360
|
+
|
|
361
|
+
if platform.system() == "Darwin":
|
|
362
|
+
emit_system_message(
|
|
363
|
+
"💡 macOS tip: Use Ctrl+V (not Cmd+V) to paste images in terminal."
|
|
364
|
+
)
|
|
357
365
|
cancel_key = get_cancel_agent_display_name()
|
|
358
366
|
emit_system_message(
|
|
359
367
|
f"Press {cancel_key} during processing to cancel the current task or inference. Use Ctrl+X to interrupt running shell commands."
|
|
@@ -567,6 +575,7 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
567
575
|
|
|
568
576
|
# Check for clear command (supports both `clear` and `/clear`)
|
|
569
577
|
if task.strip().lower() in ("clear", "/clear"):
|
|
578
|
+
from code_puppy.command_line.clipboard import get_clipboard_manager
|
|
570
579
|
from code_puppy.messaging import (
|
|
571
580
|
emit_info,
|
|
572
581
|
emit_system_message,
|
|
@@ -579,6 +588,13 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
579
588
|
emit_warning("Conversation history cleared!")
|
|
580
589
|
emit_system_message("The agent will not remember previous interactions.")
|
|
581
590
|
emit_info(f"Auto-save session rotated to: {new_session_id}")
|
|
591
|
+
|
|
592
|
+
# Also clear pending clipboard images
|
|
593
|
+
clipboard_manager = get_clipboard_manager()
|
|
594
|
+
clipboard_count = clipboard_manager.get_pending_count()
|
|
595
|
+
clipboard_manager.clear_pending()
|
|
596
|
+
if clipboard_count > 0:
|
|
597
|
+
emit_info(f"Cleared {clipboard_count} pending clipboard image(s)")
|
|
582
598
|
continue
|
|
583
599
|
|
|
584
600
|
# Parse attachments first so leading paths aren't misread as commands
|
|
@@ -679,8 +695,6 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
679
695
|
save_command_to_history(task)
|
|
680
696
|
|
|
681
697
|
try:
|
|
682
|
-
prettier_code_blocks()
|
|
683
|
-
|
|
684
698
|
# No need to get agent directly - use manager's run methods
|
|
685
699
|
|
|
686
700
|
# Use our custom helper to enable attachment handling with spinner support
|
|
@@ -750,28 +764,6 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
750
764
|
pass
|
|
751
765
|
|
|
752
766
|
|
|
753
|
-
def prettier_code_blocks():
|
|
754
|
-
"""Configure Rich to use prettier code block rendering."""
|
|
755
|
-
|
|
756
|
-
class SimpleCodeBlock(CodeBlock):
|
|
757
|
-
def __rich_console__(
|
|
758
|
-
self, console: Console, options: ConsoleOptions
|
|
759
|
-
) -> RenderResult:
|
|
760
|
-
code = str(self.text).rstrip()
|
|
761
|
-
yield Text(self.lexer_name, style="dim")
|
|
762
|
-
syntax = Syntax(
|
|
763
|
-
code,
|
|
764
|
-
self.lexer_name,
|
|
765
|
-
theme=self.theme,
|
|
766
|
-
background_color="default",
|
|
767
|
-
line_numbers=True,
|
|
768
|
-
)
|
|
769
|
-
yield syntax
|
|
770
|
-
yield Text(f"/{self.lexer_name}", style="dim")
|
|
771
|
-
|
|
772
|
-
Markdown.elements["fence"] = SimpleCodeBlock
|
|
773
|
-
|
|
774
|
-
|
|
775
767
|
async def run_prompt_with_attachments(
|
|
776
768
|
agent,
|
|
777
769
|
raw_prompt: str,
|
|
@@ -785,6 +777,7 @@ async def run_prompt_with_attachments(
|
|
|
785
777
|
tuple: (result, task) where result is the agent response and task is the asyncio task
|
|
786
778
|
"""
|
|
787
779
|
import asyncio
|
|
780
|
+
import re
|
|
788
781
|
|
|
789
782
|
from code_puppy.messaging import emit_system_message, emit_warning
|
|
790
783
|
|
|
@@ -793,21 +786,41 @@ async def run_prompt_with_attachments(
|
|
|
793
786
|
for warning in processed_prompt.warnings:
|
|
794
787
|
emit_warning(warning)
|
|
795
788
|
|
|
789
|
+
# Get clipboard images and merge with file attachments
|
|
790
|
+
clipboard_manager = get_clipboard_manager()
|
|
791
|
+
clipboard_images = clipboard_manager.get_pending_images()
|
|
792
|
+
|
|
793
|
+
# Clear pending clipboard images after retrieval
|
|
794
|
+
clipboard_manager.clear_pending()
|
|
795
|
+
|
|
796
|
+
# Build summary of all attachments
|
|
796
797
|
summary_parts = []
|
|
797
798
|
if processed_prompt.attachments:
|
|
798
|
-
summary_parts.append(f"
|
|
799
|
+
summary_parts.append(f"files: {len(processed_prompt.attachments)}")
|
|
800
|
+
if clipboard_images:
|
|
801
|
+
summary_parts.append(f"clipboard images: {len(clipboard_images)}")
|
|
799
802
|
if processed_prompt.link_attachments:
|
|
800
803
|
summary_parts.append(f"urls: {len(processed_prompt.link_attachments)}")
|
|
801
804
|
if summary_parts:
|
|
802
805
|
emit_system_message("Attachments detected -> " + ", ".join(summary_parts))
|
|
803
806
|
|
|
804
|
-
|
|
807
|
+
# Clean up clipboard placeholders from the prompt text
|
|
808
|
+
cleaned_prompt = processed_prompt.prompt
|
|
809
|
+
if clipboard_images and cleaned_prompt:
|
|
810
|
+
cleaned_prompt = re.sub(
|
|
811
|
+
r"\[📋 clipboard image \d+\]\s*", "", cleaned_prompt
|
|
812
|
+
).strip()
|
|
813
|
+
|
|
814
|
+
if not cleaned_prompt:
|
|
805
815
|
emit_warning(
|
|
806
816
|
"Prompt is empty after removing attachments; add instructions and retry."
|
|
807
817
|
)
|
|
808
818
|
return None, None
|
|
809
819
|
|
|
820
|
+
# Combine file attachments with clipboard images
|
|
810
821
|
attachments = [attachment.content for attachment in processed_prompt.attachments]
|
|
822
|
+
attachments.extend(clipboard_images) # Add clipboard images
|
|
823
|
+
|
|
811
824
|
link_attachments = [link.url_part for link in processed_prompt.link_attachments]
|
|
812
825
|
|
|
813
826
|
# IMPORTANT: Set the shared console on the agent so that streaming output
|
|
@@ -819,7 +832,7 @@ async def run_prompt_with_attachments(
|
|
|
819
832
|
# Create the agent task first so we can track and cancel it
|
|
820
833
|
agent_task = asyncio.create_task(
|
|
821
834
|
agent.run_with_mcp(
|
|
822
|
-
|
|
835
|
+
cleaned_prompt, # Use cleaned prompt (clipboard placeholders removed)
|
|
823
836
|
attachments=attachments,
|
|
824
837
|
link_attachments=link_attachments,
|
|
825
838
|
)
|