feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing - Add AUTO-TRIGGER-SUMMARY.md documentation - Add FINAL-INTEGRATION-SUMMARY.md documentation - Complete Prometheus integration (6 commands + 4 tools) - Complete Dexto integration (12 commands + 5 tools) - Enhanced Ralph with access to all agents - Fix /clawd command (removed disable-model-invocation) - Update hooks.json to v5 with intelligent routing - 291 total skills now available - All 21 commands with automatic routing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
0
prometheus/tests/graph/__init__.py
Normal file
0
prometheus/tests/graph/__init__.py
Normal file
105
prometheus/tests/graph/test_file_graph_builder.py
Normal file
105
prometheus/tests/graph/test_file_graph_builder.py
Normal file
@@ -0,0 +1,105 @@
|
||||
from prometheus.graph.file_graph_builder import FileGraphBuilder
|
||||
from prometheus.graph.graph_types import (
|
||||
ASTNode,
|
||||
KnowledgeGraphEdgeType,
|
||||
KnowledgeGraphNode,
|
||||
TextNode,
|
||||
)
|
||||
from tests.test_utils import test_project_paths
|
||||
|
||||
|
||||
def test_supports_file():
|
||||
file_graph_builder = FileGraphBuilder(0, 0, 0)
|
||||
|
||||
assert file_graph_builder.supports_file(test_project_paths.C_FILE)
|
||||
assert file_graph_builder.supports_file(test_project_paths.JAVA_FILE)
|
||||
assert file_graph_builder.supports_file(test_project_paths.MD_FILE)
|
||||
assert file_graph_builder.supports_file(test_project_paths.PYTHON_FILE)
|
||||
|
||||
assert file_graph_builder.supports_file(test_project_paths.DUMMY_FILE) is False
|
||||
|
||||
|
||||
def test_build_python_file_graph():
|
||||
file_graph_builder = FileGraphBuilder(1000, 1000, 100)
|
||||
|
||||
parent_kg_node = KnowledgeGraphNode(0, None)
|
||||
next_node_id, kg_nodes, kg_edges = file_graph_builder.build_file_graph(
|
||||
parent_kg_node, test_project_paths.PYTHON_FILE, 0
|
||||
)
|
||||
|
||||
assert next_node_id == 11
|
||||
assert len(kg_nodes) == 11
|
||||
assert len(kg_edges) == 11
|
||||
|
||||
# Test if some of the nodes exists
|
||||
argument_list_ast_node = ASTNode(
|
||||
type="argument_list", start_line=1, end_line=1, text='("Hello world!")'
|
||||
)
|
||||
string_ast_node = ASTNode(type="string", start_line=1, end_line=1, text='"Hello world!"')
|
||||
|
||||
found_argument_list_ast_node = False
|
||||
for kg_node in kg_nodes:
|
||||
if kg_node.node == argument_list_ast_node:
|
||||
found_argument_list_ast_node = True
|
||||
assert found_argument_list_ast_node
|
||||
|
||||
found_string_ast_node = False
|
||||
for kg_node in kg_nodes:
|
||||
if kg_node.node == string_ast_node:
|
||||
found_string_ast_node = True
|
||||
assert found_string_ast_node
|
||||
|
||||
# Test if some of the edges exists
|
||||
found_edge = False
|
||||
for kg_edge in kg_edges:
|
||||
if (
|
||||
kg_edge.source.node == argument_list_ast_node
|
||||
and kg_edge.target.node == string_ast_node
|
||||
and kg_edge.type == KnowledgeGraphEdgeType.parent_of
|
||||
):
|
||||
found_edge = True
|
||||
assert found_edge
|
||||
|
||||
|
||||
def test_build_text_file_graph():
|
||||
file_graph_builder = FileGraphBuilder(1000, 100, 10)
|
||||
|
||||
parent_kg_node = KnowledgeGraphNode(0, None)
|
||||
next_node_id, kg_nodes, kg_edges = file_graph_builder.build_file_graph(
|
||||
parent_kg_node, test_project_paths.MD_FILE, 0
|
||||
)
|
||||
|
||||
assert next_node_id == 2
|
||||
assert len(kg_nodes) == 2
|
||||
assert len(kg_edges) == 3
|
||||
|
||||
# Test if some of the nodes exists
|
||||
text_node_1 = TextNode(
|
||||
text="# A\n\nText under header A.\n\n## B\n\nText under header B.\n\n## C\n\nText under header C.\n\n### D",
|
||||
start_line=1,
|
||||
end_line=13,
|
||||
)
|
||||
text_node_2 = TextNode(text="### D\n\nText under header D.", start_line=13, end_line=15)
|
||||
|
||||
found_text_node_1 = False
|
||||
for kg_node in kg_nodes:
|
||||
if kg_node.node == text_node_1:
|
||||
found_text_node_1 = True
|
||||
assert found_text_node_1
|
||||
|
||||
found_text_node_2 = False
|
||||
for kg_node in kg_nodes:
|
||||
if kg_node.node == text_node_2:
|
||||
found_text_node_2 = True
|
||||
assert found_text_node_2
|
||||
|
||||
# Test if some of the edges exists
|
||||
found_edge = False
|
||||
for kg_edge in kg_edges:
|
||||
if (
|
||||
kg_edge.source.node == text_node_1
|
||||
and kg_edge.target.node == text_node_2
|
||||
and kg_edge.type == KnowledgeGraphEdgeType.next_chunk
|
||||
):
|
||||
found_edge = True
|
||||
assert found_edge
|
||||
272
prometheus/tests/graph/test_graph_types.py
Normal file
272
prometheus/tests/graph/test_graph_types.py
Normal file
@@ -0,0 +1,272 @@
|
||||
from prometheus.graph.graph_types import (
|
||||
ASTNode,
|
||||
FileNode,
|
||||
KnowledgeGraphEdge,
|
||||
KnowledgeGraphEdgeType,
|
||||
KnowledgeGraphNode,
|
||||
Neo4jASTNode,
|
||||
Neo4jFileNode,
|
||||
Neo4jTextNode,
|
||||
TextNode,
|
||||
)
|
||||
|
||||
|
||||
def test_to_neo4j_file_node():
|
||||
basename = "foo"
|
||||
relative_path = "foo/bar/baz.py"
|
||||
node_id = 1
|
||||
|
||||
file_node = FileNode(basename, relative_path)
|
||||
knowldege_graph_node = KnowledgeGraphNode(node_id, file_node)
|
||||
neo4j_file_node = knowldege_graph_node.to_neo4j_node()
|
||||
|
||||
assert isinstance(neo4j_file_node, dict)
|
||||
|
||||
assert "node_id" in neo4j_file_node
|
||||
assert "basename" in neo4j_file_node
|
||||
assert "relative_path" in neo4j_file_node
|
||||
|
||||
assert neo4j_file_node["node_id"] == node_id
|
||||
assert neo4j_file_node["basename"] == basename
|
||||
assert neo4j_file_node["relative_path"] == relative_path
|
||||
|
||||
|
||||
def test_to_neo4j_ast_node():
|
||||
type = "method_declaration"
|
||||
start_line = 1
|
||||
end_line = 5
|
||||
text = "print('Hello world')"
|
||||
node_id = 1
|
||||
|
||||
ast_node = ASTNode(type, start_line, end_line, text)
|
||||
knowldege_graph_node = KnowledgeGraphNode(node_id, ast_node)
|
||||
neo4j_ast_node = knowldege_graph_node.to_neo4j_node()
|
||||
|
||||
assert isinstance(neo4j_ast_node, dict)
|
||||
|
||||
assert "node_id" in neo4j_ast_node
|
||||
assert "type" in neo4j_ast_node
|
||||
assert "start_line" in neo4j_ast_node
|
||||
assert "end_line" in neo4j_ast_node
|
||||
assert "text" in neo4j_ast_node
|
||||
|
||||
assert neo4j_ast_node["node_id"] == node_id
|
||||
assert neo4j_ast_node["type"] == type
|
||||
assert neo4j_ast_node["start_line"] == start_line
|
||||
assert neo4j_ast_node["end_line"] == end_line
|
||||
assert neo4j_ast_node["text"] == text
|
||||
|
||||
|
||||
def test_to_neo4j_text_node():
|
||||
text = "Hello world"
|
||||
node_id = 1
|
||||
start_line = 1
|
||||
end_line = 1
|
||||
|
||||
text_node = TextNode(text, start_line, end_line)
|
||||
knowldege_graph_node = KnowledgeGraphNode(node_id, text_node)
|
||||
neo4j_text_node = knowldege_graph_node.to_neo4j_node()
|
||||
|
||||
assert isinstance(neo4j_text_node, dict)
|
||||
|
||||
assert "node_id" in neo4j_text_node
|
||||
assert "text" in neo4j_text_node
|
||||
assert "start_line" in neo4j_text_node
|
||||
assert "end_line" in neo4j_text_node
|
||||
|
||||
assert neo4j_text_node["node_id"] == node_id
|
||||
assert neo4j_text_node["text"] == text
|
||||
assert neo4j_text_node["start_line"] == start_line
|
||||
assert neo4j_text_node["end_line"] == end_line
|
||||
|
||||
|
||||
def test_to_neo4j_has_file_edge():
|
||||
source_basename = "source"
|
||||
source_relative_path = "foo/bar/source.py"
|
||||
source_node_id = 1
|
||||
target_basename = "target"
|
||||
target_relative_path = "foo/bar/target.py"
|
||||
target_node_id = 10
|
||||
|
||||
source_file_node = FileNode(source_basename, source_relative_path)
|
||||
source_knowledge_graph_node = KnowledgeGraphNode(source_node_id, source_file_node)
|
||||
target_file_node = FileNode(target_basename, target_relative_path)
|
||||
target_knowledge_graph_node = KnowledgeGraphNode(target_node_id, target_file_node)
|
||||
knowledge_graph_edge = KnowledgeGraphEdge(
|
||||
source_knowledge_graph_node,
|
||||
target_knowledge_graph_node,
|
||||
KnowledgeGraphEdgeType.has_file,
|
||||
)
|
||||
neo4j_has_file_edge = knowledge_graph_edge.to_neo4j_edge()
|
||||
|
||||
assert isinstance(neo4j_has_file_edge, dict)
|
||||
|
||||
assert "source" in neo4j_has_file_edge
|
||||
assert "target" in neo4j_has_file_edge
|
||||
|
||||
assert neo4j_has_file_edge["source"] == source_knowledge_graph_node.to_neo4j_node()
|
||||
assert neo4j_has_file_edge["target"] == target_knowledge_graph_node.to_neo4j_node()
|
||||
|
||||
|
||||
def test_to_neo4j_has_ast_edge():
|
||||
source_basename = "source"
|
||||
source_relative_path = "foo/bar/source.py"
|
||||
source_node_id = 1
|
||||
target_type = "return_statement"
|
||||
target_start_line = 7
|
||||
target_end_line = 9
|
||||
target_text = "return True"
|
||||
target_node_id = 10
|
||||
|
||||
source_file_node = FileNode(source_basename, source_relative_path)
|
||||
source_knowledge_graph_node = KnowledgeGraphNode(source_node_id, source_file_node)
|
||||
target_ast_node = ASTNode(target_type, target_start_line, target_end_line, target_text)
|
||||
target_knowledge_graph_node = KnowledgeGraphNode(target_node_id, target_ast_node)
|
||||
knowledge_graph_edge = KnowledgeGraphEdge(
|
||||
source_knowledge_graph_node,
|
||||
target_knowledge_graph_node,
|
||||
KnowledgeGraphEdgeType.has_ast,
|
||||
)
|
||||
neo4j_has_ast_edge = knowledge_graph_edge.to_neo4j_edge()
|
||||
|
||||
assert isinstance(neo4j_has_ast_edge, dict)
|
||||
|
||||
assert "source" in neo4j_has_ast_edge
|
||||
assert "target" in neo4j_has_ast_edge
|
||||
|
||||
assert neo4j_has_ast_edge["source"] == source_knowledge_graph_node.to_neo4j_node()
|
||||
assert neo4j_has_ast_edge["target"] == target_knowledge_graph_node.to_neo4j_node()
|
||||
|
||||
|
||||
def test_to_neo4j_parent_of_edge():
|
||||
source_type = "method_declaration"
|
||||
source_start_line = 1
|
||||
source_end_line = 5
|
||||
source_text = "print('Hello world')"
|
||||
source_node_id = 1
|
||||
target_type = "return_statement"
|
||||
target_start_line = 7
|
||||
target_end_line = 9
|
||||
target_text = "return True"
|
||||
target_node_id = 10
|
||||
|
||||
source_ast_node = ASTNode(source_type, source_start_line, source_end_line, source_text)
|
||||
source_knowledge_graph_node = KnowledgeGraphNode(source_node_id, source_ast_node)
|
||||
target_ast_node = ASTNode(target_type, target_start_line, target_end_line, target_text)
|
||||
target_knowledge_graph_node = KnowledgeGraphNode(target_node_id, target_ast_node)
|
||||
knowledge_graph_edge = KnowledgeGraphEdge(
|
||||
source_knowledge_graph_node,
|
||||
target_knowledge_graph_node,
|
||||
KnowledgeGraphEdgeType.parent_of,
|
||||
)
|
||||
neo4j_parent_of_edge = knowledge_graph_edge.to_neo4j_edge()
|
||||
|
||||
assert isinstance(neo4j_parent_of_edge, dict)
|
||||
|
||||
assert "source" in neo4j_parent_of_edge
|
||||
assert "target" in neo4j_parent_of_edge
|
||||
|
||||
assert neo4j_parent_of_edge["source"] == source_knowledge_graph_node.to_neo4j_node()
|
||||
assert neo4j_parent_of_edge["target"] == target_knowledge_graph_node.to_neo4j_node()
|
||||
|
||||
|
||||
def test_to_neo4j_has_text_edge():
|
||||
source_basename = "source"
|
||||
source_relative_path = "foo/bar/source.py"
|
||||
source_node_id = 1
|
||||
target_text = "Hello world"
|
||||
target_start_line = 1
|
||||
target_end_line = 1
|
||||
target_node_id = 10
|
||||
|
||||
source_file_node = FileNode(source_basename, source_relative_path)
|
||||
source_knowledge_graph_node = KnowledgeGraphNode(source_node_id, source_file_node)
|
||||
target_text_node = TextNode(target_text, target_start_line, target_end_line)
|
||||
target_knowledge_graph_node = KnowledgeGraphNode(target_node_id, target_text_node)
|
||||
knowledge_graph_edge = KnowledgeGraphEdge(
|
||||
source_knowledge_graph_node,
|
||||
target_knowledge_graph_node,
|
||||
KnowledgeGraphEdgeType.has_text,
|
||||
)
|
||||
neo4j_has_text_edge = knowledge_graph_edge.to_neo4j_edge()
|
||||
|
||||
assert isinstance(neo4j_has_text_edge, dict)
|
||||
|
||||
assert "source" in neo4j_has_text_edge
|
||||
assert "target" in neo4j_has_text_edge
|
||||
|
||||
assert neo4j_has_text_edge["source"] == source_knowledge_graph_node.to_neo4j_node()
|
||||
assert neo4j_has_text_edge["target"] == target_knowledge_graph_node.to_neo4j_node()
|
||||
|
||||
|
||||
def test_to_neo4j_next_chunk_edge():
|
||||
source_text = "Hello"
|
||||
start_line = 1
|
||||
end_line = 1
|
||||
source_node_id = 1
|
||||
target_text = "world"
|
||||
target_start_line = 1
|
||||
target_end_line = 1
|
||||
target_node_id = 10
|
||||
|
||||
source_text_node = TextNode(source_text, start_line, end_line)
|
||||
source_knowledge_graph_node = KnowledgeGraphNode(source_node_id, source_text_node)
|
||||
target_text_node = TextNode(target_text, target_start_line, target_end_line)
|
||||
target_knowledge_graph_node = KnowledgeGraphNode(target_node_id, target_text_node)
|
||||
knowledge_graph_edge = KnowledgeGraphEdge(
|
||||
source_knowledge_graph_node,
|
||||
target_knowledge_graph_node,
|
||||
KnowledgeGraphEdgeType.has_text,
|
||||
)
|
||||
neo4j_next_chunk_edge = knowledge_graph_edge.to_neo4j_edge()
|
||||
|
||||
assert isinstance(neo4j_next_chunk_edge, dict)
|
||||
|
||||
assert "source" in neo4j_next_chunk_edge
|
||||
assert "target" in neo4j_next_chunk_edge
|
||||
|
||||
assert neo4j_next_chunk_edge["source"] == source_knowledge_graph_node.to_neo4j_node()
|
||||
assert neo4j_next_chunk_edge["target"] == target_knowledge_graph_node.to_neo4j_node()
|
||||
|
||||
|
||||
def test_from_neo4j_file_node():
|
||||
node_id = 10
|
||||
basename = "foo.py"
|
||||
relative_path = "bar/baz/foo.py"
|
||||
neo4j_file_node = Neo4jFileNode(node_id=node_id, basename=basename, relative_path=relative_path)
|
||||
knowledge_graph_node = KnowledgeGraphNode.from_neo4j_file_node(neo4j_file_node)
|
||||
|
||||
expected_file_node = FileNode(basename, relative_path)
|
||||
expected_knowledge_graph_node = KnowledgeGraphNode(node_id, expected_file_node)
|
||||
assert knowledge_graph_node == expected_knowledge_graph_node
|
||||
|
||||
|
||||
def test_from_neo4j_ast_node():
|
||||
node_id = 15
|
||||
type = "string_literal"
|
||||
start_line = 5
|
||||
end_line = 6
|
||||
text = '"hello world"'
|
||||
neo4j_ast_node = Neo4jASTNode(
|
||||
node_id=node_id, type=type, start_line=start_line, end_line=end_line, text=text
|
||||
)
|
||||
knowledge_graph_node = KnowledgeGraphNode.from_neo4j_ast_node(neo4j_ast_node)
|
||||
|
||||
expected_ast_node = ASTNode(type, start_line, end_line, text)
|
||||
expected_knowledge_graph_node = KnowledgeGraphNode(node_id, expected_ast_node)
|
||||
assert knowledge_graph_node == expected_knowledge_graph_node
|
||||
|
||||
|
||||
def test_from_neo4j_text_node():
|
||||
node_id = 20
|
||||
text = "hello world"
|
||||
start_line = 1
|
||||
end_line = 1
|
||||
neo4j_text_node = Neo4jTextNode(
|
||||
node_id=node_id, text=text, start_line=start_line, end_line=end_line
|
||||
)
|
||||
knowledge_graph_node = KnowledgeGraphNode.from_neo4j_text_node(neo4j_text_node)
|
||||
|
||||
expected_text_node = TextNode(text, start_line, end_line)
|
||||
expected_knowledge_graph_node = KnowledgeGraphNode(node_id, expected_text_node)
|
||||
assert knowledge_graph_node == expected_knowledge_graph_node
|
||||
92
prometheus/tests/graph/test_knowledge_graph.py
Normal file
92
prometheus/tests/graph/test_knowledge_graph.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import pytest
|
||||
|
||||
from prometheus.app.services.neo4j_service import Neo4jService
|
||||
from prometheus.graph.knowledge_graph import KnowledgeGraph
|
||||
from prometheus.neo4j.knowledge_graph_handler import KnowledgeGraphHandler
|
||||
from tests.test_utils import test_project_paths
|
||||
from tests.test_utils.fixtures import (
|
||||
NEO4J_PASSWORD,
|
||||
NEO4J_USERNAME,
|
||||
neo4j_container_with_kg_fixture, # noqa: F401
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def mock_neo4j_service(neo4j_container_with_kg_fixture): # noqa: F811
|
||||
"""Fixture: provide a clean DatabaseService using the Postgres test container."""
|
||||
neo4j_container, kg = neo4j_container_with_kg_fixture
|
||||
service = Neo4jService(neo4j_container.get_connection_url(), NEO4J_USERNAME, NEO4J_PASSWORD)
|
||||
service.start()
|
||||
yield service, kg
|
||||
await service.close()
|
||||
|
||||
|
||||
async def test_build_graph():
|
||||
knowledge_graph = KnowledgeGraph(1, 1000, 100, 0)
|
||||
await knowledge_graph.build_graph(test_project_paths.TEST_PROJECT_PATH)
|
||||
|
||||
assert knowledge_graph._next_node_id == 15
|
||||
# 7 FileNode
|
||||
# 84 ASTnode
|
||||
# 2 TextNode
|
||||
assert len(knowledge_graph._knowledge_graph_nodes) == 15
|
||||
assert len(knowledge_graph._knowledge_graph_edges) == 14
|
||||
|
||||
assert len(knowledge_graph.get_file_nodes()) == 7
|
||||
assert len(knowledge_graph.get_ast_nodes()) == 7
|
||||
assert len(knowledge_graph.get_text_nodes()) == 1
|
||||
assert len(knowledge_graph.get_parent_of_edges()) == 4
|
||||
assert len(knowledge_graph.get_has_file_edges()) == 6
|
||||
assert len(knowledge_graph.get_has_ast_edges()) == 3
|
||||
assert len(knowledge_graph.get_has_text_edges()) == 1
|
||||
assert len(knowledge_graph.get_next_chunk_edges()) == 0
|
||||
|
||||
|
||||
async def test_get_file_tree():
|
||||
knowledge_graph = KnowledgeGraph(1000, 1000, 100, 0)
|
||||
await knowledge_graph.build_graph(test_project_paths.TEST_PROJECT_PATH)
|
||||
file_tree = knowledge_graph.get_file_tree()
|
||||
expected_file_tree = """\
|
||||
test_project
|
||||
├── bar
|
||||
| ├── test.java
|
||||
| └── test.py
|
||||
├── foo
|
||||
| └── test.md
|
||||
└── test.c"""
|
||||
assert file_tree == expected_file_tree
|
||||
|
||||
|
||||
async def test_get_file_tree_depth_one():
|
||||
knowledge_graph = KnowledgeGraph(1000, 1000, 100, 0)
|
||||
await knowledge_graph.build_graph(test_project_paths.TEST_PROJECT_PATH)
|
||||
file_tree = knowledge_graph.get_file_tree(max_depth=1)
|
||||
expected_file_tree = """\
|
||||
test_project
|
||||
├── bar
|
||||
├── foo
|
||||
└── test.c"""
|
||||
assert file_tree == expected_file_tree
|
||||
|
||||
|
||||
async def test_get_file_tree_depth_two_max_seven_lines():
|
||||
knowledge_graph = KnowledgeGraph(1000, 1000, 100, 0)
|
||||
await knowledge_graph.build_graph(test_project_paths.TEST_PROJECT_PATH)
|
||||
file_tree = knowledge_graph.get_file_tree(max_depth=2, max_lines=6)
|
||||
expected_file_tree = """\
|
||||
test_project
|
||||
├── bar
|
||||
| ├── test.java
|
||||
| └── test.py
|
||||
├── foo
|
||||
| └── test.md"""
|
||||
assert file_tree == expected_file_tree
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
async def test_from_neo4j(mock_neo4j_service):
|
||||
service, kg = mock_neo4j_service
|
||||
handler = KnowledgeGraphHandler(service.neo4j_driver, 100)
|
||||
read_kg = await handler.read_knowledge_graph(0, 1000, 100, 10)
|
||||
|
||||
assert read_kg == kg
|
||||
Reference in New Issue
Block a user