From f87c3edbdc85f87ad24c5690a3de0554417ea813 Mon Sep 17 00:00:00 2001 From: JeremyDev87 Date: Sat, 4 Apr 2026 02:21:03 +0900 Subject: [PATCH] fix(plugin): use CLAUDE_PROJECT_DIR in user-prompt-submit hook (#1236) Use CLAUDE_PROJECT_DIR env var instead of os.getcwd() for MCP detection and ModeEngine cwd, matching session-start.py behavior. When hook process cwd differs from the project dir, MCP detection now works correctly. --- .../hooks/test_user_prompt_submit.py | 72 +++++++++++++++++++ .../hooks/user-prompt-submit.py | 5 +- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/packages/claude-code-plugin/hooks/test_user_prompt_submit.py b/packages/claude-code-plugin/hooks/test_user_prompt_submit.py index 3492a4a6..c6698212 100644 --- a/packages/claude-code-plugin/hooks/test_user_prompt_submit.py +++ b/packages/claude-code-plugin/hooks/test_user_prompt_submit.py @@ -233,6 +233,78 @@ def test_no_output_without_keyword(self): assert result.returncode == 0 assert result.stdout == "" + def test_claude_project_dir_env_overrides_cwd(self): + """CLAUDE_PROJECT_DIR env set to dir with mcp.json → MCP output even when cwd differs (#1236).""" + import tempfile + import os as _os + + with tempfile.TemporaryDirectory() as project_dir, \ + tempfile.TemporaryDirectory() as cwd_dir: + # Set up MCP config in project_dir + claude_dir = Path(project_dir) / ".claude" + claude_dir.mkdir() + mcp_json = claude_dir / "mcp.json" + mcp_json.write_text(json.dumps({ + "mcpServers": { + "codingbuddy": {"command": "codingbuddy", "args": ["mcp"]} + } + })) + + hook_path = Path(__file__).parent / "user-prompt-submit.py" + input_data = json.dumps({"prompt": "PLAN: test"}) + env = _os.environ.copy() + env["HOME"] = cwd_dir # HOME has no mcp.json + env["CLAUDE_PROJECT_DIR"] = project_dir + env.pop("CODINGBUDDY_RULES_DIR", None) + + result = subprocess.run( + [sys.executable, str(hook_path)], + input=input_data, + capture_output=True, + text=True, + env=env, + cwd=cwd_dir, # cwd differs from project_dir + ) + + assert result.returncode == 0 + assert "# Mode: PLAN" in result.stdout + assert "mcp__codingbuddy__parse_mode" in result.stdout + + def test_claude_project_dir_with_project_mcp_json(self): + """CLAUDE_PROJECT_DIR with project-level .mcp.json → MCP detection (#1236).""" + import tempfile + import os as _os + + with tempfile.TemporaryDirectory() as project_dir, \ + tempfile.TemporaryDirectory() as cwd_dir: + # Set up project-level .mcp.json (not ~/.claude/mcp.json) + mcp_json = Path(project_dir) / ".mcp.json" + mcp_json.write_text(json.dumps({ + "mcpServers": { + "codingbuddy": {"command": "codingbuddy", "args": ["mcp"]} + } + })) + + hook_path = Path(__file__).parent / "user-prompt-submit.py" + input_data = json.dumps({"prompt": "ACT: test"}) + env = _os.environ.copy() + env["HOME"] = cwd_dir + env["CLAUDE_PROJECT_DIR"] = project_dir + env.pop("CODINGBUDDY_RULES_DIR", None) + + result = subprocess.run( + [sys.executable, str(hook_path)], + input=input_data, + capture_output=True, + text=True, + env=env, + cwd=cwd_dir, + ) + + assert result.returncode == 0 + assert "# Mode: ACT" in result.stdout + assert "mcp__codingbuddy__parse_mode" in result.stdout + if __name__ == "__main__": import pytest diff --git a/packages/claude-code-plugin/hooks/user-prompt-submit.py b/packages/claude-code-plugin/hooks/user-prompt-submit.py index 01cc2e96..948f7810 100644 --- a/packages/claude-code-plugin/hooks/user-prompt-submit.py +++ b/packages/claude-code-plugin/hooks/user-prompt-submit.py @@ -70,7 +70,8 @@ def main(): from runtime_mode import is_mcp_available from mode_engine import ModeEngine - if is_mcp_available(project_dir=os.getcwd()): + project_dir = os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd()) + if is_mcp_available(project_dir=project_dir): # MCP mode: minimal output, parse_mode handles the rest print(f"# Mode: {detected_mode}") print( @@ -79,7 +80,7 @@ def main(): ) else: # Standalone mode: full enriched instructions - engine = ModeEngine() + engine = ModeEngine(cwd=project_dir) instructions = engine.build_instructions(detected_mode) print(instructions) except Exception: