From c89dc9b0e526c470d659076c508b39aaa30e3e02 Mon Sep 17 00:00:00 2001 From: JeremyDev87 Date: Sat, 4 Apr 2026 00:53:40 +0900 Subject: [PATCH] fix(plugin): propagate project_dir to callers and fix HistoryDB default path (#1230) - Fix HistoryDB.__init__: use self._db_path instead of db_path (None) in sqlite3.connect() - Pass project_dir=cwd to is_mcp_available() in prompt_injection.py - Pass project_dir=os.getcwd() to is_mcp_available() in user-prompt-submit.py - Pass self._project_dir to detect_runtime_mode() in health_check.py - Extend check_mcp_connection() to check settings.json and project .mcp.json - Add tests for HistoryDB default constructor and health_check project .mcp.json Closes #1230 --- .../hooks/lib/health_check.py | 44 ++++++++++++------- .../hooks/lib/history_db.py | 2 +- .../hooks/lib/prompt_injection.py | 2 +- .../hooks/user-prompt-submit.py | 2 +- .../tests/test_health_check.py | 26 ++++++++++- .../tests/test_history_db.py | 18 ++++++++ 6 files changed, 74 insertions(+), 20 deletions(-) diff --git a/packages/claude-code-plugin/hooks/lib/health_check.py b/packages/claude-code-plugin/hooks/lib/health_check.py index 9eb576bc..18f55761 100644 --- a/packages/claude-code-plugin/hooks/lib/health_check.py +++ b/packages/claude-code-plugin/hooks/lib/health_check.py @@ -176,27 +176,41 @@ def check_events_dir(self) -> Dict[str, str]: # Check 8: MCP connection # ------------------------------------------------------------------ def check_mcp_connection(self) -> Dict[str, str]: - """Check if CodingBuddy MCP server is configured in mcp.json.""" - mcp_json = os.path.join(self._claude_dir, "mcp.json") - if not os.path.isfile(mcp_json): - return _result("mcp_connection", "WARN", "mcp.json not found — standalone mode") - try: - with open(mcp_json, "r", encoding="utf-8") as f: - data = json.load(f) - servers = data.get("mcpServers", {}) - for key in servers: - if "codingbuddy" in key.lower(): - return _result("mcp_connection", "PASS", "MCP configured (codingbuddy entry found)") - return _result("mcp_connection", "WARN", "MCP not configured — standalone mode active") - except (json.JSONDecodeError, OSError): - return _result("mcp_connection", "WARN", "mcp.json unreadable — standalone mode") + """Check if CodingBuddy MCP server is configured. + + Checks three locations in order: + 1. ~/.claude/mcp.json + 2. ~/.claude/settings.json → mcpServers + 3. {project_dir}/.mcp.json + """ + locations = [ + os.path.join(self._claude_dir, "mcp.json"), + os.path.join(self._claude_dir, "settings.json"), + os.path.join(self._project_dir, ".mcp.json"), + ] + for path in locations: + if not os.path.isfile(path): + continue + try: + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + servers = data.get("mcpServers", {}) + for key in servers: + if "codingbuddy" in key.lower(): + return _result( + "mcp_connection", "PASS", + f"MCP configured (codingbuddy entry found in {os.path.basename(path)})", + ) + except (json.JSONDecodeError, OSError): + continue + return _result("mcp_connection", "WARN", "MCP not configured — standalone mode active") # ------------------------------------------------------------------ # Check 9: runtime mode # ------------------------------------------------------------------ def check_runtime_mode(self) -> Dict[str, str]: """Detect current runtime mode (mcp or standalone).""" - mode = detect_runtime_mode(self._home_dir) + mode = detect_runtime_mode(self._home_dir, self._project_dir) return _result("runtime_mode", "PASS", f"Runtime: {mode}") # ------------------------------------------------------------------ diff --git a/packages/claude-code-plugin/hooks/lib/history_db.py b/packages/claude-code-plugin/hooks/lib/history_db.py index 7dd798c2..79509f88 100644 --- a/packages/claude-code-plugin/hooks/lib/history_db.py +++ b/packages/claude-code-plugin/hooks/lib/history_db.py @@ -36,7 +36,7 @@ def close_instance(cls) -> None: def __init__(self, db_path: str = None): self._db_path = db_path or _default_db_path() self._ensure_directory() - self._conn = sqlite3.connect(db_path, timeout=10) + self._conn = sqlite3.connect(self._db_path, timeout=10) self._conn.execute("PRAGMA journal_mode=WAL") self._set_file_permissions() self._create_tables() diff --git a/packages/claude-code-plugin/hooks/lib/prompt_injection.py b/packages/claude-code-plugin/hooks/lib/prompt_injection.py index 8f2a7090..b33c3798 100644 --- a/packages/claude-code-plugin/hooks/lib/prompt_injection.py +++ b/packages/claude-code-plugin/hooks/lib/prompt_injection.py @@ -110,7 +110,7 @@ def build_system_prompt(self, config: Dict[str, Any], cwd: str) -> str: if not isinstance(sections_cfg, dict): sections_cfg = {} - mcp_mode = is_mcp_available() + mcp_mode = is_mcp_available(project_dir=cwd) parts: list[str] = [] diff --git a/packages/claude-code-plugin/hooks/user-prompt-submit.py b/packages/claude-code-plugin/hooks/user-prompt-submit.py index 7a081869..01cc2e96 100644 --- a/packages/claude-code-plugin/hooks/user-prompt-submit.py +++ b/packages/claude-code-plugin/hooks/user-prompt-submit.py @@ -70,7 +70,7 @@ def main(): from runtime_mode import is_mcp_available from mode_engine import ModeEngine - if is_mcp_available(): + if is_mcp_available(project_dir=os.getcwd()): # MCP mode: minimal output, parse_mode handles the rest print(f"# Mode: {detected_mode}") print( diff --git a/packages/claude-code-plugin/tests/test_health_check.py b/packages/claude-code-plugin/tests/test_health_check.py index c5df9bee..54c6124c 100644 --- a/packages/claude-code-plugin/tests/test_health_check.py +++ b/packages/claude-code-plugin/tests/test_health_check.py @@ -245,11 +245,33 @@ def test_mcp_not_configured(self, env): assert result["status"] == "WARN" assert "standalone" in result["message"] - def test_mcp_json_missing(self, env): + def test_mcp_json_missing_falls_back_to_standalone(self, env): checker = _make_checker(env) result = checker.check_mcp_connection() assert result["status"] == "WARN" - assert "not found" in result["message"] + assert "standalone" in result["message"] + + def test_mcp_from_settings_json(self, env): + """settings.json with codingbuddy mcpServers → PASS.""" + settings_path = env / ".claude" / "settings.json" + settings = json.loads(settings_path.read_text()) + settings["mcpServers"] = {"codingbuddy": {"command": "npx"}} + settings_path.write_text(json.dumps(settings)) + checker = _make_checker(env) + result = checker.check_mcp_connection() + assert result["status"] == "PASS" + assert "settings.json" in result["message"] + + def test_mcp_from_project_mcp_json(self, env): + """project .mcp.json with codingbuddy → PASS.""" + project_mcp = env / ".mcp.json" + project_mcp.write_text(json.dumps({ + "mcpServers": {"codingbuddy": {"command": "npx"}} + })) + checker = _make_checker(env) + result = checker.check_mcp_connection() + assert result["status"] == "PASS" + assert ".mcp.json" in result["message"] class TestCheckRuntimeMode: diff --git a/packages/claude-code-plugin/tests/test_history_db.py b/packages/claude-code-plugin/tests/test_history_db.py index ce32243e..05432c0d 100644 --- a/packages/claude-code-plugin/tests/test_history_db.py +++ b/packages/claude-code-plugin/tests/test_history_db.py @@ -199,6 +199,24 @@ def writer2(): assert len(errors) == 0, f"Concurrent access errors: {errors}" +class TestDefaultConstructor: + """HistoryDB() with no args should use default path and not raise.""" + + def test_default_constructor_does_not_raise(self, db_dir, monkeypatch): + """HistoryDB() with no args must use self._db_path, not the original None.""" + default_path = os.path.join(db_dir, "default", "history.db") + monkeypatch.setattr( + "hooks.lib.history_db._default_db_path", lambda: default_path + ) + db = HistoryDB() + assert db._db_path == default_path + assert os.path.isfile(default_path) + db.start_session("default-test", project="/proj") + sessions = db.query_sessions(days=1) + assert len(sessions) == 1 + db.close() + + class TestSingleton: """Tests for HistoryDB singleton pattern (#931)."""