newcode 0.1.5__tar.gz → 0.1.7__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.
- {newcode-0.1.5 → newcode-0.1.7}/PKG-INFO +1 -1
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/__init__.py +1 -1
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_creator_agent.py +2 -2
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/config.py +1 -3
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/hook_engine/__init__.py +8 -2
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/hook_engine/aliases.py +17 -17
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/hook_engine/engine.py +38 -12
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/hook_engine/executor.py +15 -12
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/hook_engine/matcher.py +15 -4
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/hook_engine/models.py +24 -6
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/hook_engine/registry.py +3 -3
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/hook_engine/validator.py +14 -11
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/managed_server.py +9 -8
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/rich_renderer.py +1 -1
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/skills_install_menu.py +2 -2
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/skills_menu.py +2 -2
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_hooks/config.py +26 -20
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_hooks/register_callbacks.py +18 -6
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/hook_creator/register_callbacks.py +4 -4
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/hook_manager/__init__.py +1 -1
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/hook_manager/config.py +27 -14
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/hook_manager/hooks_menu.py +35 -22
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/hook_manager/register_callbacks.py +41 -19
- newcode-0.1.7/code_puppy/plugins/oauth_puppy_html.py +70 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/agent_tools.py +64 -4
- newcode-0.1.7/code_puppy/tools/ask_user_question/registration.py +87 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/terminal_screenshot_tools.py +6 -2
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/file_modifications.py +117 -6
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/file_operations.py +127 -3
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/skills_tools.py +6 -0
- {newcode-0.1.5 → newcode-0.1.7}/pyproject.toml +1 -1
- newcode-0.1.5/code_puppy/agents/agent_helios.py +0 -119
- newcode-0.1.5/code_puppy/plugins/oauth_puppy_html.py +0 -224
- newcode-0.1.5/code_puppy/tools/ask_user_question/registration.py +0 -36
- {newcode-0.1.5 → newcode-0.1.7}/.gitignore +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/LICENSE +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/README.md +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/__main__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_c_reviewer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_code_puppy.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_code_reviewer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_cpp_reviewer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_golang_reviewer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_javascript_reviewer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_manager.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_pack_leader.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_planning.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_python_programmer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_python_reviewer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_qa_expert.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_qa_kitten.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_scheduler.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_security_auditor.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_terminal_qa.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/agent_typescript_reviewer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/base_agent.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/event_stream_handler.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/json_agent.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/pack/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/pack/bloodhound.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/pack/husky.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/pack/retriever.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/pack/shepherd.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/pack/terrier.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/pack/watchdog.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/prompt_reviewer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/agents/subagent_stream_handler.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/app.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/main.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/pty_manager.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/routers/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/routers/agents.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/routers/commands.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/routers/config.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/routers/sessions.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/templates/terminal.html +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/api/websocket.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/chatgpt_codex_client.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/claude_cache_client.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/cli_runner.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/add_model_menu.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/agent_menu.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/attachments.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/autosave_menu.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/clipboard.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/colors_menu.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/command_handler.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/command_registry.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/config_commands.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/core_commands.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/diff_menu.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/file_path_completion.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/load_context_completion.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/base.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/edit_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/handler.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/help_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/install_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/install_menu.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/list_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/logs_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/remove_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/restart_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/search_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/start_all_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/start_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/status_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/stop_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/test_command.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/utils.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/mcp_completion.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/model_picker_completion.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/model_settings_menu.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/motd.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/onboarding_slides.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/onboarding_wizard.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/pin_command_completion.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/session_commands.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/skills_completion.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/uc_menu.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/utils.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/command_line/wiggum_state.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/error_logging.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/gemini_code_assist.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/gemini_model.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/hook_engine/README.md +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/http_utils.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/keymap.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/main.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/async_lifecycle.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/blocking_startup.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/captured_stdio_server.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/circuit_breaker.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/config_wizard.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/dashboard.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/error_isolation.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/examples/retry_example.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/health_monitor.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/manager.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/mcp_logs.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/registry.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/retry_manager.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/server_registry_catalog.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/status_tracker.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_/system_tools.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_prompts/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/mcp_prompts/hook_creator.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/bus.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/commands.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/markdown_patches.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/message_queue.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/messages.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/queue_console.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/renderers.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/spinner/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/spinner/console_spinner.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/spinner/spinner_base.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/messaging/subagent_console.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/model_factory.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/model_switching.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/model_utils.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/models.json +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/models_dev_api.json +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/models_dev_parser.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/config.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/discovery.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/downloader.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/installer.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/metadata.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/prompt_builder.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/remote_catalog.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/agent_skills/skill_catalog.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/accounts.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/antigravity_model.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/config.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/constants.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/oauth.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/storage.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/test_plugin.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/token.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/transport.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/antigravity_oauth/utils.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/chatgpt_oauth/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/chatgpt_oauth/config.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/chatgpt_oauth/oauth_flow.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/chatgpt_oauth/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/chatgpt_oauth/test_plugin.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/chatgpt_oauth/utils.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_hooks/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_oauth/README.md +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_oauth/SETUP.md +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_oauth/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_oauth/config.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_oauth/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_oauth/test_plugin.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/claude_code_oauth/utils.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/customizable_commands/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/customizable_commands/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/example_custom_command/README.md +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/file_permission_handler/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/file_permission_handler/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/frontend_emitter/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/frontend_emitter/emitter.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/frontend_emitter/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/hook_creator/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/scheduler/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/scheduler/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/scheduler/scheduler_menu.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/scheduler/scheduler_wizard.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/shell_safety/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/shell_safety/agent_shell_safety.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/shell_safety/command_cache.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/shell_safety/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/synthetic_status/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/synthetic_status/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/synthetic_status/status_api.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/universal_constructor/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/universal_constructor/models.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/universal_constructor/register_callbacks.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/universal_constructor/registry.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/plugins/universal_constructor/sandbox.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/prompts/antigravity_system_prompt.md +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/pydantic_patches.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/reopenable_async_client.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/round_robin_model.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/scheduler/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/scheduler/__main__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/scheduler/cli.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/scheduler/config.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/scheduler/daemon.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/scheduler/executor.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/scheduler/platform.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/scheduler/platform_unix.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/scheduler/platform_win.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/session_storage.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/status_display.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/summarization_agent.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/terminal_utils.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/ask_user_question/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/ask_user_question/constants.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/ask_user_question/demo_tui.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/ask_user_question/handler.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/ask_user_question/models.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/ask_user_question/renderers.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/ask_user_question/terminal_ui.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/ask_user_question/theme.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/ask_user_question/tui_loop.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/__init__.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/browser_control.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/browser_interactions.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/browser_locators.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/browser_manager.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/browser_navigation.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/browser_screenshot.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/browser_scripts.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/browser_workflows.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/chromium_terminal_manager.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/terminal_command_tools.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/browser/terminal_tools.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/command_runner.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/common.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/display.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/scheduler_tools.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/subagent_context.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/tools_content.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/tools/universal_constructor.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/uvx_detection.py +0 -0
- {newcode-0.1.5 → newcode-0.1.7}/code_puppy/version_checker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: newcode
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: AI-powered code generation agent - a fedstew fork of code-puppy
|
|
5
5
|
Project-URL: repository, https://github.com/janfeddersen-wq/new_code
|
|
6
6
|
Project-URL: HomePage, https://github.com/janfeddersen-wq/new_code
|
|
@@ -2,7 +2,7 @@ import importlib.metadata
|
|
|
2
2
|
|
|
3
3
|
# Version detection
|
|
4
4
|
try:
|
|
5
|
-
_detected_version = importlib.metadata.version("
|
|
5
|
+
_detected_version = importlib.metadata.version("newcode")
|
|
6
6
|
# Ensure we never end up with None or empty string
|
|
7
7
|
__version__ = _detected_version if _detected_version else "0.0.0-dev"
|
|
8
8
|
except Exception:
|
|
@@ -50,7 +50,7 @@ class AgentCreatorAgent(BaseAgent):
|
|
|
50
50
|
uc_tools_section = "\n".join(uc_tools_info)
|
|
51
51
|
else:
|
|
52
52
|
uc_tools_section = (
|
|
53
|
-
"No custom UC tools created yet.
|
|
53
|
+
"No custom UC tools created yet."
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
# Load available models dynamically
|
|
@@ -163,7 +163,7 @@ Users can optionally pin a specific model to their agent to override the global
|
|
|
163
163
|
- `invoke_agent` - Invoke other agents with specific prompts (recommended for agent managers)
|
|
164
164
|
|
|
165
165
|
### 🔧 **Universal Constructor Tools** (custom tools):
|
|
166
|
-
- These are tools created
|
|
166
|
+
- These are tools created via the Universal Constructor
|
|
167
167
|
- They persist across sessions and can be bound to any agent
|
|
168
168
|
- Use `universal_constructor(action="list")` to see available custom tools
|
|
169
169
|
- Bind them by adding their full name to the agent's tools array
|
|
@@ -104,7 +104,7 @@ PACK_AGENT_NAMES = frozenset(
|
|
|
104
104
|
)
|
|
105
105
|
|
|
106
106
|
# Agents that require Universal Constructor to be enabled
|
|
107
|
-
UC_AGENT_NAMES = frozenset(
|
|
107
|
+
UC_AGENT_NAMES = frozenset()
|
|
108
108
|
|
|
109
109
|
|
|
110
110
|
def get_pack_agents_enabled() -> bool:
|
|
@@ -1469,8 +1469,6 @@ DEFAULT_BANNER_COLORS = {
|
|
|
1469
1469
|
"universal_constructor": "dark_cyan", # Teal - constructing tools
|
|
1470
1470
|
# Browser/Terminal tools - same color as edit_file (gold)
|
|
1471
1471
|
"terminal_tool": "dark_goldenrod", # Gold - browser terminal operations
|
|
1472
|
-
# MCP tools - distinct from builtin tools
|
|
1473
|
-
"mcp_tool_call": "dark_cyan", # Teal - external MCP tool calls
|
|
1474
1472
|
}
|
|
1475
1473
|
|
|
1476
1474
|
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"""Hook engine package."""
|
|
2
2
|
|
|
3
|
-
from .engine import HookEngine
|
|
4
|
-
from .models import HookConfig, EventData, ExecutionResult, ProcessEventResult, HookRegistry
|
|
5
3
|
from . import aliases
|
|
4
|
+
from .engine import HookEngine
|
|
5
|
+
from .models import (
|
|
6
|
+
EventData,
|
|
7
|
+
ExecutionResult,
|
|
8
|
+
HookConfig,
|
|
9
|
+
HookRegistry,
|
|
10
|
+
ProcessEventResult,
|
|
11
|
+
)
|
|
6
12
|
|
|
7
13
|
__all__ = [
|
|
8
14
|
"HookEngine",
|
|
@@ -19,30 +19,29 @@ Adding a new provider
|
|
|
19
19
|
|
|
20
20
|
from typing import Dict, FrozenSet, Optional
|
|
21
21
|
|
|
22
|
-
|
|
23
22
|
# ---------------------------------------------------------------------------
|
|
24
23
|
# Claude Code (Anthropic)
|
|
25
24
|
# Source: `claude mcp serve` → tools/list (verified against v2.1.52)
|
|
26
25
|
# ---------------------------------------------------------------------------
|
|
27
26
|
CLAUDE_CODE_ALIASES: Dict[str, str] = {
|
|
28
27
|
# Shell execution
|
|
29
|
-
"Bash":
|
|
28
|
+
"Bash": "agent_run_shell_command",
|
|
30
29
|
# File system — read
|
|
31
|
-
"Glob":
|
|
32
|
-
"Read":
|
|
33
|
-
"Grep":
|
|
30
|
+
"Glob": "list_files",
|
|
31
|
+
"Read": "read_file",
|
|
32
|
+
"Grep": "grep",
|
|
34
33
|
# File system — write
|
|
35
|
-
"Edit":
|
|
36
|
-
"Write":
|
|
34
|
+
"Edit": "edit_file",
|
|
35
|
+
"Write": "edit_file", # Write = full overwrite; same tool in puppy
|
|
37
36
|
# File system — delete
|
|
38
|
-
"Delete":
|
|
37
|
+
"Delete": "delete_file",
|
|
39
38
|
# User interaction
|
|
40
|
-
"AskUserQuestion":
|
|
39
|
+
"AskUserQuestion": "ask_user_question",
|
|
41
40
|
# Agent / task orchestration
|
|
42
|
-
"Task":
|
|
41
|
+
"Task": "invoke_agent",
|
|
43
42
|
# Skills
|
|
44
|
-
"Skill":
|
|
45
|
-
"ToolSearch":
|
|
43
|
+
"Skill": "activate_skill",
|
|
44
|
+
"ToolSearch": "list_or_search_skills",
|
|
46
45
|
# NOTE: the tools below have no direct code_puppy equivalent yet.
|
|
47
46
|
# They are listed here for documentation and future mapping:
|
|
48
47
|
# "TaskOutput" -> (no equivalent)
|
|
@@ -92,10 +91,10 @@ SWARM_ALIASES: Dict[str, str] = {
|
|
|
92
91
|
# To disable a provider's aliases, remove its entry from this dict.
|
|
93
92
|
# ---------------------------------------------------------------------------
|
|
94
93
|
PROVIDER_ALIASES: Dict[str, Dict[str, str]] = {
|
|
95
|
-
"claude":
|
|
96
|
-
"gemini":
|
|
97
|
-
"codex":
|
|
98
|
-
"swarm":
|
|
94
|
+
"claude": CLAUDE_CODE_ALIASES,
|
|
95
|
+
"gemini": GEMINI_ALIASES, # placeholder — empty until populated
|
|
96
|
+
"codex": CODEX_ALIASES, # placeholder — empty until populated
|
|
97
|
+
"swarm": SWARM_ALIASES, # placeholder — empty until populated
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
|
|
@@ -103,6 +102,7 @@ PROVIDER_ALIASES: Dict[str, Dict[str, str]] = {
|
|
|
103
102
|
# Flattened lookup structures — built once at import time for O(1) access.
|
|
104
103
|
# ---------------------------------------------------------------------------
|
|
105
104
|
|
|
105
|
+
|
|
106
106
|
def _build_lookup() -> Dict[str, FrozenSet[str]]:
|
|
107
107
|
"""
|
|
108
108
|
Return a dict mapping every known name (provider *and* internal) to the
|
|
@@ -152,4 +152,4 @@ def resolve_internal_name(provider_tool_name: str) -> Optional[str]:
|
|
|
152
152
|
for pname, internal in provider_aliases.items():
|
|
153
153
|
if pname.lower() == provider_tool_name.lower():
|
|
154
154
|
return internal
|
|
155
|
-
return None
|
|
155
|
+
return None
|
|
@@ -6,17 +6,20 @@ import logging
|
|
|
6
6
|
import time
|
|
7
7
|
from typing import Any, Dict, List, Optional
|
|
8
8
|
|
|
9
|
+
from .executor import execute_hooks_sequential, get_blocking_result
|
|
10
|
+
from .matcher import matches
|
|
9
11
|
from .models import (
|
|
10
12
|
EventData,
|
|
11
|
-
ExecutionResult,
|
|
12
13
|
HookConfig,
|
|
13
14
|
HookRegistry,
|
|
14
15
|
ProcessEventResult,
|
|
15
16
|
)
|
|
16
|
-
from .matcher import matches
|
|
17
|
-
from .executor import execute_hook, execute_hooks_sequential, get_blocking_result
|
|
18
17
|
from .registry import build_registry_from_config, get_registry_stats
|
|
19
|
-
from .validator import
|
|
18
|
+
from .validator import (
|
|
19
|
+
format_validation_report,
|
|
20
|
+
get_config_suggestions,
|
|
21
|
+
validate_hooks_config,
|
|
22
|
+
)
|
|
20
23
|
|
|
21
24
|
logger = logging.getLogger(__name__)
|
|
22
25
|
|
|
@@ -61,7 +64,9 @@ class HookEngine:
|
|
|
61
64
|
|
|
62
65
|
try:
|
|
63
66
|
self._registry = build_registry_from_config(config)
|
|
64
|
-
logger.info(
|
|
67
|
+
logger.info(
|
|
68
|
+
f"Loaded hook configuration: {self._registry.count_hooks()} total hooks"
|
|
69
|
+
)
|
|
65
70
|
except Exception as e:
|
|
66
71
|
if self.strict_validation:
|
|
67
72
|
raise ValueError(f"Failed to build hook registry: {e}") from e
|
|
@@ -83,21 +88,37 @@ class HookEngine:
|
|
|
83
88
|
start_time = time.perf_counter()
|
|
84
89
|
|
|
85
90
|
if not self._registry:
|
|
86
|
-
return ProcessEventResult(
|
|
91
|
+
return ProcessEventResult(
|
|
92
|
+
blocked=False, executed_hooks=0, results=[], total_duration_ms=0.0
|
|
93
|
+
)
|
|
87
94
|
|
|
88
95
|
all_hooks = self._registry.get_hooks_for_event(event_type)
|
|
89
96
|
|
|
90
97
|
if not all_hooks:
|
|
91
98
|
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
92
|
-
return ProcessEventResult(
|
|
99
|
+
return ProcessEventResult(
|
|
100
|
+
blocked=False,
|
|
101
|
+
executed_hooks=0,
|
|
102
|
+
results=[],
|
|
103
|
+
total_duration_ms=duration_ms,
|
|
104
|
+
)
|
|
93
105
|
|
|
94
|
-
matching_hooks = self._filter_hooks_by_matcher(
|
|
106
|
+
matching_hooks = self._filter_hooks_by_matcher(
|
|
107
|
+
all_hooks, event_data.tool_name, event_data.tool_args
|
|
108
|
+
)
|
|
95
109
|
|
|
96
110
|
if not matching_hooks:
|
|
97
111
|
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
98
|
-
return ProcessEventResult(
|
|
112
|
+
return ProcessEventResult(
|
|
113
|
+
blocked=False,
|
|
114
|
+
executed_hooks=0,
|
|
115
|
+
results=[],
|
|
116
|
+
total_duration_ms=duration_ms,
|
|
117
|
+
)
|
|
99
118
|
|
|
100
|
-
logger.debug(
|
|
119
|
+
logger.debug(
|
|
120
|
+
f"Processing {event_type}: {len(matching_hooks)} matching hook(s) for tool '{event_data.tool_name}'"
|
|
121
|
+
)
|
|
101
122
|
|
|
102
123
|
if sequential:
|
|
103
124
|
results = await execute_hooks_sequential(
|
|
@@ -105,7 +126,10 @@ class HookEngine:
|
|
|
105
126
|
)
|
|
106
127
|
else:
|
|
107
128
|
from .executor import execute_hooks_parallel
|
|
108
|
-
|
|
129
|
+
|
|
130
|
+
results = await execute_hooks_parallel(
|
|
131
|
+
matching_hooks, event_data, self.env_vars
|
|
132
|
+
)
|
|
109
133
|
|
|
110
134
|
for hook, result in zip(matching_hooks, results):
|
|
111
135
|
if hook.once and result.success:
|
|
@@ -142,7 +166,9 @@ class HookEngine:
|
|
|
142
166
|
if matches(hook.matcher, tool_name, tool_args):
|
|
143
167
|
matching_hooks.append(hook)
|
|
144
168
|
except Exception as e:
|
|
145
|
-
logger.error(
|
|
169
|
+
logger.error(
|
|
170
|
+
f"Error matching hook '{hook.matcher}': {e}", exc_info=True
|
|
171
|
+
)
|
|
146
172
|
return matching_hooks
|
|
147
173
|
|
|
148
174
|
def get_stats(self) -> Dict[str, Any]:
|
|
@@ -20,8 +20,8 @@ import re
|
|
|
20
20
|
import time
|
|
21
21
|
from typing import Any, Dict, List, Optional
|
|
22
22
|
|
|
23
|
-
from .models import HookConfig, EventData, ExecutionResult
|
|
24
23
|
from .matcher import _extract_file_path
|
|
24
|
+
from .models import EventData, ExecutionResult, HookConfig
|
|
25
25
|
|
|
26
26
|
logger = logging.getLogger(__name__)
|
|
27
27
|
|
|
@@ -40,6 +40,7 @@ def _build_stdin_payload(event_data: EventData) -> bytes:
|
|
|
40
40
|
"permission_mode": "default"
|
|
41
41
|
}
|
|
42
42
|
"""
|
|
43
|
+
|
|
43
44
|
def _make_serializable(obj: Any) -> Any:
|
|
44
45
|
if isinstance(obj, dict):
|
|
45
46
|
return {k: _make_serializable(v) for k, v in obj.items()}
|
|
@@ -192,7 +193,7 @@ def _substitute_variables(
|
|
|
192
193
|
result = command
|
|
193
194
|
for var, value in substitutions.items():
|
|
194
195
|
result = result.replace(f"${{{var}}}", str(value))
|
|
195
|
-
result = re.sub(rf
|
|
196
|
+
result = re.sub(rf"\${re.escape(var)}(?=\W|$)", str(value), result)
|
|
196
197
|
return result
|
|
197
198
|
|
|
198
199
|
|
|
@@ -228,16 +229,18 @@ async def execute_hooks_parallel(
|
|
|
228
229
|
final_results = []
|
|
229
230
|
for i, result in enumerate(results):
|
|
230
231
|
if isinstance(result, Exception):
|
|
231
|
-
final_results.append(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
232
|
+
final_results.append(
|
|
233
|
+
ExecutionResult(
|
|
234
|
+
blocked=False,
|
|
235
|
+
hook_command=hooks[i].command,
|
|
236
|
+
stdout="",
|
|
237
|
+
stderr=str(result),
|
|
238
|
+
exit_code=-1,
|
|
239
|
+
duration_ms=0.0,
|
|
240
|
+
error=f"Hook execution failed: {result}",
|
|
241
|
+
hook_id=hooks[i].id,
|
|
242
|
+
)
|
|
243
|
+
)
|
|
241
244
|
else:
|
|
242
245
|
final_results.append(result)
|
|
243
246
|
return final_results
|
|
@@ -79,14 +79,25 @@ def _match_single(pattern: str, tool_name: str, tool_args: Dict[str, Any]) -> bo
|
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
def _extract_file_path(tool_args: Dict[str, Any]) -> Optional[str]:
|
|
82
|
-
file_keys = [
|
|
83
|
-
|
|
82
|
+
file_keys = [
|
|
83
|
+
"file_path",
|
|
84
|
+
"file",
|
|
85
|
+
"path",
|
|
86
|
+
"target",
|
|
87
|
+
"input_file",
|
|
88
|
+
"output_file",
|
|
89
|
+
"source",
|
|
90
|
+
"destination",
|
|
91
|
+
"src",
|
|
92
|
+
"dest",
|
|
93
|
+
"filename",
|
|
94
|
+
]
|
|
84
95
|
for key in file_keys:
|
|
85
96
|
if key in tool_args:
|
|
86
97
|
value = tool_args[key]
|
|
87
98
|
if isinstance(value, str):
|
|
88
99
|
return value
|
|
89
|
-
if hasattr(value,
|
|
100
|
+
if hasattr(value, "__fspath__"):
|
|
90
101
|
return str(value)
|
|
91
102
|
for value in tool_args.values():
|
|
92
103
|
if isinstance(value, str) and _looks_like_file_path(value):
|
|
@@ -107,7 +118,7 @@ def _looks_like_file_path(value: str) -> bool:
|
|
|
107
118
|
|
|
108
119
|
|
|
109
120
|
def _is_regex_pattern(pattern: str) -> bool:
|
|
110
|
-
regex_chars = [
|
|
121
|
+
regex_chars = ["^", "$", ".", "+", "?", "[", "]", "(", ")", "{", "}", "|", "\\"]
|
|
111
122
|
return any(char in pattern for char in regex_chars)
|
|
112
123
|
|
|
113
124
|
|
|
@@ -23,6 +23,7 @@ class HookConfig:
|
|
|
23
23
|
enabled: Whether this hook is enabled (default: True)
|
|
24
24
|
id: Optional unique identifier for this hook
|
|
25
25
|
"""
|
|
26
|
+
|
|
26
27
|
matcher: str
|
|
27
28
|
type: Literal["command", "prompt"]
|
|
28
29
|
command: str
|
|
@@ -37,7 +38,9 @@ class HookConfig:
|
|
|
37
38
|
raise ValueError("Hook matcher cannot be empty")
|
|
38
39
|
|
|
39
40
|
if self.type not in ("command", "prompt"):
|
|
40
|
-
raise ValueError(
|
|
41
|
+
raise ValueError(
|
|
42
|
+
f"Hook type must be 'command' or 'prompt', got: {self.type}"
|
|
43
|
+
)
|
|
41
44
|
|
|
42
45
|
if not self.command:
|
|
43
46
|
raise ValueError("Hook command cannot be empty")
|
|
@@ -47,6 +50,7 @@ class HookConfig:
|
|
|
47
50
|
|
|
48
51
|
if self.id is None:
|
|
49
52
|
import hashlib
|
|
53
|
+
|
|
50
54
|
content = f"{self.matcher}:{self.type}:{self.command}"
|
|
51
55
|
self.id = hashlib.sha256(content.encode()).hexdigest()[:12]
|
|
52
56
|
|
|
@@ -62,6 +66,7 @@ class EventData:
|
|
|
62
66
|
tool_args: Arguments passed to the tool
|
|
63
67
|
context: Optional context metadata (result, duration, etc.)
|
|
64
68
|
"""
|
|
69
|
+
|
|
65
70
|
event_type: str
|
|
66
71
|
tool_name: str
|
|
67
72
|
tool_args: Dict[str, Any] = field(default_factory=dict)
|
|
@@ -89,6 +94,7 @@ class ExecutionResult:
|
|
|
89
94
|
error: Error message if execution failed
|
|
90
95
|
hook_id: ID of the hook that was executed
|
|
91
96
|
"""
|
|
97
|
+
|
|
92
98
|
blocked: bool
|
|
93
99
|
hook_command: str
|
|
94
100
|
stdout: str = ""
|
|
@@ -117,6 +123,7 @@ class ExecutionResult:
|
|
|
117
123
|
@dataclass
|
|
118
124
|
class HookGroup:
|
|
119
125
|
"""A group of hooks that share the same matcher."""
|
|
126
|
+
|
|
120
127
|
matcher: str
|
|
121
128
|
hooks: List[HookConfig] = field(default_factory=list)
|
|
122
129
|
|
|
@@ -128,6 +135,7 @@ class HookGroup:
|
|
|
128
135
|
@dataclass
|
|
129
136
|
class HookRegistry:
|
|
130
137
|
"""Registry of all hooks organized by event type."""
|
|
138
|
+
|
|
131
139
|
pre_tool_use: List[HookConfig] = field(default_factory=list)
|
|
132
140
|
post_tool_use: List[HookConfig] = field(default_factory=list)
|
|
133
141
|
session_start: List[HookConfig] = field(default_factory=list)
|
|
@@ -163,8 +171,9 @@ class HookRegistry:
|
|
|
163
171
|
@staticmethod
|
|
164
172
|
def _normalize_event_type(event_type: str) -> str:
|
|
165
173
|
import re
|
|
166
|
-
|
|
167
|
-
|
|
174
|
+
|
|
175
|
+
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", event_type)
|
|
176
|
+
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
|
|
168
177
|
|
|
169
178
|
def add_hook(self, event_type: str, hook: HookConfig) -> None:
|
|
170
179
|
attr_name = self._normalize_event_type(event_type)
|
|
@@ -186,9 +195,17 @@ class HookRegistry:
|
|
|
186
195
|
def count_hooks(self, event_type: Optional[str] = None) -> int:
|
|
187
196
|
if event_type is None:
|
|
188
197
|
total = 0
|
|
189
|
-
for attr in [
|
|
190
|
-
|
|
191
|
-
|
|
198
|
+
for attr in [
|
|
199
|
+
"pre_tool_use",
|
|
200
|
+
"post_tool_use",
|
|
201
|
+
"session_start",
|
|
202
|
+
"session_end",
|
|
203
|
+
"pre_compact",
|
|
204
|
+
"user_prompt_submit",
|
|
205
|
+
"notification",
|
|
206
|
+
"stop",
|
|
207
|
+
"subagent_stop",
|
|
208
|
+
]:
|
|
192
209
|
total += len(getattr(self, attr))
|
|
193
210
|
return total
|
|
194
211
|
attr_name = self._normalize_event_type(event_type)
|
|
@@ -200,6 +217,7 @@ class HookRegistry:
|
|
|
200
217
|
@dataclass
|
|
201
218
|
class ProcessEventResult:
|
|
202
219
|
"""Result from processing an event through the hook engine."""
|
|
220
|
+
|
|
203
221
|
blocked: bool
|
|
204
222
|
executed_hooks: int
|
|
205
223
|
results: List[ExecutionResult]
|
|
@@ -6,7 +6,7 @@ Builds and manages the HookRegistry from configuration dictionaries.
|
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
8
|
import re
|
|
9
|
-
from typing import Any, Dict
|
|
9
|
+
from typing import Any, Dict
|
|
10
10
|
|
|
11
11
|
from .models import HookConfig, HookRegistry
|
|
12
12
|
|
|
@@ -86,8 +86,8 @@ def get_registry_stats(registry: HookRegistry) -> Dict[str, Any]:
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
def _to_attr(event_type: str) -> str:
|
|
89
|
-
s1 = re.sub(
|
|
90
|
-
return re.sub(
|
|
89
|
+
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", event_type)
|
|
90
|
+
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
|
|
91
91
|
|
|
92
92
|
for event_type in SUPPORTED_EVENT_TYPES:
|
|
93
93
|
attr = _to_attr(event_type)
|
|
@@ -53,7 +53,9 @@ def validate_hooks_config(config: Dict[str, Any]) -> Tuple[bool, List[str]]:
|
|
|
53
53
|
|
|
54
54
|
for i, group in enumerate(hook_groups):
|
|
55
55
|
if not isinstance(group, dict):
|
|
56
|
-
errors.append(
|
|
56
|
+
errors.append(
|
|
57
|
+
f"'{event_type}[{i}]' must be a dict with 'matcher' and 'hooks'"
|
|
58
|
+
)
|
|
57
59
|
continue
|
|
58
60
|
|
|
59
61
|
if "matcher" not in group:
|
|
@@ -74,8 +76,9 @@ def validate_hooks_config(config: Dict[str, Any]) -> Tuple[bool, List[str]]:
|
|
|
74
76
|
return len(errors) == 0, errors
|
|
75
77
|
|
|
76
78
|
|
|
77
|
-
def _validate_hook(
|
|
78
|
-
|
|
79
|
+
def _validate_hook(
|
|
80
|
+
event_type: str, group_idx: int, hook_idx: int, hook: Any
|
|
81
|
+
) -> List[str]:
|
|
79
82
|
errors: List[str] = []
|
|
80
83
|
prefix = f"'{event_type}[{group_idx}].hooks[{hook_idx}]'"
|
|
81
84
|
|
|
@@ -86,7 +89,9 @@ def _validate_hook(event_type: str, group_idx: int, hook_idx: int,
|
|
|
86
89
|
if not hook_type:
|
|
87
90
|
errors.append(f"{prefix} missing required field 'type'")
|
|
88
91
|
elif hook_type not in VALID_HOOK_TYPES:
|
|
89
|
-
errors.append(
|
|
92
|
+
errors.append(
|
|
93
|
+
f"{prefix} invalid type '{hook_type}'. Must be one of: {', '.join(VALID_HOOK_TYPES)}"
|
|
94
|
+
)
|
|
90
95
|
|
|
91
96
|
if hook_type == "command" and not hook.get("command"):
|
|
92
97
|
errors.append(f"{prefix} missing required field 'command' for type 'command'")
|
|
@@ -103,8 +108,9 @@ def _validate_hook(event_type: str, group_idx: int, hook_idx: int,
|
|
|
103
108
|
return errors
|
|
104
109
|
|
|
105
110
|
|
|
106
|
-
def format_validation_report(
|
|
107
|
-
|
|
111
|
+
def format_validation_report(
|
|
112
|
+
is_valid: bool, errors: List[str], suggestions: Optional[List[str]] = None
|
|
113
|
+
) -> str:
|
|
108
114
|
lines = []
|
|
109
115
|
if is_valid:
|
|
110
116
|
lines.append("✓ Configuration is valid")
|
|
@@ -121,15 +127,12 @@ def format_validation_report(is_valid: bool, errors: List[str],
|
|
|
121
127
|
return "\n".join(lines)
|
|
122
128
|
|
|
123
129
|
|
|
124
|
-
def get_config_suggestions(config: Dict[str, Any],
|
|
125
|
-
errors: List[str]) -> List[str]:
|
|
130
|
+
def get_config_suggestions(config: Dict[str, Any], errors: List[str]) -> List[str]:
|
|
126
131
|
suggestions: List[str] = []
|
|
127
132
|
|
|
128
133
|
for error in errors:
|
|
129
134
|
if "Unknown event type" in error:
|
|
130
|
-
suggestions.append(
|
|
131
|
-
"Valid event types are: " + ", ".join(VALID_EVENT_TYPES)
|
|
132
|
-
)
|
|
135
|
+
suggestions.append("Valid event types are: " + ", ".join(VALID_EVENT_TYPES))
|
|
133
136
|
break
|
|
134
137
|
|
|
135
138
|
if any("missing required field 'command'" in e for e in errors):
|
|
@@ -5,6 +5,7 @@ This module provides a managed wrapper around pydantic-ai MCP server classes
|
|
|
5
5
|
that adds management capabilities while maintaining 100% compatibility.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import json
|
|
8
9
|
import os
|
|
9
10
|
import uuid
|
|
10
11
|
from dataclasses import dataclass, field
|
|
@@ -24,6 +25,7 @@ from pydantic_ai.mcp import (
|
|
|
24
25
|
|
|
25
26
|
from code_puppy.http_utils import create_async_client
|
|
26
27
|
from code_puppy.mcp_.blocking_startup import BlockingMCPServerStdio
|
|
28
|
+
from code_puppy.messaging import emit_info
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
def _expand_env_vars(value: Any) -> Any:
|
|
@@ -80,14 +82,13 @@ async def process_tool_call(
|
|
|
80
82
|
tool_args: dict[str, Any],
|
|
81
83
|
) -> ToolResult:
|
|
82
84
|
"""A tool call processor that passes along the deps."""
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
console.print(f"\n{banner} [bold cyan]{name}[/bold cyan]")
|
|
85
|
+
group_id = uuid.uuid4()
|
|
86
|
+
emit_info(
|
|
87
|
+
f"\nMCP Tool Call - {name}",
|
|
88
|
+
message_group=group_id,
|
|
89
|
+
)
|
|
90
|
+
emit_info("\nArgs:", message_group=group_id)
|
|
91
|
+
emit_info(json.dumps(tool_args, indent=2), message_group=group_id)
|
|
91
92
|
return await call_tool(name, tool_args, {"deps": ctx.deps})
|
|
92
93
|
|
|
93
94
|
|
|
@@ -586,8 +586,8 @@ class SkillsInstallMenu:
|
|
|
586
586
|
import termios
|
|
587
587
|
|
|
588
588
|
termios.tcflush(sys.stdin.fileno(), termios.TCIFLUSH)
|
|
589
|
-
except
|
|
590
|
-
pass # Windows or not a tty
|
|
589
|
+
except Exception:
|
|
590
|
+
pass # ImportError on Windows, termios.error, or not a tty
|
|
591
591
|
|
|
592
592
|
# Small delay to let terminal settle before any output
|
|
593
593
|
time.sleep(0.1)
|
|
@@ -468,8 +468,8 @@ class SkillsMenu:
|
|
|
468
468
|
import termios
|
|
469
469
|
|
|
470
470
|
termios.tcflush(sys.stdin.fileno(), termios.TCIFLUSH)
|
|
471
|
-
except
|
|
472
|
-
pass # Windows or not a tty
|
|
471
|
+
except Exception:
|
|
472
|
+
pass # ImportError on Windows, termios.error, or not a tty
|
|
473
473
|
|
|
474
474
|
# Small delay to let terminal settle before any output
|
|
475
475
|
time.sleep(0.1)
|