Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/tool-routing/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "tool-routing"
version = "0.1.0"
version = "0.2.0"
description = "Claude Code plugin for routing tool calls to better alternatives"
requires-python = ">=3.9"
dependencies = [
Expand Down
14 changes: 12 additions & 2 deletions plugins/tool-routing/src/tool_routing/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,25 @@ def discover_routes_from_manifests(plugins: list[dict]) -> list[Path]:


def discover_all_routes(project_path: str | None = None) -> list[Path]:
"""Discover all route files from enabled plugins.
"""Discover all route files from enabled plugins and project-local sources.

This is the main entry point for route discovery.

Args:
project_path: Current project path for filtering local-scoped plugins
and finding project-local routes

Returns:
List of paths to tool-routes.yaml files
"""
# Plugin routes
plugins = get_enabled_plugins(project_path)
return discover_routes_from_manifests(plugins)
routes = discover_routes_from_manifests(plugins)

# Project-local routes (.claude/tool-routes.yaml)
if project_path:
local_routes = Path(project_path) / ".claude" / "tool-routes.yaml"
if local_routes.exists():
routes.append(local_routes)

return sorted(routes)
87 changes: 87 additions & 0 deletions plugins/tool-routing/tests/test_discovery_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,90 @@ def test_discover_all_routes_combines_cli_and_manifests(tmp_path):

assert len(paths) == 1
assert "tool-routes.yaml" in str(paths[0])


def test_discover_all_routes_includes_project_local(tmp_path):
"""discover_all_routes includes project-local .claude/tool-routes.yaml."""
from tool_routing.discovery import discover_all_routes

# Create project with .claude/tool-routes.yaml
project_path = tmp_path / "my-project"
project_path.mkdir()
claude_dir = project_path / ".claude"
claude_dir.mkdir()
(claude_dir / "tool-routes.yaml").write_text("""
routes:
project-route:
tool: Bash
pattern: "project-specific"
message: "Use project tool instead"
""")

# No plugins enabled
mock_output = json.dumps([])

with patch("tool_routing.discovery.subprocess.run") as mock_run:
mock_run.return_value = MagicMock(
returncode=0,
stdout=mock_output,
stderr=""
)
paths = discover_all_routes(str(project_path))

assert len(paths) == 1
assert paths[0] == claude_dir / "tool-routes.yaml"


def test_discover_all_routes_combines_plugins_and_project_local(tmp_path):
"""discover_all_routes combines plugin routes and project-local routes."""
from tool_routing.discovery import discover_all_routes

# Create mock plugin
plugin_path = tmp_path / "test-plugin"
plugin_path.mkdir()
(plugin_path / ".claude-plugin").mkdir()
(plugin_path / ".claude-plugin" / "routes.json").write_text(json.dumps({
"routes": ["./hooks/tool-routes.yaml"]
}))
(plugin_path / "hooks").mkdir()
(plugin_path / "hooks" / "tool-routes.yaml").write_text("""
routes:
plugin-route:
tool: Bash
pattern: "from-plugin"
message: "Plugin message"
""")

# Create project with .claude/tool-routes.yaml
project_path = tmp_path / "my-project"
project_path.mkdir()
claude_dir = project_path / ".claude"
claude_dir.mkdir()
(claude_dir / "tool-routes.yaml").write_text("""
routes:
project-route:
tool: Bash
pattern: "from-project"
message: "Project message"
""")

mock_output = json.dumps([{
"id": "test-plugin@test",
"enabled": True,
"scope": "user",
"installPath": str(plugin_path)
}])

with patch("tool_routing.discovery.subprocess.run") as mock_run:
mock_run.return_value = MagicMock(
returncode=0,
stdout=mock_output,
stderr=""
)
paths = discover_all_routes(str(project_path))

assert len(paths) == 2
# Should have both plugin and project-local routes
path_strs = [str(p) for p in paths]
assert any("hooks/tool-routes.yaml" in p for p in path_strs)
assert any(".claude/tool-routes.yaml" in p for p in path_strs)
Loading