code-puppy 0.0.783__tar.gz → 0.0.785__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.783 → code_puppy-0.0.785}/PKG-INFO +2 -2
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/callbacks.py +40 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/add_model_menu.py +19 -1
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/trust_command.py +5 -2
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/config.py +10 -1
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/manager.py +17 -6
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/plugins/__init__.py +19 -14
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/session_surrogate_unpickler.py +3 -11
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/file_modifications.py +94 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/pyproject.toml +2 -2
- {code_puppy-0.0.783 → code_puppy-0.0.785}/.gitignore +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/LICENSE +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/README.md +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/__main__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agent_execution_context.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_builder.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_compaction.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_diagnostics.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_history.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_key_listeners.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_model_message_transform.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_non_streaming_render.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_output_limits.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_run_signals.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_runtime.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_steer_processor.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/_subagent_recursion.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/agent_code_puppy.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/agent_creator_agent.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/agent_helios.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/agent_manager.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/agent_model_judge.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/agent_planning.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/agent_qa_kitten.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/agent_web_retriever.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/base_agent.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/event_stream_handler.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/json_agent.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/retry_profiles.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/run_stats.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/smooth_stream.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/agents/subagent_stream_handler.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/atomic_io.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/atomic_json.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/chatgpt_codex_client.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/claude_cache_client.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/cli_runner.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/add_model_details.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/agent_menu.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/attachments.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/autosave_menu.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/autosave_search.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/clipboard.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/colors_menu.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/command_handler.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/command_registry.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/completers.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/completion_cache.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/config_apply.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/config_commands.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/core_commands.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/file_index.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/file_path_completion.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/help_catalog.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/help_overlay.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/image_utils.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/load_context_completion.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/base.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/edit_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/handler.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/help_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/install_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/install_menu.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/list_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/logs_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/mcp_servers_store.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/remove_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/restart_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/search_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/silence_warning_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/start_all_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/start_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/status_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/stop_command.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/utils.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp_binding_menu.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp_completion.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/menu_session.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/model_picker_completion.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/model_settings_defs.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/model_settings_menu.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/onboarding_slides.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/onboarding_wizard.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/pagination.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/pin_command_completion.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/session_browser.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/session_browser_data.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/session_commands.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/set_menu.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/set_menu_catalog.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/set_menu_schema.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/set_menu_settings.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/set_menu_shims.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/set_menu_values.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/shell_passthrough.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/skills_completion.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/tui_style.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/uc_menu.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/utils.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/config_file.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/error_logging.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/gemini_code_assist.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/gemini_model.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/hook_engine/README.md +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/hook_engine/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/hook_engine/aliases.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/hook_engine/engine.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/hook_engine/executor.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/hook_engine/matcher.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/hook_engine/models.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/hook_engine/registry.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/hook_engine/validator.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/http_utils.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/audit.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/catalog.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/formats.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/locale.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/locales/en-US.json +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/locales/es.json +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/locales/fr-CA.json +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/plurals.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/pseudo.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/i18n/translate.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/kennel_provider.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/keymap.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/list_filtering.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/main.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/agent_bindings.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/async_lifecycle.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/blocking_startup.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/circuit_breaker.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/config_wizard.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/dashboard.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/error_isolation.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/examples/retry_example.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/health_monitor.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/managed_server.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/mcp_logs.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/project_config.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/registry.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/retry_manager.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/server_registry_catalog.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/status_tracker.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/system_tools.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/tool_arg_coercion.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_/toolset_utils.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_prompts/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/mcp_prompts/hook_creator.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/bar_painters.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/bar_rendering.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/bottom_bar.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/bus.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/chords.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/commands.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/editor_actions.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/editor_completion.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/editor_display.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/editor_history.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/editor_keys.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/editor_paste.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/external_editor.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/inline_bar.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/line_editor.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/markdown_patches.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/message_queue.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/messages.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/pause_controller.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/prompt_prefix_style.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/queue_console.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/renderers.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/rich_renderer.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/run_ui.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/run_ui_wiring.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/spinner/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/subagent_console.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/messaging/transcript_guard.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/model_descriptions.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/model_factory.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/model_switching.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/model_utils.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/models.json +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/models_dev_parser.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/observability.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/platform_utils.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/plugins/config.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/plugins/oauth_pasteback.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/plugins/oauth_puppy_html.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/plugins/trust.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/plugins/trust_notice.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/provider_credentials.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/provider_identity.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/pydantic_patches.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/reopenable_async_client.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/round_robin_model.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/secret_store.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/secret_store_backends.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/session_format_migration.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/session_lifecycle.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/session_migration.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/session_storage.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/skill_provider.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/splash.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/status_display.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/terminal_utils.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/token_usage.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/agent_tools.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/ask_user_question/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/ask_user_question/constants.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/ask_user_question/demo_tui.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/ask_user_question/handler.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/ask_user_question/models.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/ask_user_question/registration.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/ask_user_question/renderers.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/ask_user_question/terminal_ui.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/ask_user_question/theme.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/ask_user_question/tui_loop.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/__init__.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_control.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_interactions.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_locator_resolver.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_locators.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_manager.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_navigation.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_page_snapshot.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_screenshot.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_scripts.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_semantic_interactions.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/browser_workflows.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/browser/tool_registry.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/command_runner.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/common.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/display.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/file_operations.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/file_permission_state.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/fs_access.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/image_tools.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/io_backends.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/model_tools.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/shell_backgrounding.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/skills_tools.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/subagent_context.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/subagent_invocation.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/subagent_usage_metrics.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/tools_content.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/tools/universal_constructor.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/undo_manager.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/universal_constructor_provider.py +0 -0
- {code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/version_checker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: code-puppy
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.785
|
|
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
|
|
@@ -20,7 +20,7 @@ Requires-Dist: anthropic<1,>=0.108.0
|
|
|
20
20
|
Requires-Dist: anyio>=4.7.0
|
|
21
21
|
Requires-Dist: azure-identity>=1.15.0
|
|
22
22
|
Requires-Dist: boto3>=1.43.9
|
|
23
|
-
Requires-Dist: code-puppy-core-plugins>=0.0.
|
|
23
|
+
Requires-Dist: code-puppy-core-plugins>=0.0.27
|
|
24
24
|
Requires-Dist: httpx[http2]>=0.24.1
|
|
25
25
|
Requires-Dist: json-repair>=0.46.2
|
|
26
26
|
Requires-Dist: keyring>=24.0.0
|
|
@@ -49,6 +49,7 @@ PhaseType = Literal[
|
|
|
49
49
|
"agent_run_end",
|
|
50
50
|
"agent_run_result",
|
|
51
51
|
"model_select",
|
|
52
|
+
"provider_credential_flow",
|
|
52
53
|
"register_mcp_catalog_servers",
|
|
53
54
|
"register_browser_types",
|
|
54
55
|
"register_model_providers",
|
|
@@ -138,6 +139,7 @@ _callbacks: Dict[PhaseType, List[CallbackFunc]] = {
|
|
|
138
139
|
"agent_run_end": [],
|
|
139
140
|
"agent_run_result": [],
|
|
140
141
|
"model_select": [],
|
|
142
|
+
"provider_credential_flow": [],
|
|
141
143
|
"register_mcp_catalog_servers": [],
|
|
142
144
|
"register_browser_types": [],
|
|
143
145
|
"register_model_providers": [],
|
|
@@ -1225,6 +1227,44 @@ def on_model_select(
|
|
|
1225
1227
|
return None
|
|
1226
1228
|
|
|
1227
1229
|
|
|
1230
|
+
def on_provider_credential_flow(*, provider_id: str, env_var: str) -> bool:
|
|
1231
|
+
"""Ask plugins to acquire a missing provider credential.
|
|
1232
|
+
|
|
1233
|
+
Fired by the ``/add_model`` flow for each required env var that is not
|
|
1234
|
+
set, before falling back to manual key entry. A plugin that can mint the
|
|
1235
|
+
credential itself (e.g. an OAuth flow that exchanges an authorization
|
|
1236
|
+
code for an API key) should persist it so it takes effect immediately
|
|
1237
|
+
(config + ``os.environ``) and return ``True``. Return ``None``/``False``
|
|
1238
|
+
to defer to manual entry.
|
|
1239
|
+
|
|
1240
|
+
Unlike most hooks this one deliberately short-circuits: callbacks run
|
|
1241
|
+
interactive, side-effectful flows, so the first one to return ``True``
|
|
1242
|
+
wins and the rest are never invoked. Callbacks must gate on
|
|
1243
|
+
``provider_id``/``env_var`` and return ``None`` immediately for
|
|
1244
|
+
providers they do not own.
|
|
1245
|
+
|
|
1246
|
+
Args:
|
|
1247
|
+
provider_id: models.dev provider id (e.g. ``"openrouter"``).
|
|
1248
|
+
env_var: The credential env var being requested.
|
|
1249
|
+
|
|
1250
|
+
Returns:
|
|
1251
|
+
True when a callback reports the credential as acquired.
|
|
1252
|
+
"""
|
|
1253
|
+
for callback in get_callbacks("provider_credential_flow"):
|
|
1254
|
+
try:
|
|
1255
|
+
result = callback(provider_id=provider_id, env_var=env_var)
|
|
1256
|
+
except Exception as e:
|
|
1257
|
+
logger.error(
|
|
1258
|
+
f"Callback {getattr(callback, '__name__', callback)!r} failed "
|
|
1259
|
+
f"in phase 'provider_credential_flow': {e}\n"
|
|
1260
|
+
f"{traceback.format_exc()}"
|
|
1261
|
+
)
|
|
1262
|
+
continue
|
|
1263
|
+
if result is True:
|
|
1264
|
+
return True
|
|
1265
|
+
return False
|
|
1266
|
+
|
|
1267
|
+
|
|
1228
1268
|
async def on_agent_run_end(
|
|
1229
1269
|
agent_name: str,
|
|
1230
1270
|
model_name: str,
|
|
@@ -346,16 +346,34 @@ def _text_input(title: str, **kwargs):
|
|
|
346
346
|
return builder
|
|
347
347
|
|
|
348
348
|
|
|
349
|
+
def _plugin_credential_flow(provider: ProviderInfo, env_var: str) -> bool:
|
|
350
|
+
"""Give plugins first crack at acquiring a missing credential.
|
|
351
|
+
|
|
352
|
+
A plugin (e.g. an OAuth flow that exchanges a code for an API key) saves
|
|
353
|
+
the credential itself and returns True via the ``provider_credential_flow``
|
|
354
|
+
hook. Trust but verify: the env var must actually be set afterwards,
|
|
355
|
+
otherwise we fall back to manual entry anyway.
|
|
356
|
+
"""
|
|
357
|
+
from code_puppy.callbacks import on_provider_credential_flow
|
|
358
|
+
|
|
359
|
+
handled = on_provider_credential_flow(provider_id=provider.id, env_var=env_var)
|
|
360
|
+
return bool(handled and os.environ.get(env_var))
|
|
361
|
+
|
|
362
|
+
|
|
349
363
|
def prompt_for_credentials(provider: ProviderInfo, **overrides) -> bool:
|
|
350
364
|
"""Prompt for each missing credential. Empty skips; Esc cancels all.
|
|
351
365
|
|
|
352
|
-
|
|
366
|
+
Plugins get first crack via the ``provider_credential_flow`` hook (e.g.
|
|
367
|
+
browser OAuth); anything unhandled falls back to manual entry, which
|
|
368
|
+
saves via config + os.environ so the key is immediately usable.
|
|
353
369
|
"""
|
|
354
370
|
missing = missing_env_vars(provider)
|
|
355
371
|
if not missing:
|
|
356
372
|
emit_info(t("model_menu.credentials.all_set", provider=provider.name))
|
|
357
373
|
return True
|
|
358
374
|
for env_var in missing:
|
|
375
|
+
if _plugin_credential_flow(provider, env_var):
|
|
376
|
+
continue
|
|
359
377
|
result = (
|
|
360
378
|
_text_input(
|
|
361
379
|
f"{provider.name}: {env_var}",
|
|
@@ -150,11 +150,14 @@ class TrustCommand(MCPCommandBase):
|
|
|
150
150
|
|
|
151
151
|
def _revoke(self, group_id: str) -> None:
|
|
152
152
|
if revoke_project_mcp():
|
|
153
|
+
try:
|
|
154
|
+
self.manager.sync_from_config()
|
|
155
|
+
except Exception as exc: # pragma: no cover - defensive
|
|
156
|
+
logger.warning("Post-revoke registry sync failed: %s", exc)
|
|
153
157
|
emit_info(
|
|
154
158
|
Text.from_markup(
|
|
155
159
|
"[green]\u2713 Revoked[/green] trust for this project's MCP "
|
|
156
|
-
"config. Its servers will no longer load.
|
|
157
|
-
"to drop already-registered ones."
|
|
160
|
+
"config. Its servers will no longer load."
|
|
158
161
|
),
|
|
159
162
|
message_group=group_id,
|
|
160
163
|
)
|
|
@@ -595,7 +595,7 @@ def _parse_mcp_servers_mapping(raw_text: str) -> dict:
|
|
|
595
595
|
return servers
|
|
596
596
|
|
|
597
597
|
|
|
598
|
-
def load_mcp_server_configs():
|
|
598
|
+
def load_mcp_server_configs(*, raise_on_error: bool = False):
|
|
599
599
|
"""Load MCP server configs, merging user-level and trusted project-level.
|
|
600
600
|
|
|
601
601
|
Sources, in ascending order of precedence:
|
|
@@ -610,6 +610,11 @@ def load_mcp_server_configs():
|
|
|
610
610
|
Project entries win on name collision, matching how project agents, skills,
|
|
611
611
|
and plugins override their user-level counterparts. Returns an empty dict
|
|
612
612
|
when nothing is configured.
|
|
613
|
+
|
|
614
|
+
When *raise_on_error* is true, a parse/IO failure of an existing user-level
|
|
615
|
+
file (or a failure of the project loader) is re-raised after the error is
|
|
616
|
+
emitted, so callers that unregister missing names can skip that drop
|
|
617
|
+
instead of treating ``{}`` as "configure nothing".
|
|
613
618
|
"""
|
|
614
619
|
from code_puppy.messaging.message_queue import emit_error
|
|
615
620
|
|
|
@@ -622,6 +627,8 @@ def load_mcp_server_configs():
|
|
|
622
627
|
configs.update(_parse_mcp_servers_mapping(f.read()))
|
|
623
628
|
except Exception as e:
|
|
624
629
|
emit_error(f"Failed to load MCP servers - {str(e)}")
|
|
630
|
+
if raise_on_error:
|
|
631
|
+
raise
|
|
625
632
|
|
|
626
633
|
# 2. Project-level config (opt-in, trust-gated). A broken or untrusted
|
|
627
634
|
# project file must never break user-level loading.
|
|
@@ -633,6 +640,8 @@ def load_mcp_server_configs():
|
|
|
633
640
|
configs.update(project_configs)
|
|
634
641
|
except Exception as e:
|
|
635
642
|
emit_error(f"Failed to load project MCP servers - {str(e)}")
|
|
643
|
+
if raise_on_error:
|
|
644
|
+
raise
|
|
636
645
|
|
|
637
646
|
return configs
|
|
638
647
|
|
|
@@ -161,10 +161,7 @@ class MCPManager:
|
|
|
161
161
|
try:
|
|
162
162
|
from code_puppy.config import load_mcp_server_configs
|
|
163
163
|
|
|
164
|
-
configs = load_mcp_server_configs()
|
|
165
|
-
if not configs:
|
|
166
|
-
logger.debug("No servers found in mcp_servers.json")
|
|
167
|
-
return
|
|
164
|
+
configs = load_mcp_server_configs(raise_on_error=True) or {}
|
|
168
165
|
|
|
169
166
|
synced_count = 0
|
|
170
167
|
updated_count = 0
|
|
@@ -209,9 +206,23 @@ class MCPManager:
|
|
|
209
206
|
logger.warning(f"Failed to sync server '{name}' from config: {e}")
|
|
210
207
|
continue
|
|
211
208
|
|
|
212
|
-
|
|
209
|
+
configured_names = {
|
|
210
|
+
name for name, conf in configs.items() if isinstance(conf, dict)
|
|
211
|
+
}
|
|
212
|
+
dropped_count = 0
|
|
213
|
+
for existing in list(self.registry.list_all()):
|
|
214
|
+
if existing.name not in configured_names:
|
|
215
|
+
if self.remove_server(existing.id):
|
|
216
|
+
dropped_count += 1
|
|
217
|
+
logger.debug(
|
|
218
|
+
"Dropped server no longer in mcp_servers.json: %s",
|
|
219
|
+
existing.name,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
if synced_count > 0 or updated_count > 0 or dropped_count > 0:
|
|
213
223
|
logger.info(
|
|
214
|
-
f"Synced {synced_count} new
|
|
224
|
+
f"Synced {synced_count} new, updated {updated_count}, "
|
|
225
|
+
f"dropped {dropped_count} servers from mcp_servers.json"
|
|
215
226
|
)
|
|
216
227
|
|
|
217
228
|
except Exception as e:
|
|
@@ -117,6 +117,18 @@ def _install_project_plugin_finder() -> None:
|
|
|
117
117
|
|
|
118
118
|
PLUGIN_ENTRY_POINT_GROUP = "code_puppy.plugins"
|
|
119
119
|
|
|
120
|
+
# shell_safety implements the safety_permission_level threshold. Skip it only
|
|
121
|
+
# when the user opted into high/critical autonomy; the default (medium) must
|
|
122
|
+
# load it or the setting is a no-op.
|
|
123
|
+
_SHELL_SAFETY_SKIP_LEVELS = frozenset({"high", "critical"})
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _skip_shell_safety_plugin() -> bool:
|
|
127
|
+
from code_puppy.config import get_safety_permission_level
|
|
128
|
+
|
|
129
|
+
return get_safety_permission_level() in _SHELL_SAFETY_SKIP_LEVELS
|
|
130
|
+
|
|
131
|
+
|
|
120
132
|
# Track if plugins have already been loaded to prevent duplicate registration
|
|
121
133
|
_PLUGINS_LOADED = False
|
|
122
134
|
|
|
@@ -135,18 +147,13 @@ def _load_installed_plugins() -> list[str]:
|
|
|
135
147
|
project plugins, but remain physically independent from the core package.
|
|
136
148
|
Entry points are sorted for deterministic startup and test behavior.
|
|
137
149
|
"""
|
|
138
|
-
from code_puppy.config import get_safety_permission_level
|
|
139
|
-
|
|
140
150
|
loaded: list[str] = []
|
|
141
151
|
discovered = sorted(
|
|
142
152
|
entry_points(group=PLUGIN_ENTRY_POINT_GROUP), key=lambda item: item.name
|
|
143
153
|
)
|
|
144
154
|
for entry_point in discovered:
|
|
145
155
|
plugin_name = entry_point.name
|
|
146
|
-
if plugin_name == "shell_safety" and
|
|
147
|
-
"none",
|
|
148
|
-
"low",
|
|
149
|
-
):
|
|
156
|
+
if plugin_name == "shell_safety" and _skip_shell_safety_plugin():
|
|
150
157
|
logger.debug("Skipping shell_safety plugin due to safety permission level")
|
|
151
158
|
continue
|
|
152
159
|
try:
|
|
@@ -191,14 +198,12 @@ def _load_builtin_plugins(
|
|
|
191
198
|
continue
|
|
192
199
|
|
|
193
200
|
if callbacks_file.exists():
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
)
|
|
201
|
-
continue
|
|
201
|
+
if plugin_name == "shell_safety" and _skip_shell_safety_plugin():
|
|
202
|
+
logger.debug(
|
|
203
|
+
"Skipping shell_safety plugin - safety_permission_level is %s",
|
|
204
|
+
get_safety_permission_level(),
|
|
205
|
+
)
|
|
206
|
+
continue
|
|
202
207
|
|
|
203
208
|
try:
|
|
204
209
|
module_name = f"code_puppy.plugins.{plugin_name}.register_callbacks"
|
|
@@ -72,9 +72,9 @@ _TZINFO_EQUIVALENTS: Dict[Tuple[str, str], Callable[..., Any]] = {
|
|
|
72
72
|
("pydantic_core", "TzInfo"): _tz_from_utc_offset,
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
|
|
75
|
+
# pytz / dateutil.tz must NOT go through super().find_class: those packages
|
|
76
|
+
# re-export os/sys the same way uuid and collections did. Known tzinfo
|
|
77
|
+
# shapes are rebuilt via _TZINFO_EQUIVALENTS; everything else is a surrogate.
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
class SurrogateBase:
|
|
@@ -145,14 +145,6 @@ class SurrogateUnpickler(pickle.Unpickler):
|
|
|
145
145
|
equivalent = _TZINFO_EQUIVALENTS.get((module, name))
|
|
146
146
|
if equivalent is not None:
|
|
147
147
|
return equivalent
|
|
148
|
-
if any(
|
|
149
|
-
module == prefix or module.startswith(prefix + ".")
|
|
150
|
-
for prefix in _TZ_LIBRARY_PREFIXES
|
|
151
|
-
):
|
|
152
|
-
try:
|
|
153
|
-
return super().find_class(module, name)
|
|
154
|
-
except Exception: # noqa: BLE001 - library absent/renamed
|
|
155
|
-
pass
|
|
156
148
|
return self._surrogate_for(module, name)
|
|
157
149
|
|
|
158
150
|
def _tz_tolerant(self, obj: Any) -> Any:
|
|
@@ -14,6 +14,7 @@ import difflib
|
|
|
14
14
|
import json
|
|
15
15
|
import os
|
|
16
16
|
import traceback
|
|
17
|
+
from pathlib import Path
|
|
17
18
|
from code_puppy.undo_manager import UndoManager
|
|
18
19
|
import warnings
|
|
19
20
|
from typing import Annotated, Any, Dict, List, Union
|
|
@@ -45,6 +46,75 @@ from code_puppy.tools.file_permission_state import (
|
|
|
45
46
|
)
|
|
46
47
|
|
|
47
48
|
|
|
49
|
+
def _split_existing(path: Path) -> tuple[Path, tuple[str, ...]]:
|
|
50
|
+
"""Deepest existing ancestor of *path*, plus the missing trailing names."""
|
|
51
|
+
missing: list[str] = []
|
|
52
|
+
current = path
|
|
53
|
+
while True:
|
|
54
|
+
if current.exists():
|
|
55
|
+
return current.resolve(), tuple(reversed(missing))
|
|
56
|
+
parent = current.parent
|
|
57
|
+
if parent == current:
|
|
58
|
+
return current, tuple(reversed(missing))
|
|
59
|
+
missing.append(current.name)
|
|
60
|
+
current = parent
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _casefold_has_prefix(parts: tuple[str, ...], prefix: tuple[str, ...]) -> bool:
|
|
64
|
+
if len(parts) < len(prefix):
|
|
65
|
+
return False
|
|
66
|
+
return all(a.casefold() == b.casefold() for a, b in zip(prefix, parts))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _is_inside_user_plugin_root(target: Path, root: Path) -> bool:
|
|
70
|
+
"""Containment that survives APFS case-folding. ``Path.resolve`` does not."""
|
|
71
|
+
target_existing, target_rest = _split_existing(target)
|
|
72
|
+
root_existing, root_rest = _split_existing(root)
|
|
73
|
+
|
|
74
|
+
if os.path.samefile(target_existing, root_existing):
|
|
75
|
+
return _casefold_has_prefix(target_rest, root_rest)
|
|
76
|
+
|
|
77
|
+
current = target_existing
|
|
78
|
+
while True:
|
|
79
|
+
if os.path.samefile(current, root_existing):
|
|
80
|
+
return not root_rest
|
|
81
|
+
parent = current.parent
|
|
82
|
+
if parent == current:
|
|
83
|
+
return False
|
|
84
|
+
current = parent
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _is_user_plugin_tree_path(file_path: str) -> bool:
|
|
88
|
+
"""True if *file_path* is inside ``~/.code_puppy/plugins``.
|
|
89
|
+
|
|
90
|
+
That tree is imported at the next process start with no trust ceremony.
|
|
91
|
+
File tools must not plant ``register_callbacks.py`` there.
|
|
92
|
+
Canonicalization errors fail closed (treated as inside).
|
|
93
|
+
"""
|
|
94
|
+
from code_puppy.plugins import USER_PLUGINS_DIR
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
resolved = Path(resolve_path(file_path)).resolve()
|
|
98
|
+
root = Path(USER_PLUGINS_DIR).expanduser().resolve()
|
|
99
|
+
return _is_inside_user_plugin_root(resolved, root)
|
|
100
|
+
except (OSError, RuntimeError, ValueError):
|
|
101
|
+
return True
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _refuse_user_plugin_tree(file_path: str) -> Dict[str, Any] | None:
|
|
105
|
+
if not _is_user_plugin_tree_path(file_path):
|
|
106
|
+
return None
|
|
107
|
+
return {
|
|
108
|
+
"success": False,
|
|
109
|
+
"path": file_path,
|
|
110
|
+
"message": (
|
|
111
|
+
"Refused: file tools cannot modify ~/.code_puppy/plugins. "
|
|
112
|
+
"That directory is imported at startup."
|
|
113
|
+
),
|
|
114
|
+
"changed": False,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
48
118
|
def _permission_denied(permission_results: List[Any]) -> bool:
|
|
49
119
|
"""Return True when any permission callback explicitly denies.
|
|
50
120
|
|
|
@@ -424,6 +494,9 @@ def _write_to_file(
|
|
|
424
494
|
def delete_snippet_from_file(
|
|
425
495
|
context: RunContext, file_path: str, snippet: str, message_group: str | None = None
|
|
426
496
|
) -> Dict[str, Any]:
|
|
497
|
+
refused = _refuse_user_plugin_tree(file_path)
|
|
498
|
+
if refused is not None:
|
|
499
|
+
return refused
|
|
427
500
|
# Use the plugin system for permission handling with operation data
|
|
428
501
|
from code_puppy.callbacks import on_file_permission
|
|
429
502
|
|
|
@@ -452,6 +525,9 @@ def write_to_file(
|
|
|
452
525
|
overwrite: bool,
|
|
453
526
|
message_group: str | None = None,
|
|
454
527
|
) -> Dict[str, Any]:
|
|
528
|
+
refused = _refuse_user_plugin_tree(path)
|
|
529
|
+
if refused is not None:
|
|
530
|
+
return refused
|
|
455
531
|
# Use the plugin system for permission handling with operation data
|
|
456
532
|
from code_puppy.callbacks import on_file_permission
|
|
457
533
|
|
|
@@ -481,6 +557,9 @@ def replace_in_file(
|
|
|
481
557
|
replacements: List[Dict[str, str]],
|
|
482
558
|
message_group: str | None = None,
|
|
483
559
|
) -> Dict[str, Any]:
|
|
560
|
+
refused = _refuse_user_plugin_tree(path)
|
|
561
|
+
if refused is not None:
|
|
562
|
+
return refused
|
|
484
563
|
# Use the plugin system for permission handling with operation data
|
|
485
564
|
from code_puppy.callbacks import on_file_permission
|
|
486
565
|
|
|
@@ -504,6 +583,9 @@ async def delete_snippet_from_file_async(
|
|
|
504
583
|
context: RunContext, file_path: str, snippet: str, message_group: str | None = None
|
|
505
584
|
) -> Dict[str, Any]:
|
|
506
585
|
"""Async permission-aware variant of ``delete_snippet_from_file``."""
|
|
586
|
+
refused = _refuse_user_plugin_tree(file_path)
|
|
587
|
+
if refused is not None:
|
|
588
|
+
return refused
|
|
507
589
|
from code_puppy.callbacks import on_file_permission_async
|
|
508
590
|
|
|
509
591
|
operation_data = {"snippet": snippet}
|
|
@@ -530,6 +612,9 @@ async def write_to_file_async(
|
|
|
530
612
|
message_group: str | None = None,
|
|
531
613
|
) -> Dict[str, Any]:
|
|
532
614
|
"""Async permission-aware variant of ``write_to_file``."""
|
|
615
|
+
refused = _refuse_user_plugin_tree(path)
|
|
616
|
+
if refused is not None:
|
|
617
|
+
return refused
|
|
533
618
|
from code_puppy.callbacks import on_file_permission_async
|
|
534
619
|
|
|
535
620
|
operation_data = {"content": content, "overwrite": overwrite}
|
|
@@ -556,6 +641,9 @@ async def replace_in_file_async(
|
|
|
556
641
|
message_group: str | None = None,
|
|
557
642
|
) -> Dict[str, Any]:
|
|
558
643
|
"""Async permission-aware variant of ``replace_in_file``."""
|
|
644
|
+
refused = _refuse_user_plugin_tree(path)
|
|
645
|
+
if refused is not None:
|
|
646
|
+
return refused
|
|
559
647
|
from code_puppy.callbacks import on_file_permission_async
|
|
560
648
|
|
|
561
649
|
operation_data = {"replacements": replacements}
|
|
@@ -726,6 +814,9 @@ async def _edit_file_async(
|
|
|
726
814
|
def _delete_file(
|
|
727
815
|
context: RunContext, file_path: str, message_group: str | None = None
|
|
728
816
|
) -> Dict[str, Any]:
|
|
817
|
+
refused = _refuse_user_plugin_tree(file_path)
|
|
818
|
+
if refused is not None:
|
|
819
|
+
return refused
|
|
729
820
|
UndoManager().record_change(file_path, "delete_file")
|
|
730
821
|
file_path = resolve_path(file_path)
|
|
731
822
|
|
|
@@ -786,6 +877,9 @@ async def _delete_file_async(
|
|
|
786
877
|
context: RunContext, file_path: str, message_group: str | None = None
|
|
787
878
|
) -> Dict[str, Any]:
|
|
788
879
|
"""Async permission-aware variant of ``_delete_file``."""
|
|
880
|
+
refused = _refuse_user_plugin_tree(file_path)
|
|
881
|
+
if refused is not None:
|
|
882
|
+
return refused
|
|
789
883
|
file_path = resolve_path(file_path)
|
|
790
884
|
|
|
791
885
|
from code_puppy.callbacks import on_file_permission_async
|
|
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "code-puppy"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.785"
|
|
8
8
|
description = "Code generation agent"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11,<3.15"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"code-puppy-core-plugins>=0.0.
|
|
12
|
+
"code-puppy-core-plugins>=0.0.27",
|
|
13
13
|
"logfire>=4.0",
|
|
14
14
|
"pydantic-ai-slim[openai,anthropic,mcp]==2.31.0",
|
|
15
15
|
"pydantic-ai-harness==0.23.0",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/load_context_completion.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/catalog_server_installer.py
RENAMED
|
File without changes
|
|
File without changes
|
{code_puppy-0.0.783 → code_puppy-0.0.785}/code_puppy/command_line/mcp/custom_server_installer.py
RENAMED
|
File without changes
|
|
File without changes
|