hanzo-mcp 0.8.6__tar.gz → 0.8.8__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.
Potentially problematic release.
This version of hanzo-mcp might be problematic. Click here for more details.
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/PKG-INFO +1 -1
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/analytics/posthog_analytics.py +8 -1
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/config/tool_config.py +1 -1
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/__init__.py +13 -1
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/agent_tool.py +2 -1
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +1 -1
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/swarm_tool.py +12 -7
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/stats.py +1 -1
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/mcp/mcp_add.py +2 -2
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/mcp/mcp_remove.py +1 -1
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/search/unified_search.py +3 -3
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/vector/infinity_store.py +3 -4
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp.egg-info/PKG-INFO +1 -1
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp.egg-info/SOURCES.txt +1 -1
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/pyproject.toml +1 -1
- hanzo_mcp-0.8.8/tests/test_no_stubs.py +299 -0
- hanzo_mcp-0.8.6/hanzo_mcp/tools/vector/mock_infinity.py +0 -161
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/README.md +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/__main__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/analytics/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/bridge.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/cli.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/cli_enhanced.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/cli_plugin.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/compute_nodes.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/config/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/config/settings.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/core/base_agent.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/core/model_registry.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/dev_server.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/prompts/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/prompts/compact_conversation.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/prompts/create_release.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/prompts/enhanced_prompts.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/prompts/example_custom_prompt.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/prompts/project_system.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/prompts/project_todo_reminder.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/prompts/tool_explorer.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/prompts/utils.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/server.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/server_enhanced.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/agent.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/clarification_protocol.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/clarification_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/claude_cli_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/claude_desktop_auth.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/cli_agent_base.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/cli_tools.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/code_auth.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/code_auth_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/codex_cli_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/critic_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/gemini_cli_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/grok_cli_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/iching_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/network_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/prompt.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/review_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/swarm_alias.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/tool_adapter.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/agent/unified_cli_tools.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/base.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/batch_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/config_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/context.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/context_fix.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/critic_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/decorators.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/enhanced_base.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/fastmcp_pagination.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/forgiving_edit.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/mode.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/mode_loader.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/paginated_base.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/paginated_response.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/pagination.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/permissions.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/personality.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/plugin_loader.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/test_helpers.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/thinking_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/tool_disable.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/tool_enable.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/tool_list.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/truncate.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/common/validation.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/config/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/config/config_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/config/index_config.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/config/mode_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/database_manager.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/graph.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/graph_add.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/graph_query.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/graph_remove.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/graph_search.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/graph_stats.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/sql.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/sql_query.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/sql_search.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/database/sql_stats.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/editor/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/editor/neovim_command.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/editor/neovim_edit.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/editor/neovim_session.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/ast_multi_edit.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/ast_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/base.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/batch_search.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/content_replace.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/diff.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/directory_tree.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/directory_tree_paginated.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/edit.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/find.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/find_files.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/git_search.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/grep.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/multi_edit.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/read.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/rules_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/search_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/symbols_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/tree.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/unix_aliases.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/watch.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/filesystem/write.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/jupyter/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/jupyter/base.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/jupyter/jupyter.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/jupyter/notebook_edit.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/jupyter/notebook_read.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/llm/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/llm/consensus_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/llm/llm_manage.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/llm/llm_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/llm/llm_unified.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/llm/provider_tools.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/lsp/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/lsp/lsp_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/mcp/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/mcp/mcp_stats.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/mcp/mcp_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/memory/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/memory/knowledge_tools.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/memory/memory_tools.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/search/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/search/find_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/auto_background.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/base.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/base_process.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/bash_session.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/bash_session_executor.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/bash_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/command_executor.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/logs.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/npx.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/npx_background.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/npx_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/open.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/pkill.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/process_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/processes.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/run_background.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/run_command.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/run_command_windows.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/session_manager.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/session_storage.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/streaming_command.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/uvx.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/uvx_background.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/uvx_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/shell/zsh_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/todo/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/todo/base.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/todo/todo.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/todo/todo_read.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/todo/todo_write.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/vector/__init__.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/vector/ast_analyzer.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/vector/git_ingester.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/vector/index_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/vector/project_manager.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/vector/vector.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/vector/vector_index.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/tools/vector/vector_search.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp/types.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp.egg-info/dependency_links.txt +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp.egg-info/entry_points.txt +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp.egg-info/requires.txt +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/hanzo_mcp.egg-info/top_level.txt +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/setup.cfg +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_agent_tools_ci.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_async_support.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_batch_tool_edge_cases.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_cli.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_cli_agents_consolidated.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_cli_tools.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_e2e_demo.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_e2e_simple.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_failure_cases.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_find_tool_ffind.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_find_tool_integration.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_find_tool_registration.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_git_ingestion.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_hanzo_agents_integration.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_hanzo_mcp_integration.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_hanzo_mcp_local.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_hanzo_mcp_simple.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_hanzo_network_integration.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_litellm_warnings.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_lsp_tool.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_manual.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_memory_base.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_memory_basic.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_memory_consolidated.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_memory_simple.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_memory_utils.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_new_tools.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_performance.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_search.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_search_quality.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_shell_features.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_shell_tools.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_simple.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_stdio_protocol.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_stdio_simple.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_streaming_command.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_swarm_simple.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_tools_suite.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_unified_search.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_utils.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_vector_store.py +0 -0
- {hanzo_mcp-0.8.6 → hanzo_mcp-0.8.8}/tests/test_web3_integration.py +0 -0
|
@@ -16,6 +16,7 @@ import traceback
|
|
|
16
16
|
from typing import Any, Dict, TypeVar, Callable, Optional
|
|
17
17
|
from datetime import datetime
|
|
18
18
|
from dataclasses import dataclass
|
|
19
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
19
20
|
|
|
20
21
|
# Try to import PostHog, but make it optional
|
|
21
22
|
try:
|
|
@@ -26,6 +27,12 @@ except ImportError:
|
|
|
26
27
|
POSTHOG_AVAILABLE = False
|
|
27
28
|
Posthog = None
|
|
28
29
|
|
|
30
|
+
# Get package version
|
|
31
|
+
try:
|
|
32
|
+
MCP_VERSION = version("hanzo-mcp")
|
|
33
|
+
except PackageNotFoundError:
|
|
34
|
+
MCP_VERSION = "0.8.7" # Fallback version
|
|
35
|
+
|
|
29
36
|
|
|
30
37
|
F = TypeVar("F", bound=Callable[..., Any])
|
|
31
38
|
|
|
@@ -98,7 +105,7 @@ class Analytics:
|
|
|
98
105
|
"timestamp": datetime.utcnow().isoformat(),
|
|
99
106
|
"platform": platform.system(),
|
|
100
107
|
"python_version": platform.python_version(),
|
|
101
|
-
"mcp_version":
|
|
108
|
+
"mcp_version": MCP_VERSION,
|
|
102
109
|
**(properties or {}),
|
|
103
110
|
}
|
|
104
111
|
|
|
@@ -105,7 +105,7 @@ TOOL_REGISTRY: Dict[str, ToolConfig] = {
|
|
|
105
105
|
description="Edit Jupyter notebook cells (replace, insert, delete)",
|
|
106
106
|
cli_flag="--disable-notebook-edit",
|
|
107
107
|
),
|
|
108
|
-
#
|
|
108
|
+
# Task Management Tools (2)
|
|
109
109
|
"todo_read": ToolConfig(
|
|
110
110
|
name="todo_read",
|
|
111
111
|
category=ToolCategory.TODO,
|
|
@@ -64,7 +64,19 @@ try: # pragma: no cover
|
|
|
64
64
|
register_memory_tools = None # type: ignore
|
|
65
65
|
except Exception:
|
|
66
66
|
# Minimal surface to allow submodule imports elsewhere
|
|
67
|
-
|
|
67
|
+
# Define fallback functions for required imports
|
|
68
|
+
def activate_mode_from_env():
|
|
69
|
+
"""Fallback: No mode activation when imports fail."""
|
|
70
|
+
return None
|
|
71
|
+
class ModeLoader:
|
|
72
|
+
@staticmethod
|
|
73
|
+
def get_enabled_tools_from_mode(base_enabled_tools=None, force_mode=None):
|
|
74
|
+
"""Fallback: Return base tools when imports fail."""
|
|
75
|
+
return base_enabled_tools or {}
|
|
76
|
+
@staticmethod
|
|
77
|
+
def apply_environment_from_mode():
|
|
78
|
+
"""Fallback: No environment changes when imports fail."""
|
|
79
|
+
return None
|
|
68
80
|
|
|
69
81
|
# Try to import LSP tool
|
|
70
82
|
try:
|
|
@@ -96,7 +96,7 @@ class AgentTool(AgentClarificationMixin, BaseTool):
|
|
|
96
96
|
Returns:
|
|
97
97
|
Tool description
|
|
98
98
|
"""
|
|
99
|
-
#
|
|
99
|
+
# Glob is now implemented via find_files tool
|
|
100
100
|
at = [t.name for t in self.available_tools]
|
|
101
101
|
|
|
102
102
|
return f"""Launch a new agent that has access to the following tools: {at}. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries, use the Agent tool to perform the search for you.
|
|
@@ -96,12 +96,14 @@ except ImportError:
|
|
|
96
96
|
# Try core module import
|
|
97
97
|
from hanzo_agents.core.memory import create_memory_kv, create_memory_vector
|
|
98
98
|
except ImportError:
|
|
99
|
-
# Define
|
|
99
|
+
# Define fallback functions if not available
|
|
100
100
|
def create_memory_kv(*args, **kwargs):
|
|
101
|
-
|
|
101
|
+
"""Fallback: Key-value memory not available."""
|
|
102
|
+
return {}
|
|
102
103
|
|
|
103
104
|
def create_memory_vector(*args, **kwargs):
|
|
104
|
-
|
|
105
|
+
"""Fallback: Vector memory not available."""
|
|
106
|
+
return {}
|
|
105
107
|
|
|
106
108
|
|
|
107
109
|
try:
|
|
@@ -115,15 +117,18 @@ except ImportError:
|
|
|
115
117
|
state_based_router,
|
|
116
118
|
)
|
|
117
119
|
except ImportError:
|
|
118
|
-
# Define
|
|
120
|
+
# Define fallback functions if not available
|
|
119
121
|
def sequential_router(*args, **kwargs):
|
|
120
|
-
|
|
122
|
+
"""Fallback: Sequential router not available."""
|
|
123
|
+
return lambda agents, task: None
|
|
121
124
|
|
|
122
125
|
def conditional_router(*args, **kwargs):
|
|
123
|
-
|
|
126
|
+
"""Fallback: Conditional router not available."""
|
|
127
|
+
return lambda agents, task: None
|
|
124
128
|
|
|
125
129
|
def state_based_router(*args, **kwargs):
|
|
126
|
-
|
|
130
|
+
"""Fallback: State-based router not available."""
|
|
131
|
+
return lambda agents, task: None
|
|
127
132
|
|
|
128
133
|
|
|
129
134
|
try:
|
|
@@ -236,7 +236,7 @@ Example:
|
|
|
236
236
|
output.append(f"Config Files: {config_count}")
|
|
237
237
|
|
|
238
238
|
# Tool status (if available)
|
|
239
|
-
#
|
|
239
|
+
# Tool usage statistics can be tracked via analytics
|
|
240
240
|
output.append("\nTool Categories:")
|
|
241
241
|
output.append(" - File Operations: grep, find_files, read, write, edit")
|
|
242
242
|
output.append(" - Shell: bash, run_background, processes, pkill")
|
|
@@ -221,8 +221,8 @@ Use 'mcp_stats' to see all added servers and their status.
|
|
|
221
221
|
if not shutil.which("uvx"):
|
|
222
222
|
return "Error: uvx not found. Install uv first."
|
|
223
223
|
|
|
224
|
-
#
|
|
225
|
-
#
|
|
224
|
+
# MCP server connection handled by runtime
|
|
225
|
+
# Store configuration for runtime to use
|
|
226
226
|
server_config["status"] = "ready"
|
|
227
227
|
|
|
228
228
|
except Exception as e:
|
|
@@ -103,7 +103,7 @@ Use 'mcp_stats' to see all servers before removing.
|
|
|
103
103
|
if not force:
|
|
104
104
|
return f"Error: Server '{name}' is currently running. Use --force to remove anyway."
|
|
105
105
|
else:
|
|
106
|
-
#
|
|
106
|
+
# Server process stopping handled by MCP runtime
|
|
107
107
|
await tool_ctx.info(f"Stopping running server '{name}'")
|
|
108
108
|
|
|
109
109
|
# Remove from registry
|
|
@@ -93,7 +93,7 @@ class UnifiedSearch(BaseTool):
|
|
|
93
93
|
|
|
94
94
|
1. Find code patterns:
|
|
95
95
|
search("error handling") # Finds all error handling code
|
|
96
|
-
search("
|
|
96
|
+
search("TASK|FIX") # Regex search for task markers
|
|
97
97
|
search("async function") # Find async functions
|
|
98
98
|
|
|
99
99
|
2. Find symbols/definitions:
|
|
@@ -963,8 +963,8 @@ class CodeIndexer:
|
|
|
963
963
|
self, content: str, file_path: Path
|
|
964
964
|
) -> List[Dict[str, Any]]:
|
|
965
965
|
"""Split code into meaningful chunks."""
|
|
966
|
-
#
|
|
967
|
-
#
|
|
966
|
+
# Using line-based splitting for compatibility with all file types
|
|
967
|
+
# AST parsing would only work for specific languages
|
|
968
968
|
chunks = []
|
|
969
969
|
lines = content.split("\n")
|
|
970
970
|
|
|
@@ -11,10 +11,9 @@ try:
|
|
|
11
11
|
|
|
12
12
|
INFINITY_AVAILABLE = True
|
|
13
13
|
except ImportError:
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
INFINITY_AVAILABLE = True # Mock is always available
|
|
14
|
+
# infinity_embedded not available, functionality will be limited
|
|
15
|
+
INFINITY_AVAILABLE = False
|
|
16
|
+
infinity_embedded = None # type: ignore
|
|
18
17
|
|
|
19
18
|
from .ast_analyzer import Symbol, FileAST, ASTAnalyzer, create_symbol_embedding_text
|
|
20
19
|
|
|
@@ -189,7 +189,6 @@ hanzo_mcp/tools/vector/ast_analyzer.py
|
|
|
189
189
|
hanzo_mcp/tools/vector/git_ingester.py
|
|
190
190
|
hanzo_mcp/tools/vector/index_tool.py
|
|
191
191
|
hanzo_mcp/tools/vector/infinity_store.py
|
|
192
|
-
hanzo_mcp/tools/vector/mock_infinity.py
|
|
193
192
|
hanzo_mcp/tools/vector/project_manager.py
|
|
194
193
|
hanzo_mcp/tools/vector/vector.py
|
|
195
194
|
hanzo_mcp/tools/vector/vector_index.py
|
|
@@ -221,6 +220,7 @@ tests/test_memory_consolidated.py
|
|
|
221
220
|
tests/test_memory_simple.py
|
|
222
221
|
tests/test_memory_utils.py
|
|
223
222
|
tests/test_new_tools.py
|
|
223
|
+
tests/test_no_stubs.py
|
|
224
224
|
tests/test_performance.py
|
|
225
225
|
tests/test_search.py
|
|
226
226
|
tests/test_search_quality.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hanzo-mcp"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.8"
|
|
8
8
|
description = "The Zen of Hanzo MCP: One server to rule them all. The ultimate MCP that orchestrates all others."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""Test to ensure no stub/fake/incomplete code exists in production."""
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import List, Tuple
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class StubDetector(ast.NodeVisitor):
|
|
13
|
+
"""AST visitor to detect stub implementations."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, filepath: str):
|
|
16
|
+
self.filepath = filepath
|
|
17
|
+
self.issues: List[Tuple[int, str]] = []
|
|
18
|
+
self.in_test_file = 'test' in filepath or 'mock' in filepath.lower()
|
|
19
|
+
|
|
20
|
+
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
21
|
+
"""Check function definitions for stub patterns."""
|
|
22
|
+
# Skip test files for certain checks
|
|
23
|
+
if self.in_test_file and node.name.startswith('test_'):
|
|
24
|
+
self.generic_visit(node)
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
# Check for empty functions with just pass
|
|
28
|
+
if len(node.body) == 1 and isinstance(node.body[0], ast.Pass):
|
|
29
|
+
# Check if there's a comment indicating it's a stub
|
|
30
|
+
if node.body[0].lineno:
|
|
31
|
+
self.issues.append((
|
|
32
|
+
node.lineno,
|
|
33
|
+
f"Function '{node.name}' contains only 'pass' statement"
|
|
34
|
+
))
|
|
35
|
+
|
|
36
|
+
# Check for functions that just raise NotImplementedError
|
|
37
|
+
if len(node.body) == 1 and isinstance(node.body[0], ast.Raise):
|
|
38
|
+
if isinstance(node.body[0].exc, ast.Call):
|
|
39
|
+
if (hasattr(node.body[0].exc.func, 'id') and
|
|
40
|
+
node.body[0].exc.func.id == 'NotImplementedError'):
|
|
41
|
+
self.issues.append((
|
|
42
|
+
node.lineno,
|
|
43
|
+
f"Function '{node.name}' raises NotImplementedError"
|
|
44
|
+
))
|
|
45
|
+
|
|
46
|
+
# Check for functions with only ellipsis
|
|
47
|
+
if len(node.body) == 1 and isinstance(node.body[0], ast.Expr):
|
|
48
|
+
if isinstance(node.body[0].value, ast.Constant):
|
|
49
|
+
if node.body[0].value.value is Ellipsis:
|
|
50
|
+
self.issues.append((
|
|
51
|
+
node.lineno,
|
|
52
|
+
f"Function '{node.name}' contains only ellipsis"
|
|
53
|
+
))
|
|
54
|
+
|
|
55
|
+
self.generic_visit(node)
|
|
56
|
+
|
|
57
|
+
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
|
58
|
+
"""Check async function definitions."""
|
|
59
|
+
# Treat async functions same as regular functions
|
|
60
|
+
self.visit_FunctionDef(node)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def find_stub_patterns(filepath: Path) -> List[Tuple[int, str, str]]:
|
|
64
|
+
"""Find stub patterns in a Python file."""
|
|
65
|
+
issues = []
|
|
66
|
+
|
|
67
|
+
# Skip test files for most checks
|
|
68
|
+
is_test_file = 'test' in filepath.name or 'mock' in filepath.name.lower()
|
|
69
|
+
|
|
70
|
+
# Files where NotImplementedError is legitimate (abstract base classes, feature flags)
|
|
71
|
+
allowed_not_implemented = {
|
|
72
|
+
'base_agent.py', # Abstract base class
|
|
73
|
+
'posthog_analytics.py', # Feature flag decorator
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
content = filepath.read_text(encoding='utf-8')
|
|
78
|
+
except Exception:
|
|
79
|
+
return issues
|
|
80
|
+
|
|
81
|
+
# Regex patterns to find stub indicators
|
|
82
|
+
patterns = [
|
|
83
|
+
(r'#\s*(TODO|FIXME|STUB|FAKE|UNFINISHED|HACK|XXX)\s*:?', 'contains {0} comment'),
|
|
84
|
+
(r'assert\s+False,?\s*["\']Not implemented', 'has "Not implemented" assertion'),
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
# Only check for NotImplementedError in files where it's not allowed
|
|
88
|
+
if filepath.name not in allowed_not_implemented:
|
|
89
|
+
patterns.append((r'raise\s+NotImplementedError', 'raises NotImplementedError'))
|
|
90
|
+
|
|
91
|
+
# Additional patterns for non-test files
|
|
92
|
+
if not is_test_file:
|
|
93
|
+
patterns.extend([
|
|
94
|
+
(r'pass\s*#\s*(stub|todo|fake)', 'has stub/todo/fake comment after pass'),
|
|
95
|
+
(r'^\s*return\s+["\']TODO["\']$', 'returns TODO string'), # Only match exact "TODO"
|
|
96
|
+
(r'^\s*return\s+["\']STUB["\']$', 'returns STUB string'), # Only match exact "STUB"
|
|
97
|
+
(r'return\s+None\s*#\s*(TODO|STUB|FAKE)', 'returns None with stub comment'),
|
|
98
|
+
])
|
|
99
|
+
|
|
100
|
+
lines = content.split('\n')
|
|
101
|
+
for line_num, line in enumerate(lines, 1):
|
|
102
|
+
for pattern, message in patterns:
|
|
103
|
+
# Use case-sensitive matching for TODO/STUB literal strings
|
|
104
|
+
flags = 0 if 'TODO' in pattern and ('returns' in message) else re.IGNORECASE
|
|
105
|
+
if match := re.search(pattern, line, flags):
|
|
106
|
+
keyword = match.group(1) if match.groups() else 'stub pattern'
|
|
107
|
+
issues.append((
|
|
108
|
+
line_num,
|
|
109
|
+
message.format(keyword),
|
|
110
|
+
filepath.name
|
|
111
|
+
))
|
|
112
|
+
|
|
113
|
+
# Parse AST for deeper inspection
|
|
114
|
+
try:
|
|
115
|
+
tree = ast.parse(content)
|
|
116
|
+
detector = StubDetector(str(filepath))
|
|
117
|
+
detector.visit(tree)
|
|
118
|
+
for line_num, message in detector.issues:
|
|
119
|
+
issues.append((line_num, message, filepath.name))
|
|
120
|
+
except SyntaxError:
|
|
121
|
+
pass # Ignore files with syntax errors
|
|
122
|
+
|
|
123
|
+
return issues
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def get_python_files(root_dir: Path, exclude_dirs: set = None) -> List[Path]:
|
|
127
|
+
"""Get all Python files in directory, excluding certain directories."""
|
|
128
|
+
if exclude_dirs is None:
|
|
129
|
+
exclude_dirs = {
|
|
130
|
+
'__pycache__', '.git', '.tox', '.pytest_cache',
|
|
131
|
+
'build', 'dist', '*.egg-info', '.venv', 'venv',
|
|
132
|
+
'node_modules', '.mypy_cache'
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
python_files = []
|
|
136
|
+
for path in root_dir.rglob('*.py'):
|
|
137
|
+
# Skip excluded directories
|
|
138
|
+
if any(excluded in path.parts for excluded in exclude_dirs):
|
|
139
|
+
continue
|
|
140
|
+
python_files.append(path)
|
|
141
|
+
|
|
142
|
+
return python_files
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class TestNoStubs:
|
|
146
|
+
"""Test suite to ensure no stub implementations exist."""
|
|
147
|
+
|
|
148
|
+
def test_no_stub_functions_in_source(self):
|
|
149
|
+
"""Ensure no stub functions exist in source code."""
|
|
150
|
+
# Get the package root
|
|
151
|
+
package_root = Path(__file__).parent.parent / 'hanzo_mcp'
|
|
152
|
+
|
|
153
|
+
if not package_root.exists():
|
|
154
|
+
pytest.skip(f"Package root {package_root} does not exist")
|
|
155
|
+
|
|
156
|
+
all_issues = []
|
|
157
|
+
python_files = get_python_files(package_root)
|
|
158
|
+
|
|
159
|
+
for filepath in python_files:
|
|
160
|
+
issues = find_stub_patterns(filepath)
|
|
161
|
+
for line_num, message, filename in issues:
|
|
162
|
+
all_issues.append(
|
|
163
|
+
f"{filepath.relative_to(package_root.parent)}:{line_num} - {message}"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if all_issues:
|
|
167
|
+
report = "\n".join(all_issues)
|
|
168
|
+
pytest.fail(
|
|
169
|
+
f"Found {len(all_issues)} stub/incomplete implementations:\n{report}"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def test_critical_functions_implemented(self):
|
|
173
|
+
"""Ensure critical functions are actually implemented."""
|
|
174
|
+
package_root = Path(__file__).parent.parent / 'hanzo_mcp'
|
|
175
|
+
|
|
176
|
+
# Critical modules and functions that must be implemented
|
|
177
|
+
critical_checks = [
|
|
178
|
+
('tools/__init__.py', 'register_all_tools'),
|
|
179
|
+
('server.py', '__init__'),
|
|
180
|
+
('server.py', 'run'),
|
|
181
|
+
('cli.py', 'main'),
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
for module_path, function_name in critical_checks:
|
|
185
|
+
filepath = package_root / module_path
|
|
186
|
+
if not filepath.exists():
|
|
187
|
+
pytest.fail(f"Critical module {module_path} does not exist")
|
|
188
|
+
|
|
189
|
+
content = filepath.read_text()
|
|
190
|
+
# Check function exists and has more than just pass/raise
|
|
191
|
+
pattern = rf'def {function_name}\([^)]*\):[^:]*\n(?:\s+"""[^"]*"""\n)?(\s+.+)'
|
|
192
|
+
match = re.search(pattern, content, re.MULTILINE)
|
|
193
|
+
|
|
194
|
+
if not match:
|
|
195
|
+
pytest.fail(f"Function {function_name} not found in {module_path}")
|
|
196
|
+
|
|
197
|
+
function_body = match.group(1).strip()
|
|
198
|
+
if function_body in ['pass', 'raise NotImplementedError', 'raise NotImplementedError()', '...']:
|
|
199
|
+
pytest.fail(f"Function {function_name} in {module_path} is not implemented")
|
|
200
|
+
|
|
201
|
+
def test_no_pytest_skip_in_non_test_files(self):
|
|
202
|
+
"""Ensure pytest.skip is only used in test files."""
|
|
203
|
+
package_root = Path(__file__).parent.parent / 'hanzo_mcp'
|
|
204
|
+
|
|
205
|
+
for filepath in get_python_files(package_root):
|
|
206
|
+
# Skip test directories
|
|
207
|
+
if 'test' in str(filepath):
|
|
208
|
+
continue
|
|
209
|
+
|
|
210
|
+
content = filepath.read_text()
|
|
211
|
+
if 'pytest.skip' in content or '@pytest.mark.skip' in content:
|
|
212
|
+
pytest.fail(f"Found pytest.skip in non-test file: {filepath}")
|
|
213
|
+
|
|
214
|
+
def test_no_mock_implementations_in_production(self):
|
|
215
|
+
"""Ensure no mock implementations exist in production code."""
|
|
216
|
+
package_root = Path(__file__).parent.parent / 'hanzo_mcp'
|
|
217
|
+
|
|
218
|
+
for filepath in get_python_files(package_root):
|
|
219
|
+
# Skip test directories and legitimate mock modules
|
|
220
|
+
if 'test' in str(filepath) or 'mock' in filepath.name:
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
content = filepath.read_text()
|
|
224
|
+
|
|
225
|
+
# Check for mock-related imports in production code
|
|
226
|
+
mock_patterns = [
|
|
227
|
+
r'from unittest\.mock import',
|
|
228
|
+
r'import unittest\.mock',
|
|
229
|
+
r'class Mock',
|
|
230
|
+
r'class Fake',
|
|
231
|
+
r'def fake_',
|
|
232
|
+
r'def mock_',
|
|
233
|
+
r'return\s+["\']fake',
|
|
234
|
+
r'return\s+["\']mock',
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
for pattern in mock_patterns:
|
|
238
|
+
if re.search(pattern, content, re.IGNORECASE):
|
|
239
|
+
pytest.fail(
|
|
240
|
+
f"Found mock/fake pattern '{pattern}' in production file: {filepath}"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def test_all_tool_classes_have_run_method(self):
|
|
244
|
+
"""Ensure all tool classes have a proper run method."""
|
|
245
|
+
package_root = Path(__file__).parent.parent / 'hanzo_mcp' / 'tools'
|
|
246
|
+
|
|
247
|
+
if not package_root.exists():
|
|
248
|
+
pytest.skip("Tools directory does not exist")
|
|
249
|
+
|
|
250
|
+
for filepath in get_python_files(package_root):
|
|
251
|
+
if 'test' in str(filepath) or '__pycache__' in str(filepath):
|
|
252
|
+
continue
|
|
253
|
+
|
|
254
|
+
content = filepath.read_text()
|
|
255
|
+
|
|
256
|
+
# Find all class definitions that inherit from BaseTool or end with Tool
|
|
257
|
+
class_pattern = r'class\s+(\w*Tool\w*)\s*\([^)]*\):'
|
|
258
|
+
classes = re.findall(class_pattern, content)
|
|
259
|
+
|
|
260
|
+
for class_name in classes:
|
|
261
|
+
# Check if class has a run method
|
|
262
|
+
run_pattern = rf'class\s+{class_name}.*?def\s+run\s*\([^)]*\):'
|
|
263
|
+
if not re.search(run_pattern, content, re.DOTALL):
|
|
264
|
+
# Check if it's an abstract base class
|
|
265
|
+
if 'Base' not in class_name and 'Abstract' not in class_name:
|
|
266
|
+
pytest.fail(
|
|
267
|
+
f"Tool class {class_name} in {filepath.name} missing run() method"
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def test_no_debug_prints_in_production(self):
|
|
271
|
+
"""Ensure no debug print statements in production code."""
|
|
272
|
+
package_root = Path(__file__).parent.parent / 'hanzo_mcp'
|
|
273
|
+
|
|
274
|
+
for filepath in get_python_files(package_root):
|
|
275
|
+
# Skip test files
|
|
276
|
+
if 'test' in str(filepath):
|
|
277
|
+
continue
|
|
278
|
+
|
|
279
|
+
content = filepath.read_text()
|
|
280
|
+
|
|
281
|
+
# Check for debug patterns
|
|
282
|
+
debug_patterns = [
|
|
283
|
+
(r'print\s*\([^)]*#\s*DEBUG', 'debug print statement'),
|
|
284
|
+
(r'print\s*\([^)]*#\s*TODO', 'TODO print statement'),
|
|
285
|
+
(r'print\s*\([^)]*#\s*REMOVE', 'REMOVE print statement'),
|
|
286
|
+
(r'console\.log', 'console.log statement'),
|
|
287
|
+
(r'debugger;?', 'debugger statement'),
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
for pattern, description in debug_patterns:
|
|
291
|
+
if re.search(pattern, content, re.IGNORECASE):
|
|
292
|
+
pytest.fail(
|
|
293
|
+
f"Found {description} in production file: {filepath}"
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
if __name__ == '__main__':
|
|
298
|
+
# Run tests directly
|
|
299
|
+
pytest.main([__file__, '-v'])
|