From 707e6507ed34b4e28bcd37b4aeadd2736fed74e0 Mon Sep 17 00:00:00 2001 From: Husam Alshehadat Date: Fri, 27 Jun 2025 10:14:26 -0700 Subject: [PATCH 1/4] feat: Add uvx support --- config.py | 2 +- providers/openrouter.py | 5 +++++ pyproject.toml | 30 ++++++++++++++++++++++++++- server.py | 46 ++++++++++++++++++++++++++++++++++------- 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/config.py b/config.py index f995bc3..75135b2 100644 --- a/config.py +++ b/config.py @@ -14,7 +14,7 @@ import os # These values are used in server responses and for tracking releases # IMPORTANT: This is the single source of truth for version and author info # Semantic versioning: MAJOR.MINOR.PATCH -__version__ = "5.7.3" +__version__ = "5.7.4" # Last update date in ISO format __updated__ = "2025-06-27" # Primary maintainer diff --git a/providers/openrouter.py b/providers/openrouter.py index 18d3d5e..b5e6ea7 100644 --- a/providers/openrouter.py +++ b/providers/openrouter.py @@ -178,6 +178,11 @@ class OpenRouterProvider(OpenAICompatibleProvider): # Resolve model alias to actual OpenRouter model name resolved_model = self._resolve_model_name(model_name) + # Always disable streaming for OpenRouter + # MCP doesn't use streaming, and this avoids issues with O3 model access + if "stream" not in kwargs: + kwargs["stream"] = False + # Call parent method with resolved model name return super().generate_content( prompt=prompt, diff --git a/pyproject.toml b/pyproject.toml index 303a47e..b3e715b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,31 @@ +[project] +name = "zen-mcp-server" +version = "0.1.0" +description = "AI-powered MCP server with multiple model providers" +requires-python = ">=3.9" +dependencies = [ + "mcp>=1.0.0", + "google-genai>=1.19.0", + "openai>=1.55.2", + "pydantic>=2.0.0", + "python-dotenv>=1.0.0", +] + +[tool.setuptools.packages.find] +include = ["tools*", "providers*", "systemprompts*", "utils*"] + +[tool.setuptools] +py-modules = ["server", "config"] + +[tool.setuptools.package-data] +"*" = ["conf/*.json"] + +[tool.setuptools.data-files] +"conf" = ["conf/custom_models.json"] + +[project.scripts] +zen-mcp-server = "server:run" + [tool.black] line-length = 120 target-version = ['py39', 'py310', 'py311', 'py312', 'py313'] @@ -57,4 +85,4 @@ ignore = [ [build-system] requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" diff --git a/server.py b/server.py index 32ec5b9..ee1e37b 100644 --- a/server.py +++ b/server.py @@ -28,13 +28,20 @@ from logging.handlers import RotatingFileHandler from pathlib import Path from typing import Any, Optional -from dotenv import load_dotenv - -# Load environment variables from .env file in the script's directory -# This ensures .env is loaded regardless of the current working directory -script_dir = Path(__file__).parent -env_file = script_dir / ".env" -load_dotenv(dotenv_path=env_file) +# Try to load environment variables from .env file if dotenv is available +# This is optional - environment variables can still be passed directly +try: + from dotenv import load_dotenv + + # Load environment variables from .env file in the script's directory + # This ensures .env is loaded regardless of the current working directory + script_dir = Path(__file__).parent + env_file = script_dir / ".env" + load_dotenv(dotenv_path=env_file) +except ImportError: + # dotenv not available - this is fine, environment variables can still be passed directly + # This commonly happens when running via uvx or in minimal environments + pass from mcp.server import Server # noqa: E402 from mcp.server.models import InitializationOptions # noqa: E402 @@ -362,6 +369,12 @@ def configure_providers(): Raises: ValueError: If no valid API keys are found or conflicting configurations detected """ + # Log environment variable status for debugging + logger.debug("Checking environment variables for API keys...") + api_keys_to_check = ["OPENAI_API_KEY", "OPENROUTER_API_KEY", "GEMINI_API_KEY", "XAI_API_KEY", "CUSTOM_API_URL"] + for key in api_keys_to_check: + value = os.getenv(key) + logger.debug(f" {key}: {'[PRESENT]' if value else '[MISSING]'}") from providers import ModelProviderRegistry from providers.base import ProviderType from providers.custom import CustomProvider @@ -386,10 +399,16 @@ def configure_providers(): # Check for OpenAI API key openai_key = os.getenv("OPENAI_API_KEY") + logger.debug(f"OpenAI key check: key={'[PRESENT]' if openai_key else '[MISSING]'}") if openai_key and openai_key != "your_openai_api_key_here": valid_providers.append("OpenAI (o3)") has_native_apis = True logger.info("OpenAI API key found - o3 model available") + else: + if not openai_key: + logger.debug("OpenAI API key not found in environment") + else: + logger.debug("OpenAI API key is placeholder value") # Check for X.AI API key xai_key = os.getenv("XAI_API_KEY") @@ -407,10 +426,16 @@ def configure_providers(): # Check for OpenRouter API key openrouter_key = os.getenv("OPENROUTER_API_KEY") + logger.debug(f"OpenRouter key check: key={'[PRESENT]' if openrouter_key else '[MISSING]'}") if openrouter_key and openrouter_key != "your_openrouter_api_key_here": valid_providers.append("OpenRouter") has_openrouter = True logger.info("OpenRouter API key found - Multiple models available via OpenRouter") + else: + if not openrouter_key: + logger.debug("OpenRouter API key not found in environment") + else: + logger.debug("OpenRouter API key is placeholder value") # Check for custom API endpoint (Ollama, vLLM, etc.) custom_url = os.getenv("CUSTOM_API_URL") @@ -1285,9 +1310,14 @@ async def main(): ) -if __name__ == "__main__": +def run(): + """Console script entry point for zen-mcp-server.""" try: asyncio.run(main()) except KeyboardInterrupt: # Handle graceful shutdown pass + + +if __name__ == "__main__": + run() From 36bba89325e5116cae9b4ea47021770022d1f593 Mon Sep 17 00:00:00 2001 From: Husam Alshehadat Date: Fri, 27 Jun 2025 10:35:16 -0700 Subject: [PATCH 2/4] style: Fix formatting after sync --- communication_simulator_test.py | 11 ++- server.py | 2 +- simulator_tests/base_test.py | 18 +++- tests/test_uvx_support.py | 151 ++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+), 6 deletions(-) create mode 100644 tests/test_uvx_support.py diff --git a/communication_simulator_test.py b/communication_simulator_test.py index e471b33..55b1a92 100644 --- a/communication_simulator_test.py +++ b/communication_simulator_test.py @@ -94,13 +94,14 @@ class CommunicationSimulator: self.quick_mode = quick_mode self.temp_dir = None self.server_process = None - self.python_path = self._get_python_path() # Configure logging first log_level = logging.DEBUG if verbose else logging.INFO logging.basicConfig(level=log_level, format="%(asctime)s - %(levelname)s - %(message)s") self.logger = logging.getLogger(__name__) + self.python_path = self._get_python_path() + # Import test registry from simulator_tests import TEST_REGISTRY @@ -133,8 +134,14 @@ class CommunicationSimulator: def _get_python_path(self) -> str: """Get the Python path for the virtual environment""" current_dir = os.getcwd() - venv_python = os.path.join(current_dir, "venv", "bin", "python") + # Try .venv first (modern convention) + venv_python = os.path.join(current_dir, ".venv", "bin", "python") + if os.path.exists(venv_python): + return venv_python + + # Try venv as fallback + venv_python = os.path.join(current_dir, "venv", "bin", "python") if os.path.exists(venv_python): return venv_python diff --git a/server.py b/server.py index ee1e37b..9e6fe31 100644 --- a/server.py +++ b/server.py @@ -32,7 +32,7 @@ from typing import Any, Optional # This is optional - environment variables can still be passed directly try: from dotenv import load_dotenv - + # Load environment variables from .env file in the script's directory # This ensures .env is loaded regardless of the current working directory script_dir = Path(__file__).parent diff --git a/simulator_tests/base_test.py b/simulator_tests/base_test.py index f6282e2..bbc0a75 100644 --- a/simulator_tests/base_test.py +++ b/simulator_tests/base_test.py @@ -21,21 +21,33 @@ class BaseSimulatorTest: self.verbose = verbose self.test_files = {} self.test_dir = None - self.python_path = self._get_python_path() - # Configure logging + # Configure logging first log_level = logging.DEBUG if verbose else logging.INFO logging.basicConfig(level=log_level, format="%(asctime)s - %(levelname)s - %(message)s") self.logger = logging.getLogger(self.__class__.__name__) + self.python_path = self._get_python_path() + def _get_python_path(self) -> str: """Get the Python path for the virtual environment""" current_dir = os.getcwd() - venv_python = os.path.join(current_dir, ".zen_venv", "bin", "python") + # Try .venv first (modern convention) + venv_python = os.path.join(current_dir, ".venv", "bin", "python") if os.path.exists(venv_python): return venv_python + # Try venv as fallback + venv_python = os.path.join(current_dir, "venv", "bin", "python") + if os.path.exists(venv_python): + return venv_python + + # Try .zen_venv as fallback + zen_venv_python = os.path.join(current_dir, ".zen_venv", "bin", "python") + if os.path.exists(zen_venv_python): + return zen_venv_python + # Fallback to system python if venv doesn't exist self.logger.warning("Virtual environment not found, using system python") return "python" diff --git a/tests/test_uvx_support.py b/tests/test_uvx_support.py new file mode 100644 index 0000000..419686b --- /dev/null +++ b/tests/test_uvx_support.py @@ -0,0 +1,151 @@ +""" +Test cases for uvx support and environment handling. +""" + +import os +import sys +from pathlib import Path +from unittest import mock + +import pytest + + +class TestUvxEnvironmentHandling: + """Test uvx-specific environment handling features.""" + + def test_dotenv_import_success(self): + """Test that dotenv is imported successfully when available.""" + # Mock successful dotenv import + with mock.patch.dict("sys.modules", {"dotenv": mock.MagicMock()}): + with mock.patch("dotenv.load_dotenv") as mock_load_dotenv: + # Re-import server module to trigger the import logic + if "server" in sys.modules: + del sys.modules["server"] + + import server # noqa: F401 + + # Should have called load_dotenv with the correct path + mock_load_dotenv.assert_called_once() + call_args = mock_load_dotenv.call_args + assert "dotenv_path" in call_args.kwargs + + def test_dotenv_import_failure_graceful_handling(self): + """Test that ImportError for dotenv is handled gracefully (uvx scenario).""" + # Mock only the dotenv import to fail + original_import = __builtins__["__import__"] + + def mock_import(name, *args, **kwargs): + if name == "dotenv": + raise ImportError("No module named 'dotenv'") + return original_import(name, *args, **kwargs) + + with mock.patch("builtins.__import__", side_effect=mock_import): + # This should not raise an exception when trying to import dotenv + try: + from dotenv import load_dotenv # noqa: F401 + + pytest.fail("Should have raised ImportError for dotenv") + except ImportError: + # Expected behavior - ImportError should be caught gracefully in server.py + pass + + def test_env_file_path_resolution(self): + """Test that .env file path is correctly resolved relative to server.py.""" + import server + + # Test that the server module correctly resolves .env path + script_dir = Path(server.__file__).parent + expected_env_file = script_dir / ".env" + + # The logic should create a path relative to server.py + assert expected_env_file.name == ".env" + assert expected_env_file.parent == script_dir + + def test_environment_variables_still_work_without_dotenv(self): + """Test that environment variables work even when dotenv is not available.""" + # Set a test environment variable + test_key = "TEST_ZEN_MCP_VAR" + test_value = "test_value_123" + + with mock.patch.dict(os.environ, {test_key: test_value}): + # Environment variable should still be accessible regardless of dotenv + assert os.getenv(test_key) == test_value + + def test_dotenv_graceful_fallback_behavior(self): + """Test the actual graceful fallback behavior in server module.""" + # Test that server module handles missing dotenv gracefully + # This is tested by the fact that the server can be imported even if dotenv fails + import server + + # If we can import server, the graceful handling works + assert hasattr(server, "run") + + # Test that environment variables still work + test_key = "TEST_FALLBACK_VAR" + test_value = "fallback_test_123" + + with mock.patch.dict(os.environ, {test_key: test_value}): + assert os.getenv(test_key) == test_value + + +class TestUvxProjectConfiguration: + """Test uvx-specific project configuration features.""" + + def test_pyproject_toml_has_required_uvx_fields(self): + """Test that pyproject.toml has all required fields for uvx support.""" + import tomllib + + pyproject_path = Path(__file__).parent.parent / "pyproject.toml" + assert pyproject_path.exists(), "pyproject.toml should exist" + + with open(pyproject_path, "rb") as f: + pyproject_data = tomllib.load(f) + + # Check required uvx fields + assert "project" in pyproject_data + project = pyproject_data["project"] + + # Essential fields for uvx + assert "name" in project + assert project["name"] == "zen-mcp-server" + assert "dependencies" in project + assert "requires-python" in project + + # Script entry point for uvx + assert "scripts" in project + assert "zen-mcp-server" in project["scripts"] + assert project["scripts"]["zen-mcp-server"] == "server:run" + + def test_pyproject_dependencies_match_requirements(self): + """Test that pyproject.toml dependencies align with requirements.txt.""" + import tomllib + + # Read pyproject.toml + pyproject_path = Path(__file__).parent.parent / "pyproject.toml" + with open(pyproject_path, "rb") as f: + pyproject_data = tomllib.load(f) + + pyproject_deps = set(pyproject_data["project"]["dependencies"]) + + # Read requirements.txt + requirements_path = Path(__file__).parent.parent / "requirements.txt" + if requirements_path.exists(): + # Note: We primarily validate pyproject.toml has core dependencies + # requirements.txt might have additional dev dependencies + + # Core dependencies should be present in both + core_packages = {"mcp", "openai", "google-genai", "pydantic", "python-dotenv"} + + for pkg in core_packages: + pyproject_has = any(pkg in dep for dep in pyproject_deps) + + assert pyproject_has, f"{pkg} should be in pyproject.toml dependencies" + # requirements.txt might have additional dev dependencies + + def test_uvx_entry_point_callable(self): + """Test that the uvx entry point (server:run) is callable.""" + import server + + # The entry point should reference a callable function + assert hasattr(server, "run"), "server module should have a 'run' function" + assert callable(server.run), "server.run should be callable" From e3e9e4eb55a9735b728c1c923e9aa57449ca4179 Mon Sep 17 00:00:00 2001 From: Husam Alshehadat Date: Fri, 27 Jun 2025 10:37:32 -0700 Subject: [PATCH 3/4] fix: Handle tomllib import for Python 3.10 compatibility in uvx tests --- tests/test_uvx_support.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/test_uvx_support.py b/tests/test_uvx_support.py index 419686b..8cb404d 100644 --- a/tests/test_uvx_support.py +++ b/tests/test_uvx_support.py @@ -93,7 +93,15 @@ class TestUvxProjectConfiguration: def test_pyproject_toml_has_required_uvx_fields(self): """Test that pyproject.toml has all required fields for uvx support.""" - import tomllib + try: + import tomllib + except ImportError: + # tomllib is only available in Python 3.11+ + # For older versions, use tomli or skip the test + try: + import tomli as tomllib + except ImportError: + pytest.skip("tomllib/tomli not available for TOML parsing") pyproject_path = Path(__file__).parent.parent / "pyproject.toml" assert pyproject_path.exists(), "pyproject.toml should exist" @@ -118,7 +126,14 @@ class TestUvxProjectConfiguration: def test_pyproject_dependencies_match_requirements(self): """Test that pyproject.toml dependencies align with requirements.txt.""" - import tomllib + try: + import tomllib + except ImportError: + # tomllib is only available in Python 3.11+ + try: + import tomli as tomllib + except ImportError: + pytest.skip("tomllib/tomli not available for TOML parsing") # Read pyproject.toml pyproject_path = Path(__file__).parent.parent / "pyproject.toml" From 5a4262ce89ed16095778231f05baf896175b469c Mon Sep 17 00:00:00 2001 From: Husam Alshehadat Date: Fri, 27 Jun 2025 11:09:44 -0700 Subject: [PATCH 4/4] docs: uvx installation guide --- README.md | 133 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 92 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index daadbb4..82bff9a 100644 --- a/README.md +++ b/README.md @@ -2,22 +2,22 @@ [zen_web.webm](https://github.com/user-attachments/assets/851e3911-7f06-47c0-a4ab-a2601236697c) -
+
🤖 Claude OR Gemini CLI + [Gemini / OpenAI / Grok / OpenRouter / DIAL / Ollama / Any Model] = Your Ultimate AI Development Team

-The ultimate development partners for your favorite Coding Agent ([Claude](https://www.anthropic.com/claude-code) OR [Gemini CLI](https://github.com/google-gemini/gemini-cli)) - a Model Context Protocol server that gives you access to multiple AI +The ultimate development partners for your favorite Coding Agent ([Claude](https://www.anthropic.com/claude-code) OR [Gemini CLI](https://github.com/google-gemini/gemini-cli)) - a Model Context Protocol server that gives you access to multiple AI models for enhanced code analysis, problem-solving, and collaborative development. **Features true AI orchestration with conversations that continue across workflows** - Give Claude a complex -_workflow_ and let it orchestrate between models automatically. Claude stays in control, performs the actual work, -but gets perspectives from the best AI for each subtask. With tools like [`planner`](#3-planner---interactive-step-by-step-planning) for -breaking down complex projects, [`analyze`](#8-analyze---smart-file-analysis) for understanding codebases, -[`codereview`](#5-codereview---professional-code-review) for audits, [`refactor`](#9-refactor---intelligent-code-refactoring) for -improving code structure, [`debug`](#7-debug---expert-debugging-assistant) for solving complex problems, and [`precommit`](#6-precommit---pre-commit-validation) for -validating changes, Claude can switch between different tools _and_ models mid-conversation, +_workflow_ and let it orchestrate between models automatically. Claude stays in control, performs the actual work, +but gets perspectives from the best AI for each subtask. With tools like [`planner`](#3-planner---interactive-step-by-step-planning) for +breaking down complex projects, [`analyze`](#8-analyze---smart-file-analysis) for understanding codebases, +[`codereview`](#5-codereview---professional-code-review) for audits, [`refactor`](#9-refactor---intelligent-code-refactoring) for +improving code structure, [`debug`](#7-debug---expert-debugging-assistant) for solving complex problems, and [`precommit`](#6-precommit---pre-commit-validation) for +validating changes, Claude can switch between different tools _and_ models mid-conversation, with context carrying forward seamlessly. **Example Workflow - Claude Code:** @@ -38,10 +38,10 @@ and review into consideration to aid with its final pre-commit review. **Think of it as Claude Code _for_ Claude Code.** This MCP isn't magic. It's just **super-glue**. -> **Remember:** Claude stays in full control — but **YOU** call the shots. -> Zen is designed to have Claude engage other models only when needed — and to follow through with meaningful back-and-forth. -> **You're** the one who crafts the powerful prompt that makes Claude bring in Gemini, Flash, O3 — or fly solo. -> You're the guide. The prompter. The puppeteer. +> **Remember:** Claude stays in full control — but **YOU** call the shots. +> Zen is designed to have Claude engage other models only when needed — and to follow through with meaningful back-and-forth. +> **You're** the one who crafts the powerful prompt that makes Claude bring in Gemini, Flash, O3 — or fly solo. +> You're the guide. The prompter. The puppeteer. > ### You are the AI - **Actually Intelligent**. Because these AI models [clearly aren't when they get chatty →](docs/ai_banter.md) @@ -103,11 +103,11 @@ Claude is brilliant, but sometimes you need: **This is an extremely powerful feature that cannot be highlighted enough**: > The most amazing side-effect of this _conversation continuation_ system is that even AFTER Claude's context resets or -> compacts, since the continuation info is kept within MCP's memory, you can ask it to _continue_ discussing -> the plan with `o3`, and it will suddenly revive Claude because O3 would know what was being talked about and +> compacts, since the continuation info is kept within MCP's memory, you can ask it to _continue_ discussing +> the plan with `o3`, and it will suddenly revive Claude because O3 would know what was being talked about and > relay this back in a way that re-ignites Claude's understanding. All this without wasting context on asking Claude to > ingest lengthy documents / code again and re-prompting it to communicate with another model. Zen manages that internally. The model's response -> revives Claude with better context around the discussion than an automatic summary ever can. +> revives Claude with better context around the discussion than an automatic summary ever can. **[📖 Read the complete technical deep-dive on how this revolutionary system works](docs/context-revival.md)** @@ -156,11 +156,62 @@ The final implementation resulted in a 26% improvement in JSON parsing performan - **Text Generation WebUI**: Popular local interface for running models - **Any OpenAI-compatible API**: Custom endpoints for your own infrastructure -> **Note:** Using multiple provider options may create ambiguity about which provider / model to use if there is an overlap. +> **Note:** Using multiple provider options may create ambiguity about which provider / model to use if there is an overlap. > If all APIs are configured, native APIs will take priority when there is a clash in model name, such as for `gemini` and `o3`. > Configure your model aliases and give them unique names in [`conf/custom_models.json`](conf/custom_models.json) -### 2. Clone and Set Up +### 2. Choose Your Installation Method + +**Option A: Quick Install with uvx** + +**Prerequisites**: Install [uv](https://docs.astral.sh/uv/getting-started/installation/) first (required for uvx) + +For **Claude Desktop**, add this to your `claude_desktop_config.json` +```json +{ + "mcpServers": { + "zen": { + "command": "uvx", + "args": [ + "--from", + "git+https://github.com/BeehiveInnovations/zen-mcp-server.git", + "zen-mcp-server" + ], + "env": { + "OPENAI_API_KEY": "your_api_key_here" + } + } + } +} +``` + +For **Claude Code CLI**, create a `.mcp.json` file in your project root for [project-scoped configuration](https://docs.anthropic.com/en/docs/claude-code/mcp#project-scope): +```json +{ + "mcpServers": { + "zen": { + "command": "uvx", + "args": [ + "--from", + "git+https://github.com/BeehiveInnovations/zen-mcp-server.git", + "zen-mcp-server" + ], + "env": { + "OPENAI_API_KEY": "your_api_key_here" + } + } + } +} +``` + +**What this does:** +- **Zero setup required** - uvx handles everything automatically +- **Always up-to-date** - Pulls latest version on each run +- **No local dependencies** - Works without Python environment setup +- **Instant availability** - Ready to use immediately + + +**Option B: Traditional Clone and Set Up** ```bash # Clone to your preferred location @@ -212,9 +263,9 @@ nano .env # Note: At least one API key OR custom URL is required ``` -**No restart needed**: The server reads the .env file each time Claude calls a tool, so changes take effect immediately. +**No restart needed**: The server reads the .env file each time Claude calls a tool, so changes take effect immediately. -**Next**: Now run `claude` from your project folder using the terminal for it to connect to the newly added mcp server. +**Next**: Now run `claude` from your project folder using the terminal for it to connect to the newly added mcp server. If you were already running a `claude` code session, please exit and start a new session. #### If Setting up for Claude Desktop @@ -240,11 +291,11 @@ Just ask Claude naturally: ## Available Tools -These aren't just tools—they're how you get Claude to think like a real developer. Instead of rushing to reply with -surface-level takes or shallow-insight, these workflows make Claude pause, dig into your code, and reason through -problems step by step. +These aren't just tools—they're how you get Claude to think like a real developer. Instead of rushing to reply with +surface-level takes or shallow-insight, these workflows make Claude pause, dig into your code, and reason through +problems step by step. -It's the difference between a rushed guess and a focused second pair of eyes that actually understands your code. Try them +It's the difference between a rushed guess and a focused second pair of eyes that actually understands your code. Try them and feel the difference. **Quick Tool Selection Guide:** @@ -306,26 +357,26 @@ Get a second opinion to augment Claude's own extended thinking. Uses specialized ``` The button won't animate when clicked, it seems something else is intercepting the clicks. Use thinkdeep with gemini pro after gathering related code and handing it the files -and find out what the root cause is +and find out what the root cause is ``` **[📖 Read More](docs/tools/thinkdeep.md)** - Enhanced analysis capabilities and critical evaluation process ### 3. `planner` - Interactive Step-by-Step Planning -Break down complex projects or ideas into manageable, structured plans through step-by-step thinking. -Perfect for adding new features to an existing system, scaling up system design, migration strategies, +Break down complex projects or ideas into manageable, structured plans through step-by-step thinking. +Perfect for adding new features to an existing system, scaling up system design, migration strategies, and architectural planning with branching and revision capabilities. #### Pro Tip -Claude supports `sub-tasks` where it will spawn and run separate background tasks. You can ask Claude to +Claude supports `sub-tasks` where it will spawn and run separate background tasks. You can ask Claude to run Zen's planner with two separate ideas. Then when it's done, use Zen's `consensus` tool to pass the entire plan and get expert perspective from two powerful AI models on which one to work on first! Like performing **AB** testing in one-go without the wait! ``` -Create two separate sub-tasks: in one, using planner tool show me how to add natural language support -to my cooking app. In the other sub-task, use planner to plan how to add support for voice notes to my cooking app. -Once done, start a consensus by sharing both plans to o3 and flash to give me the final verdict. Which one do +Create two separate sub-tasks: in one, using planner tool show me how to add natural language support +to my cooking app. In the other sub-task, use planner to plan how to add support for voice notes to my cooking app. +Once done, start a consensus by sharing both plans to o3 and flash to give me the final verdict. Which one do I implement first? ``` @@ -335,7 +386,7 @@ I implement first? Get diverse expert opinions from multiple AI models on technical proposals and decisions. Supports stance steering (for/against/neutral) and structured decision-making. ``` -Get a consensus with flash taking a supportive stance and gemini pro being critical to evaluate whether we should +Get a consensus with flash taking a supportive stance and gemini pro being critical to evaluate whether we should migrate from REST to GraphQL for our API. I need a definitive answer. ``` @@ -345,7 +396,7 @@ migrate from REST to GraphQL for our API. I need a definitive answer. Comprehensive code analysis with prioritized feedback and severity levels. This workflow tool guides Claude through systematic investigation steps with forced pauses between each step to ensure thorough code examination, issue identification, and quality assessment before providing expert analysis. ``` -Perform a codereview with gemini pro especially the auth.py as I feel some of the code is bypassing security checks +Perform a codereview with gemini pro especially the auth.py as I feel some of the code is bypassing security checks and there may be more potential vulnerabilities. Find and share related code." ``` @@ -368,7 +419,7 @@ Perform a thorough precommit with o3, we want to only highlight critical issues, I then ran: ```text -Run a precommit with o3 confirm our changes are sound and diffs are valid. Confirm this won't cause breakage or +Run a precommit with o3 confirm our changes are sound and diffs are valid. Confirm this won't cause breakage or regressions and codesmells are out ``` @@ -386,9 +437,9 @@ Output: ... ``` -The reported issue was in fact a _very subtle bug_ that slipped through the quick glance — and a unit test for this exact case apparently -was missing (out of 540 existing tests!) - explains the zero reported regressions. The fix was ultimately simple, but the -fact Claude (and by extension, I) overlooked this, was a stark reminder: no number of eyeballs is ever enough. Fixed the +The reported issue was in fact a _very subtle bug_ that slipped through the quick glance — and a unit test for this exact case apparently +was missing (out of 540 existing tests!) - explains the zero reported regressions. The fix was ultimately simple, but the +fact Claude (and by extension, I) overlooked this, was a stark reminder: no number of eyeballs is ever enough. Fixed the issue, ran `precommit` with o3 again and got: **RECOMMENDATION: PROCEED WITH COMMIT** @@ -401,10 +452,10 @@ Nice! This is just one instance - take a look at [another example here](docs/too Systematic investigation-guided debugging that walks Claude through step-by-step root cause analysis. This workflow tool enforces a structured investigation process where Claude performs methodical code examination, evidence collection, and hypothesis formation across multiple steps before receiving expert analysis from the selected AI model. When Claude's confidence reaches **100% certainty** during the investigative workflow, expert analysis via another model is skipped to save on tokens and cost, and Claude proceeds directly to fixing the issue. ``` -See logs under /Users/me/project/diagnostics.log and related code under the sync folder. -Logs show that sync works but sometimes it gets stuck and there are no errors displayed to -the user. Using zen's debug tool with gemini pro, find out why this is happening and what the root -cause is and its fix +See logs under /Users/me/project/diagnostics.log and related code under the sync folder. +Logs show that sync works but sometimes it gets stuck and there are no errors displayed to +the user. Using zen's debug tool with gemini pro, find out why this is happening and what the root +cause is and its fix ``` You can also add `do not use another model` to make Claude perform the entire workflow on its own. This is recommended @@ -467,7 +518,7 @@ Perform a secaudit with o3 on this e-commerce web application focusing on paymen Generates thorough documentation with complexity analysis and gotcha identification. This workflow tool guides Claude through systematic investigation of code structure, function complexity, and documentation needs across multiple steps before generating comprehensive documentation that includes algorithmic complexity, call flow information, and unexpected behaviors that developers should know about. ``` -# Includes complexity Big-O notiation, documents dependencies / code-flow, fixes existing stale docs +# Includes complexity Big-O notiation, documents dependencies / code-flow, fixes existing stale docs Use docgen to documentation the UserManager class # Includes complexity Big-O notiation, documents dependencies / code-flow