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:
admin
2026-01-28 00:27:56 +04:00
Unverified
parent 3b128ba3bd
commit b52318eeae
1724 changed files with 351216 additions and 0 deletions

View File

View 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

View 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

View 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