Navegador

fix: GraphStore create_node/create_edge Cypher compatibility with FalkorDB create_node now defaults missing name/file_path and coerces None to empty string. create_edge uses proper Cypher property pattern syntax ({k: $v}) instead of the invalid dot notation ({a.k = $v}). Tested by successfully ingesting navegador's own codebase: 138 files, 2661 functions, 612 classes, 16022 edges. Bumps to 0.7.3.

lmata 2026-03-24 01:46 trunk
Commit 04dabf9624f861f345446ecfaa101645c39911e1e4bc9a71843dc872f48c89e9
--- navegador/__init__.py
+++ navegador/__init__.py
@@ -1,10 +1,10 @@
11
"""
22
Navegador — AST + knowledge graph context engine for AI coding agents.
33
"""
44
5
-__version__ = "0.7.2"
5
+__version__ = "0.7.3"
66
__author__ = "CONFLICT LLC"
77
88
from navegador.sdk import Navegador
99
1010
__all__ = ["Navegador"]
1111
--- navegador/__init__.py
+++ navegador/__init__.py
@@ -1,10 +1,10 @@
1 """
2 Navegador — AST + knowledge graph context engine for AI coding agents.
3 """
4
5 __version__ = "0.7.2"
6 __author__ = "CONFLICT LLC"
7
8 from navegador.sdk import Navegador
9
10 __all__ = ["Navegador"]
11
--- navegador/__init__.py
+++ navegador/__init__.py
@@ -1,10 +1,10 @@
1 """
2 Navegador — AST + knowledge graph context engine for AI coding agents.
3 """
4
5 __version__ = "0.7.3"
6 __author__ = "CONFLICT LLC"
7
8 from navegador.sdk import Navegador
9
10 __all__ = ["Navegador"]
11
--- navegador/graph/store.py
+++ navegador/graph/store.py
@@ -72,11 +72,16 @@
7272
def query(self, cypher: str, params: dict[str, Any] | None = None) -> Any:
7373
"""Execute a raw Cypher query and return the result."""
7474
return self._graph.query(cypher, params or {})
7575
7676
def create_node(self, label: str, props: dict[str, Any]) -> None:
77
- """Upsert a node by (label, name, file_path)."""
77
+ """Upsert a node by (label, name[, file_path])."""
78
+ # Ensure merge key fields exist
79
+ props.setdefault("name", "")
80
+ props.setdefault("file_path", "")
81
+ # Filter out None values — FalkorDB rejects them as params
82
+ props = {k: ("" if v is None else v) for k, v in props.items()}
7883
prop_str = ", ".join(f"n.{k} = ${k}" for k in props)
7984
cypher = f"MERGE (n:{label} {{name: $name, file_path: $file_path}}) SET {prop_str}"
8085
self.query(cypher, props)
8186
8287
def create_edge(
@@ -87,12 +92,12 @@
8792
to_label: str,
8893
to_key: dict[str, Any],
8994
props: dict[str, Any] | None = None,
9095
) -> None:
9196
"""Create a directed edge between two nodes, merging if it already exists."""
92
- from_match = " AND ".join(f"a.{k} = $from_{k}" for k in from_key)
93
- to_match = " AND ".join(f"b.{k} = $to_{k}" for k in to_key)
97
+ from_match = ", ".join(f"{k}: $from_{k}" for k in from_key)
98
+ to_match = ", ".join(f"{k}: $to_{k}" for k in to_key)
9499
prop_set = ""
95100
if props:
96101
prop_set = " SET " + ", ".join(f"r.{k} = $p_{k}" for k in props)
97102
98103
cypher = (
99104
--- navegador/graph/store.py
+++ navegador/graph/store.py
@@ -72,11 +72,16 @@
72 def query(self, cypher: str, params: dict[str, Any] | None = None) -> Any:
73 """Execute a raw Cypher query and return the result."""
74 return self._graph.query(cypher, params or {})
75
76 def create_node(self, label: str, props: dict[str, Any]) -> None:
77 """Upsert a node by (label, name, file_path)."""
 
 
 
 
 
78 prop_str = ", ".join(f"n.{k} = ${k}" for k in props)
79 cypher = f"MERGE (n:{label} {{name: $name, file_path: $file_path}}) SET {prop_str}"
80 self.query(cypher, props)
81
82 def create_edge(
@@ -87,12 +92,12 @@
87 to_label: str,
88 to_key: dict[str, Any],
89 props: dict[str, Any] | None = None,
90 ) -> None:
91 """Create a directed edge between two nodes, merging if it already exists."""
92 from_match = " AND ".join(f"a.{k} = $from_{k}" for k in from_key)
93 to_match = " AND ".join(f"b.{k} = $to_{k}" for k in to_key)
94 prop_set = ""
95 if props:
96 prop_set = " SET " + ", ".join(f"r.{k} = $p_{k}" for k in props)
97
98 cypher = (
99
--- navegador/graph/store.py
+++ navegador/graph/store.py
@@ -72,11 +72,16 @@
72 def query(self, cypher: str, params: dict[str, Any] | None = None) -> Any:
73 """Execute a raw Cypher query and return the result."""
74 return self._graph.query(cypher, params or {})
75
76 def create_node(self, label: str, props: dict[str, Any]) -> None:
77 """Upsert a node by (label, name[, file_path])."""
78 # Ensure merge key fields exist
79 props.setdefault("name", "")
80 props.setdefault("file_path", "")
81 # Filter out None values — FalkorDB rejects them as params
82 props = {k: ("" if v is None else v) for k, v in props.items()}
83 prop_str = ", ".join(f"n.{k} = ${k}" for k in props)
84 cypher = f"MERGE (n:{label} {{name: $name, file_path: $file_path}}) SET {prop_str}"
85 self.query(cypher, props)
86
87 def create_edge(
@@ -87,12 +92,12 @@
92 to_label: str,
93 to_key: dict[str, Any],
94 props: dict[str, Any] | None = None,
95 ) -> None:
96 """Create a directed edge between two nodes, merging if it already exists."""
97 from_match = ", ".join(f"{k}: $from_{k}" for k in from_key)
98 to_match = ", ".join(f"{k}: $to_{k}" for k in to_key)
99 prop_set = ""
100 if props:
101 prop_set = " SET " + ", ".join(f"r.{k} = $p_{k}" for k in props)
102
103 cypher = (
104
--- navegador/ingestion/parser.py
+++ navegador/ingestion/parser.py
@@ -109,10 +109,11 @@
109109
self.store.create_node(
110110
NodeLabel.Repository,
111111
{
112112
"name": repo_path.name,
113113
"path": str(repo_path),
114
+ "file_path": "",
114115
},
115116
)
116117
117118
stats: dict[str, int] = {
118119
"files": 0,
119120
--- navegador/ingestion/parser.py
+++ navegador/ingestion/parser.py
@@ -109,10 +109,11 @@
109 self.store.create_node(
110 NodeLabel.Repository,
111 {
112 "name": repo_path.name,
113 "path": str(repo_path),
 
114 },
115 )
116
117 stats: dict[str, int] = {
118 "files": 0,
119
--- navegador/ingestion/parser.py
+++ navegador/ingestion/parser.py
@@ -109,10 +109,11 @@
109 self.store.create_node(
110 NodeLabel.Repository,
111 {
112 "name": repo_path.name,
113 "path": str(repo_path),
114 "file_path": "",
115 },
116 )
117
118 stats: dict[str, int] = {
119 "files": 0,
120
+1 -1
--- pyproject.toml
+++ pyproject.toml
@@ -2,11 +2,11 @@
22
requires = ["setuptools>=69.0", "wheel"]
33
build-backend = "setuptools.build_meta"
44
55
[project]
66
name = "navegador"
7
-version = "0.7.2"
7
+version = "0.7.3"
88
description = "AST + knowledge graph context engine for AI coding agents"
99
readme = "README.md"
1010
license = "MIT"
1111
requires-python = ">=3.12"
1212
authors = [
1313
--- pyproject.toml
+++ pyproject.toml
@@ -2,11 +2,11 @@
2 requires = ["setuptools>=69.0", "wheel"]
3 build-backend = "setuptools.build_meta"
4
5 [project]
6 name = "navegador"
7 version = "0.7.2"
8 description = "AST + knowledge graph context engine for AI coding agents"
9 readme = "README.md"
10 license = "MIT"
11 requires-python = ">=3.12"
12 authors = [
13
--- pyproject.toml
+++ pyproject.toml
@@ -2,11 +2,11 @@
2 requires = ["setuptools>=69.0", "wheel"]
3 build-backend = "setuptools.build_meta"
4
5 [project]
6 name = "navegador"
7 version = "0.7.3"
8 description = "AST + knowledge graph context engine for AI coding agents"
9 readme = "README.md"
10 license = "MIT"
11 requires-python = ">=3.12"
12 authors = [
13

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button