Navegador
docs: comprehensive documentation update for 0.7.0 Updates all existing pages (index, getting-started, guide, api, architecture) to reflect 13 languages, 11 MCP tools, SDK, enrichment, analysis, intelligence, and cluster features. New pages: SDK guide, framework enrichment, structural analysis, intelligence layer, cluster mode, CI/CD integration, SDK API reference, analysis API reference. Updated mkdocs.yml nav to include all new pages.
Commit
89816aa17fef324effc3dc58c03737a1547fc78cecc45aca51a32c7ddf0e598a
Parent
4966011eed927ba…
21 files changed
+393
+19
+94
-5
+152
-3
+486
+11
+80
-14
+65
-11
+59
-4
+70
-3
+287
+272
+280
+163
+51
+62
-9
+267
+70
-1
+254
+59
-2
+8
+
docs/api/analysis.md
~
docs/api/graph.md
~
docs/api/ingestion.md
~
docs/api/mcp.md
+
docs/api/sdk.md
~
docs/architecture/graph-schema.md
~
docs/architecture/overview.md
~
docs/getting-started/configuration.md
~
docs/getting-started/installation.md
~
docs/getting-started/quickstart.md
+
docs/guide/analysis.md
+
docs/guide/ci-cd.md
+
docs/guide/cluster.md
~
docs/guide/context-loading.md
+
docs/guide/framework-enrichment.md
~
docs/guide/ingestion.md
+
docs/guide/intelligence.md
~
docs/guide/mcp-integration.md
+
docs/guide/sdk.md
~
docs/index.md
~
mkdocs.yml
+393
| --- a/docs/api/analysis.md | ||
| +++ b/docs/api/analysis.md | ||
| @@ -0,0 +1,393 @@ | ||
| 1 | +# Analysis API Reference | |
| 2 | + | |
| 3 | +```python | |
| 4 | +from navegador.analysis import ( | |
| 5 | + ImpactAnalyzer, | |
| 6 | + FlowTracer, | |
| 7 | + DeadCodeDetector, | |
| 8 | + CycleDetector, | |
| 9 | + TestMapper, | |
| 10 | +) | |
| 11 | +from navegador.graph import GraphStore | |
| 12 | +``` | |
| 13 | + | |
| 14 | +--- | |
| 15 | + | |
| 16 | +## ImpactAnalyzer | |
| 17 | + | |
| 18 | +Traces downstream dependents of a function, class, or file by following `CALLS`, `INHERITS`, and `IMPORTS` edges. | |
| 19 | + | |
| 20 | +```python | |
| 21 | +class ImpactAnalyzer: | |
| 22 | + def __init__(self, store: GraphStore) -> None: ... | |
| 23 | +``` | |
| 24 | + | |
| 25 | +### `analyze` | |
| 26 | + | |
| 27 | +```python | |
| 28 | +def analyze( | |
| 29 | + self, | |
| 30 | + name: str, | |
| 31 | + *, | |
| 32 | + node_type: str = "", | |
| 33 | + file: str = "", | |
| 34 | + depth: int = 0, | |
| 35 | + include_tests: bool = False, | |
| 36 | + include_knowledge: bool = False, | |
| 37 | +) -> ImpactResult | |
| 38 | +``` | |
| 39 | + | |
| 40 | +Compute the impact set for a given node. | |
| 41 | + | |
| 42 | +**Parameters:** | |
| 43 | + | |
| 44 | +| Name | Type | Default | Description | | |
| 45 | +|---|---|---|---| | |
| 46 | +| `name` | `str` | — | Name of the function, class, or file to analyze | | |
| 47 | +| `node_type` | `str` | `""` | Node type hint: `"Function"`, `"Class"`, `"File"` | | |
| 48 | +| `file` | `str` | `""` | Optional file path to disambiguate | | |
| 49 | +| `depth` | `int` | `0` | Maximum hops to follow (0 = unlimited) | | |
| 50 | +| `include_tests` | `bool` | `False` | Include test files in the impact set | | |
| 51 | +| `include_knowledge` | `bool` | `False` | Include linked knowledge nodes (rules, concepts, decisions) | | |
| 52 | + | |
| 53 | +**Returns:** `ImpactResult` | |
| 54 | + | |
| 55 | +--- | |
| 56 | + | |
| 57 | +### ImpactResult | |
| 58 | + | |
| 59 | +```python | |
| 60 | +@dataclass | |
| 61 | +class ImpactResult: | |
| 62 | + root: ContextNode | |
| 63 | + direct_dependents: list[ContextNode] | |
| 64 | + transitive_dependents: list[ContextNode] | |
| 65 | + affected_files: list[str] | |
| 66 | + knowledge_nodes: list[ContextNode] # empty unless include_knowledge=True | |
| 67 | + depth_reached: int | |
| 68 | +``` | |
| 69 | + | |
| 70 | +| Field | Type | Description | | |
| 71 | +|---|---|---| | |
| 72 | +| `root` | `ContextNode` | The analyzed node | | |
| 73 | +| `direct_dependents` | `list[ContextNode]` | Nodes one hop away | | |
| 74 | +| `transitive_dependents` | `list[ContextNode]` | All nodes reachable beyond one hop | | |
| 75 | +| `affected_files` | `list[str]` | Unique file paths in the full dependent set | | |
| 76 | +| `knowledge_nodes` | `list[ContextNode]` | Linked concepts, rules, decisions | | |
| 77 | +| `depth_reached` | `int` | Actual maximum depth traversed | | |
| 78 | + | |
| 79 | +**Example:** | |
| 80 | + | |
| 81 | +```python | |
| 82 | +store = GraphStore.sqlite(".navegador/navegador.db") | |
| 83 | +analyzer = ImpactAnalyzer(store) | |
| 84 | + | |
| 85 | +result = analyzer.analyze("validate_token", depth=3, include_knowledge=True) | |
| 86 | +print(f"{len(result.direct_dependents)} direct dependents") | |
| 87 | +print(f"{len(result.transitive_dependents)} transitive dependents") | |
| 88 | +print(f"Affects {len(result.affected_files)} files") | |
| 89 | +for rule in [n for n in result.knowledge_nodes if n.label == "Rule"]: | |
| 90 | + print(f" Governed by: {rule.name} ({rule.properties.get('severity')})") | |
| 91 | +``` | |
| 92 | + | |
| 93 | +--- | |
| 94 | + | |
| 95 | +## FlowTracer | |
| 96 | + | |
| 97 | +Finds call paths between two functions. | |
| 98 | + | |
| 99 | +```python | |
| 100 | +class FlowTracer: | |
| 101 | + def __init__(self, store: GraphStore) -> None: ... | |
| 102 | +``` | |
| 103 | + | |
| 104 | +### `trace` | |
| 105 | + | |
| 106 | +```python | |
| 107 | +def trace( | |
| 108 | + self, | |
| 109 | + from_name: str, | |
| 110 | + to_name: str, | |
| 111 | + *, | |
| 112 | + from_file: str = "", | |
| 113 | + to_file: str = "", | |
| 114 | + max_paths: int = 3, | |
| 115 | + max_depth: int = 10, | |
| 116 | +) -> list[FlowPath] | |
| 117 | +``` | |
| 118 | + | |
| 119 | +Find call chains from `from_name` to `to_name`. | |
| 120 | + | |
| 121 | +**Parameters:** | |
| 122 | + | |
| 123 | +| Name | Type | Default | Description | | |
| 124 | +|---|---|---|---| | |
| 125 | +| `from_name` | `str` | — | Starting function name | | |
| 126 | +| `to_name` | `str` | — | Target function name | | |
| 127 | +| `from_file` | `str` | `""` | File path to disambiguate start | | |
| 128 | +| `to_file` | `str` | `""` | File path to disambiguate target | | |
| 129 | +| `max_paths` | `int` | `3` | Maximum number of paths to return | | |
| 130 | +| `max_depth` | `int` | `10` | Maximum call chain length | | |
| 131 | + | |
| 132 | +**Returns:** `list[FlowPath]` | |
| 133 | + | |
| 134 | +--- | |
| 135 | + | |
| 136 | +### FlowPath | |
| 137 | + | |
| 138 | +```python | |
| 139 | +@dataclass | |
| 140 | +class FlowPath: | |
| 141 | + nodes: list[str] # function names in order | |
| 142 | + node_details: list[ContextNode] | |
| 143 | + length: int | |
| 144 | +``` | |
| 145 | + | |
| 146 | +**Example:** | |
| 147 | + | |
| 148 | +```python | |
| 149 | +tracer = FlowTracer(store) | |
| 150 | +paths = tracer.trace("create_order", "process_payment", max_paths=5) | |
| 151 | +for i, path in enumerate(paths, 1): | |
| 152 | + print(f"Path {i}: {' -> '.join(path.nodes)}") | |
| 153 | +``` | |
| 154 | + | |
| 155 | +--- | |
| 156 | + | |
| 157 | +## DeadCodeDetector | |
| 158 | + | |
| 159 | +Identifies functions and classes that are never called, never imported, and not entry points. | |
| 160 | + | |
| 161 | +```python | |
| 162 | +class DeadCodeDetector: | |
| 163 | + def __init__(self, store: GraphStore) -> None: ... | |
| 164 | +``` | |
| 165 | + | |
| 166 | +### `find` | |
| 167 | + | |
| 168 | +```python | |
| 169 | +def find( | |
| 170 | + self, | |
| 171 | + path: str | Path, | |
| 172 | + *, | |
| 173 | + exclude_tests: bool = False, | |
| 174 | + min_confidence: int = 80, | |
| 175 | +) -> list[DeadCodeCandidate] | |
| 176 | +``` | |
| 177 | + | |
| 178 | +Find potentially dead code within a path. | |
| 179 | + | |
| 180 | +**Parameters:** | |
| 181 | + | |
| 182 | +| Name | Type | Default | Description | | |
| 183 | +|---|---|---|---| | |
| 184 | +| `path` | `str \| Path` | — | Directory or file to analyze | | |
| 185 | +| `exclude_tests` | `bool` | `False` | Skip test files | | |
| 186 | +| `min_confidence` | `int` | `80` | Minimum confidence score to include (0–100) | | |
| 187 | + | |
| 188 | +**Returns:** `list[DeadCodeCandidate]` | |
| 189 | + | |
| 190 | +--- | |
| 191 | + | |
| 192 | +### DeadCodeCandidate | |
| 193 | + | |
| 194 | +```python | |
| 195 | +@dataclass | |
| 196 | +class DeadCodeCandidate: | |
| 197 | + node: ContextNode | |
| 198 | + confidence: int # 0–100 | |
| 199 | + reasons: list[str] # e.g. ["no callers", "no imports", "no decorator entry point"] | |
| 200 | +``` | |
| 201 | + | |
| 202 | +| Field | Type | Description | | |
| 203 | +|---|---|---| | |
| 204 | +| `node` | `ContextNode` | The potentially dead node | | |
| 205 | +| `confidence` | `int` | Confidence that this is truly unreachable (higher = more confident) | | |
| 206 | +| `reasons` | `list[str]` | Reasons for the classification | | |
| 207 | + | |
| 208 | +**Example:** | |
| 209 | + | |
| 210 | +```python | |
| 211 | +detector = DeadCodeDetector(store) | |
| 212 | +candidates = detector.find("./src", exclude_tests=True, min_confidence=90) | |
| 213 | +for c in candidates: | |
| 214 | + print(f"[{c.confidence}%] {c.node.label}: {c.node.name} {c.node.properties['file']}") | |
| 215 | + print(f" Reasons: {', '.join(c.reasons)}") | |
| 216 | +``` | |
| 217 | + | |
| 218 | +--- | |
| 219 | + | |
| 220 | +## CycleDetector | |
| 221 | + | |
| 222 | +Finds circular dependency chains in call and import graphs. | |
| 223 | + | |
| 224 | +```python | |
| 225 | +class CycleDetector: | |
| 226 | + def __init__(self, store: GraphStore) -> None: ... | |
| 227 | +``` | |
| 228 | + | |
| 229 | +### `find_import_cycles` | |
| 230 | + | |
| 231 | +```python | |
| 232 | +def find_import_cycles( | |
| 233 | + self, | |
| 234 | + path: str | Path, | |
| 235 | + *, | |
| 236 | + min_length: int = 2, | |
| 237 | +) -> list[Cycle] | |
| 238 | +``` | |
| 239 | + | |
| 240 | +Find circular import chains within a path. | |
| 241 | + | |
| 242 | +--- | |
| 243 | + | |
| 244 | +### `find_call_cycles` | |
| 245 | + | |
| 246 | +```python | |
| 247 | +def find_call_cycles( | |
| 248 | + self, | |
| 249 | + path: str | Path, | |
| 250 | + *, | |
| 251 | + min_length: int = 2, | |
| 252 | +) -> list[Cycle] | |
| 253 | +``` | |
| 254 | + | |
| 255 | +Find circular call chains within a path. | |
| 256 | + | |
| 257 | +--- | |
| 258 | + | |
| 259 | +### `find_all` | |
| 260 | + | |
| 261 | +```python | |
| 262 | +def find_all( | |
| 263 | + self, | |
| 264 | + path: str | Path, | |
| 265 | + *, | |
| 266 | + min_length: int = 2, | |
| 267 | +) -> CycleReport | |
| 268 | +``` | |
| 269 | + | |
| 270 | +Find both import and call cycles. | |
| 271 | + | |
| 272 | +**Parameters (all methods):** | |
| 273 | + | |
| 274 | +| Name | Type | Default | Description | | |
| 275 | +|---|---|---|---| | |
| 276 | +| `path` | `str \| Path` | — | Directory or file to analyze | | |
| 277 | +| `min_length` | `int` | `2` | Minimum cycle length to report | | |
| 278 | + | |
| 279 | +**Returns:** `list[Cycle]` or `CycleReport` | |
| 280 | + | |
| 281 | +--- | |
| 282 | + | |
| 283 | +### Cycle | |
| 284 | + | |
| 285 | +```python | |
| 286 | +@dataclass | |
| 287 | +class Cycle: | |
| 288 | + path: list[str] # node names (files or functions) forming the cycle | |
| 289 | + cycle_type: str # "import" or "call" | |
| 290 | + length: int | |
| 291 | +``` | |
| 292 | + | |
| 293 | +### CycleReport | |
| 294 | + | |
| 295 | +```python | |
| 296 | +@dataclass | |
| 297 | +class CycleReport: | |
| 298 | + import_cycles: list[Cycle] | |
| 299 | + call_cycles: list[Cycle] | |
| 300 | + total: int | |
| 301 | +``` | |
| 302 | + | |
| 303 | +**Example:** | |
| 304 | + | |
| 305 | +```python | |
| 306 | +detector = CycleDetector(store) | |
| 307 | +report = detector.find_all("./src") | |
| 308 | +print(f"{report.total} cycles found") | |
| 309 | +for cycle in report.import_cycles: | |
| 310 | + print(f" Import cycle: {' -> '.join(cycle.path)}") | |
| 311 | +``` | |
| 312 | + | |
| 313 | +--- | |
| 314 | + | |
| 315 | +## TestMapper | |
| 316 | + | |
| 317 | +Maps test functions to the production code they exercise via call graph analysis. | |
| 318 | + | |
| 319 | +```python | |
| 320 | +class TestMapper: | |
| 321 | + def __init__(self, store: GraphStore) -> None: ... | |
| 322 | +``` | |
| 323 | + | |
| 324 | +### `map` | |
| 325 | + | |
| 326 | +```python | |
| 327 | +def map( | |
| 328 | + self, | |
| 329 | + src_path: str | Path, | |
| 330 | + test_path: str | Path, | |
| 331 | + *, | |
| 332 | + target: str = "", | |
| 333 | +) -> TestCoverageMap | |
| 334 | +``` | |
| 335 | + | |
| 336 | +Build a mapping of production functions to their covering tests. | |
| 337 | + | |
| 338 | +**Parameters:** | |
| 339 | + | |
| 340 | +| Name | Type | Default | Description | | |
| 341 | +|---|---|---|---| | |
| 342 | +| `src_path` | `str \| Path` | — | Production code directory | | |
| 343 | +| `test_path` | `str \| Path` | — | Test directory | | |
| 344 | +| `target` | `str` | `""` | If set, only map coverage for this specific function | | |
| 345 | + | |
| 346 | +**Returns:** `TestCoverageMap` | |
| 347 | + | |
| 348 | +--- | |
| 349 | + | |
| 350 | +### `uncovered` | |
| 351 | + | |
| 352 | +```python | |
| 353 | +def uncovered( | |
| 354 | + self, | |
| 355 | + src_path: str | Path, | |
| 356 | + test_path: str | Path, | |
| 357 | +) -> list[ContextNode] | |
| 358 | +``` | |
| 359 | + | |
| 360 | +Return production functions and classes with no covering tests. | |
| 361 | + | |
| 362 | +--- | |
| 363 | + | |
| 364 | +### TestCoverageMap | |
| 365 | + | |
| 366 | +```python | |
| 367 | +@dataclass | |
| 368 | +class TestCoverageMap: | |
| 369 | + coverage: dict[str, list[ContextNode]] # function name -> list of test nodes | |
| 370 | + uncovered: list[ContextNode] | |
| 371 | + coverage_percent: float | |
| 372 | +``` | |
| 373 | + | |
| 374 | +| Field | Type | Description | | |
| 375 | +|---|---|---| | |
| 376 | +| `coverage` | `dict[str, list[ContextNode]]` | Maps each production function to its test nodes | | |
| 377 | +| `uncovered` | `list[ContextNode]` | Production functions with no tests | | |
| 378 | +| `coverage_percent` | `float` | Percentage of functions with at least one test | | |
| 379 | + | |
| 380 | +**Example:** | |
| 381 | + | |
| 382 | +```python | |
| 383 | +mapper = TestMapper(store) | |
| 384 | +coverage_map = mapper.map("./src", "./tests") | |
| 385 | + | |
| 386 | +print(f"Coverage: {coverage_map.coverage_percent:.1f}%") | |
| 387 | +print(f"Uncovered: {len(coverage_map.uncovered)} functions") | |
| 388 | + | |
| 389 | +for fn_name, tests in coverage_map.coverage.items(): | |
| 390 | + print(f" {fn_name}: {len(tests)} tests") | |
| 391 | + for test in tests: | |
| 392 | + print(f" {test.properties['file']}::{test.name}") | |
| 393 | +``` |
| --- a/docs/api/analysis.md | |
| +++ b/docs/api/analysis.md | |
| @@ -0,0 +1,393 @@ | |
| --- a/docs/api/analysis.md | |
| +++ b/docs/api/analysis.md | |
| @@ -0,0 +1,393 @@ | |
| 1 | # Analysis API Reference |
| 2 | |
| 3 | ```python |
| 4 | from navegador.analysis import ( |
| 5 | ImpactAnalyzer, |
| 6 | FlowTracer, |
| 7 | DeadCodeDetector, |
| 8 | CycleDetector, |
| 9 | TestMapper, |
| 10 | ) |
| 11 | from navegador.graph import GraphStore |
| 12 | ``` |
| 13 | |
| 14 | --- |
| 15 | |
| 16 | ## ImpactAnalyzer |
| 17 | |
| 18 | Traces downstream dependents of a function, class, or file by following `CALLS`, `INHERITS`, and `IMPORTS` edges. |
| 19 | |
| 20 | ```python |
| 21 | class ImpactAnalyzer: |
| 22 | def __init__(self, store: GraphStore) -> None: ... |
| 23 | ``` |
| 24 | |
| 25 | ### `analyze` |
| 26 | |
| 27 | ```python |
| 28 | def analyze( |
| 29 | self, |
| 30 | name: str, |
| 31 | *, |
| 32 | node_type: str = "", |
| 33 | file: str = "", |
| 34 | depth: int = 0, |
| 35 | include_tests: bool = False, |
| 36 | include_knowledge: bool = False, |
| 37 | ) -> ImpactResult |
| 38 | ``` |
| 39 | |
| 40 | Compute the impact set for a given node. |
| 41 | |
| 42 | **Parameters:** |
| 43 | |
| 44 | | Name | Type | Default | Description | |
| 45 | |---|---|---|---| |
| 46 | | `name` | `str` | — | Name of the function, class, or file to analyze | |
| 47 | | `node_type` | `str` | `""` | Node type hint: `"Function"`, `"Class"`, `"File"` | |
| 48 | | `file` | `str` | `""` | Optional file path to disambiguate | |
| 49 | | `depth` | `int` | `0` | Maximum hops to follow (0 = unlimited) | |
| 50 | | `include_tests` | `bool` | `False` | Include test files in the impact set | |
| 51 | | `include_knowledge` | `bool` | `False` | Include linked knowledge nodes (rules, concepts, decisions) | |
| 52 | |
| 53 | **Returns:** `ImpactResult` |
| 54 | |
| 55 | --- |
| 56 | |
| 57 | ### ImpactResult |
| 58 | |
| 59 | ```python |
| 60 | @dataclass |
| 61 | class ImpactResult: |
| 62 | root: ContextNode |
| 63 | direct_dependents: list[ContextNode] |
| 64 | transitive_dependents: list[ContextNode] |
| 65 | affected_files: list[str] |
| 66 | knowledge_nodes: list[ContextNode] # empty unless include_knowledge=True |
| 67 | depth_reached: int |
| 68 | ``` |
| 69 | |
| 70 | | Field | Type | Description | |
| 71 | |---|---|---| |
| 72 | | `root` | `ContextNode` | The analyzed node | |
| 73 | | `direct_dependents` | `list[ContextNode]` | Nodes one hop away | |
| 74 | | `transitive_dependents` | `list[ContextNode]` | All nodes reachable beyond one hop | |
| 75 | | `affected_files` | `list[str]` | Unique file paths in the full dependent set | |
| 76 | | `knowledge_nodes` | `list[ContextNode]` | Linked concepts, rules, decisions | |
| 77 | | `depth_reached` | `int` | Actual maximum depth traversed | |
| 78 | |
| 79 | **Example:** |
| 80 | |
| 81 | ```python |
| 82 | store = GraphStore.sqlite(".navegador/navegador.db") |
| 83 | analyzer = ImpactAnalyzer(store) |
| 84 | |
| 85 | result = analyzer.analyze("validate_token", depth=3, include_knowledge=True) |
| 86 | print(f"{len(result.direct_dependents)} direct dependents") |
| 87 | print(f"{len(result.transitive_dependents)} transitive dependents") |
| 88 | print(f"Affects {len(result.affected_files)} files") |
| 89 | for rule in [n for n in result.knowledge_nodes if n.label == "Rule"]: |
| 90 | print(f" Governed by: {rule.name} ({rule.properties.get('severity')})") |
| 91 | ``` |
| 92 | |
| 93 | --- |
| 94 | |
| 95 | ## FlowTracer |
| 96 | |
| 97 | Finds call paths between two functions. |
| 98 | |
| 99 | ```python |
| 100 | class FlowTracer: |
| 101 | def __init__(self, store: GraphStore) -> None: ... |
| 102 | ``` |
| 103 | |
| 104 | ### `trace` |
| 105 | |
| 106 | ```python |
| 107 | def trace( |
| 108 | self, |
| 109 | from_name: str, |
| 110 | to_name: str, |
| 111 | *, |
| 112 | from_file: str = "", |
| 113 | to_file: str = "", |
| 114 | max_paths: int = 3, |
| 115 | max_depth: int = 10, |
| 116 | ) -> list[FlowPath] |
| 117 | ``` |
| 118 | |
| 119 | Find call chains from `from_name` to `to_name`. |
| 120 | |
| 121 | **Parameters:** |
| 122 | |
| 123 | | Name | Type | Default | Description | |
| 124 | |---|---|---|---| |
| 125 | | `from_name` | `str` | — | Starting function name | |
| 126 | | `to_name` | `str` | — | Target function name | |
| 127 | | `from_file` | `str` | `""` | File path to disambiguate start | |
| 128 | | `to_file` | `str` | `""` | File path to disambiguate target | |
| 129 | | `max_paths` | `int` | `3` | Maximum number of paths to return | |
| 130 | | `max_depth` | `int` | `10` | Maximum call chain length | |
| 131 | |
| 132 | **Returns:** `list[FlowPath]` |
| 133 | |
| 134 | --- |
| 135 | |
| 136 | ### FlowPath |
| 137 | |
| 138 | ```python |
| 139 | @dataclass |
| 140 | class FlowPath: |
| 141 | nodes: list[str] # function names in order |
| 142 | node_details: list[ContextNode] |
| 143 | length: int |
| 144 | ``` |
| 145 | |
| 146 | **Example:** |
| 147 | |
| 148 | ```python |
| 149 | tracer = FlowTracer(store) |
| 150 | paths = tracer.trace("create_order", "process_payment", max_paths=5) |
| 151 | for i, path in enumerate(paths, 1): |
| 152 | print(f"Path {i}: {' -> '.join(path.nodes)}") |
| 153 | ``` |
| 154 | |
| 155 | --- |
| 156 | |
| 157 | ## DeadCodeDetector |
| 158 | |
| 159 | Identifies functions and classes that are never called, never imported, and not entry points. |
| 160 | |
| 161 | ```python |
| 162 | class DeadCodeDetector: |
| 163 | def __init__(self, store: GraphStore) -> None: ... |
| 164 | ``` |
| 165 | |
| 166 | ### `find` |
| 167 | |
| 168 | ```python |
| 169 | def find( |
| 170 | self, |
| 171 | path: str | Path, |
| 172 | *, |
| 173 | exclude_tests: bool = False, |
| 174 | min_confidence: int = 80, |
| 175 | ) -> list[DeadCodeCandidate] |
| 176 | ``` |
| 177 | |
| 178 | Find potentially dead code within a path. |
| 179 | |
| 180 | **Parameters:** |
| 181 | |
| 182 | | Name | Type | Default | Description | |
| 183 | |---|---|---|---| |
| 184 | | `path` | `str \| Path` | — | Directory or file to analyze | |
| 185 | | `exclude_tests` | `bool` | `False` | Skip test files | |
| 186 | | `min_confidence` | `int` | `80` | Minimum confidence score to include (0–100) | |
| 187 | |
| 188 | **Returns:** `list[DeadCodeCandidate]` |
| 189 | |
| 190 | --- |
| 191 | |
| 192 | ### DeadCodeCandidate |
| 193 | |
| 194 | ```python |
| 195 | @dataclass |
| 196 | class DeadCodeCandidate: |
| 197 | node: ContextNode |
| 198 | confidence: int # 0–100 |
| 199 | reasons: list[str] # e.g. ["no callers", "no imports", "no decorator entry point"] |
| 200 | ``` |
| 201 | |
| 202 | | Field | Type | Description | |
| 203 | |---|---|---| |
| 204 | | `node` | `ContextNode` | The potentially dead node | |
| 205 | | `confidence` | `int` | Confidence that this is truly unreachable (higher = more confident) | |
| 206 | | `reasons` | `list[str]` | Reasons for the classification | |
| 207 | |
| 208 | **Example:** |
| 209 | |
| 210 | ```python |
| 211 | detector = DeadCodeDetector(store) |
| 212 | candidates = detector.find("./src", exclude_tests=True, min_confidence=90) |
| 213 | for c in candidates: |
| 214 | print(f"[{c.confidence}%] {c.node.label}: {c.node.name} {c.node.properties['file']}") |
| 215 | print(f" Reasons: {', '.join(c.reasons)}") |
| 216 | ``` |
| 217 | |
| 218 | --- |
| 219 | |
| 220 | ## CycleDetector |
| 221 | |
| 222 | Finds circular dependency chains in call and import graphs. |
| 223 | |
| 224 | ```python |
| 225 | class CycleDetector: |
| 226 | def __init__(self, store: GraphStore) -> None: ... |
| 227 | ``` |
| 228 | |
| 229 | ### `find_import_cycles` |
| 230 | |
| 231 | ```python |
| 232 | def find_import_cycles( |
| 233 | self, |
| 234 | path: str | Path, |
| 235 | *, |
| 236 | min_length: int = 2, |
| 237 | ) -> list[Cycle] |
| 238 | ``` |
| 239 | |
| 240 | Find circular import chains within a path. |
| 241 | |
| 242 | --- |
| 243 | |
| 244 | ### `find_call_cycles` |
| 245 | |
| 246 | ```python |
| 247 | def find_call_cycles( |
| 248 | self, |
| 249 | path: str | Path, |
| 250 | *, |
| 251 | min_length: int = 2, |
| 252 | ) -> list[Cycle] |
| 253 | ``` |
| 254 | |
| 255 | Find circular call chains within a path. |
| 256 | |
| 257 | --- |
| 258 | |
| 259 | ### `find_all` |
| 260 | |
| 261 | ```python |
| 262 | def find_all( |
| 263 | self, |
| 264 | path: str | Path, |
| 265 | *, |
| 266 | min_length: int = 2, |
| 267 | ) -> CycleReport |
| 268 | ``` |
| 269 | |
| 270 | Find both import and call cycles. |
| 271 | |
| 272 | **Parameters (all methods):** |
| 273 | |
| 274 | | Name | Type | Default | Description | |
| 275 | |---|---|---|---| |
| 276 | | `path` | `str \| Path` | — | Directory or file to analyze | |
| 277 | | `min_length` | `int` | `2` | Minimum cycle length to report | |
| 278 | |
| 279 | **Returns:** `list[Cycle]` or `CycleReport` |
| 280 | |
| 281 | --- |
| 282 | |
| 283 | ### Cycle |
| 284 | |
| 285 | ```python |
| 286 | @dataclass |
| 287 | class Cycle: |
| 288 | path: list[str] # node names (files or functions) forming the cycle |
| 289 | cycle_type: str # "import" or "call" |
| 290 | length: int |
| 291 | ``` |
| 292 | |
| 293 | ### CycleReport |
| 294 | |
| 295 | ```python |
| 296 | @dataclass |
| 297 | class CycleReport: |
| 298 | import_cycles: list[Cycle] |
| 299 | call_cycles: list[Cycle] |
| 300 | total: int |
| 301 | ``` |
| 302 | |
| 303 | **Example:** |
| 304 | |
| 305 | ```python |
| 306 | detector = CycleDetector(store) |
| 307 | report = detector.find_all("./src") |
| 308 | print(f"{report.total} cycles found") |
| 309 | for cycle in report.import_cycles: |
| 310 | print(f" Import cycle: {' -> '.join(cycle.path)}") |
| 311 | ``` |
| 312 | |
| 313 | --- |
| 314 | |
| 315 | ## TestMapper |
| 316 | |
| 317 | Maps test functions to the production code they exercise via call graph analysis. |
| 318 | |
| 319 | ```python |
| 320 | class TestMapper: |
| 321 | def __init__(self, store: GraphStore) -> None: ... |
| 322 | ``` |
| 323 | |
| 324 | ### `map` |
| 325 | |
| 326 | ```python |
| 327 | def map( |
| 328 | self, |
| 329 | src_path: str | Path, |
| 330 | test_path: str | Path, |
| 331 | *, |
| 332 | target: str = "", |
| 333 | ) -> TestCoverageMap |
| 334 | ``` |
| 335 | |
| 336 | Build a mapping of production functions to their covering tests. |
| 337 | |
| 338 | **Parameters:** |
| 339 | |
| 340 | | Name | Type | Default | Description | |
| 341 | |---|---|---|---| |
| 342 | | `src_path` | `str \| Path` | — | Production code directory | |
| 343 | | `test_path` | `str \| Path` | — | Test directory | |
| 344 | | `target` | `str` | `""` | If set, only map coverage for this specific function | |
| 345 | |
| 346 | **Returns:** `TestCoverageMap` |
| 347 | |
| 348 | --- |
| 349 | |
| 350 | ### `uncovered` |
| 351 | |
| 352 | ```python |
| 353 | def uncovered( |
| 354 | self, |
| 355 | src_path: str | Path, |
| 356 | test_path: str | Path, |
| 357 | ) -> list[ContextNode] |
| 358 | ``` |
| 359 | |
| 360 | Return production functions and classes with no covering tests. |
| 361 | |
| 362 | --- |
| 363 | |
| 364 | ### TestCoverageMap |
| 365 | |
| 366 | ```python |
| 367 | @dataclass |
| 368 | class TestCoverageMap: |
| 369 | coverage: dict[str, list[ContextNode]] # function name -> list of test nodes |
| 370 | uncovered: list[ContextNode] |
| 371 | coverage_percent: float |
| 372 | ``` |
| 373 | |
| 374 | | Field | Type | Description | |
| 375 | |---|---|---| |
| 376 | | `coverage` | `dict[str, list[ContextNode]]` | Maps each production function to its test nodes | |
| 377 | | `uncovered` | `list[ContextNode]` | Production functions with no tests | |
| 378 | | `coverage_percent` | `float` | Percentage of functions with at least one test | |
| 379 | |
| 380 | **Example:** |
| 381 | |
| 382 | ```python |
| 383 | mapper = TestMapper(store) |
| 384 | coverage_map = mapper.map("./src", "./tests") |
| 385 | |
| 386 | print(f"Coverage: {coverage_map.coverage_percent:.1f}%") |
| 387 | print(f"Uncovered: {len(coverage_map.uncovered)} functions") |
| 388 | |
| 389 | for fn_name, tests in coverage_map.coverage.items(): |
| 390 | print(f" {fn_name}: {len(tests)} tests") |
| 391 | for test in tests: |
| 392 | print(f" {test.properties['file']}::{test.name}") |
| 393 | ``` |
+19
| --- docs/api/graph.md | ||
| +++ docs/api/graph.md | ||
| @@ -57,10 +57,16 @@ | ||
| 57 | 57 | ) -> None: ... |
| 58 | 58 | |
| 59 | 59 | def clear(self) -> None: ... |
| 60 | 60 | |
| 61 | 61 | def close(self) -> None: ... |
| 62 | + | |
| 63 | + def export_jsonl(self, fp: IO[str]) -> None: ... | |
| 64 | + """Write all nodes and edges to a JSONL stream.""" | |
| 65 | + | |
| 66 | + def import_jsonl(self, fp: IO[str]) -> None: ... | |
| 67 | + """Read nodes and edges from a JSONL stream and merge into the graph.""" | |
| 62 | 68 | ``` |
| 63 | 69 | |
| 64 | 70 | ### Usage |
| 65 | 71 | |
| 66 | 72 | ```python |
| @@ -242,10 +248,23 @@ | ||
| 242 | 248 | to_id: str |
| 243 | 249 | edge_type: str # e.g. "CALLS", "ANNOTATES" |
| 244 | 250 | properties: dict |
| 245 | 251 | ``` |
| 246 | 252 | |
| 253 | +--- | |
| 254 | + | |
| 255 | +## Schema migrations | |
| 256 | + | |
| 257 | +```python | |
| 258 | +from navegador.graph import migrate | |
| 259 | + | |
| 260 | +store = GraphStore.sqlite(".navegador/navegador.db") | |
| 261 | +migrate(store) # applies any pending schema migrations; idempotent | |
| 262 | +``` | |
| 263 | + | |
| 264 | +The `migrate()` function is safe to call on every startup. It compares the stored migration version against the current schema and applies only missing migrations. | |
| 265 | + | |
| 247 | 266 | --- |
| 248 | 267 | |
| 249 | 268 | ## Formatting output |
| 250 | 269 | |
| 251 | 270 | ```python |
| 252 | 271 |
| --- docs/api/graph.md | |
| +++ docs/api/graph.md | |
| @@ -57,10 +57,16 @@ | |
| 57 | ) -> None: ... |
| 58 | |
| 59 | def clear(self) -> None: ... |
| 60 | |
| 61 | def close(self) -> None: ... |
| 62 | ``` |
| 63 | |
| 64 | ### Usage |
| 65 | |
| 66 | ```python |
| @@ -242,10 +248,23 @@ | |
| 242 | to_id: str |
| 243 | edge_type: str # e.g. "CALLS", "ANNOTATES" |
| 244 | properties: dict |
| 245 | ``` |
| 246 | |
| 247 | --- |
| 248 | |
| 249 | ## Formatting output |
| 250 | |
| 251 | ```python |
| 252 |
| --- docs/api/graph.md | |
| +++ docs/api/graph.md | |
| @@ -57,10 +57,16 @@ | |
| 57 | ) -> None: ... |
| 58 | |
| 59 | def clear(self) -> None: ... |
| 60 | |
| 61 | def close(self) -> None: ... |
| 62 | |
| 63 | def export_jsonl(self, fp: IO[str]) -> None: ... |
| 64 | """Write all nodes and edges to a JSONL stream.""" |
| 65 | |
| 66 | def import_jsonl(self, fp: IO[str]) -> None: ... |
| 67 | """Read nodes and edges from a JSONL stream and merge into the graph.""" |
| 68 | ``` |
| 69 | |
| 70 | ### Usage |
| 71 | |
| 72 | ```python |
| @@ -242,10 +248,23 @@ | |
| 248 | to_id: str |
| 249 | edge_type: str # e.g. "CALLS", "ANNOTATES" |
| 250 | properties: dict |
| 251 | ``` |
| 252 | |
| 253 | --- |
| 254 | |
| 255 | ## Schema migrations |
| 256 | |
| 257 | ```python |
| 258 | from navegador.graph import migrate |
| 259 | |
| 260 | store = GraphStore.sqlite(".navegador/navegador.db") |
| 261 | migrate(store) # applies any pending schema migrations; idempotent |
| 262 | ``` |
| 263 | |
| 264 | The `migrate()` function is safe to call on every startup. It compares the stored migration version against the current schema and applies only missing migrations. |
| 265 | |
| 266 | --- |
| 267 | |
| 268 | ## Formatting output |
| 269 | |
| 270 | ```python |
| 271 |
+94
-5
| --- docs/api/ingestion.md | ||
| +++ docs/api/ingestion.md | ||
| @@ -35,15 +35,20 @@ | ||
| 35 | 35 | def ingest( |
| 36 | 36 | self, |
| 37 | 37 | path: str | Path, |
| 38 | 38 | *, |
| 39 | 39 | clear: bool = False, |
| 40 | + incremental: bool = False, | |
| 41 | + redact: bool = False, | |
| 42 | + monorepo: bool = False, | |
| 40 | 43 | ) -> IngestionResult: ... |
| 41 | 44 | |
| 42 | 45 | def ingest_file( |
| 43 | 46 | self, |
| 44 | 47 | path: str | Path, |
| 48 | + *, | |
| 49 | + redact: bool = False, | |
| 45 | 50 | ) -> IngestionResult: ... |
| 46 | 51 | ``` |
| 47 | 52 | |
| 48 | 53 | ### Usage |
| 49 | 54 | |
| @@ -53,24 +58,43 @@ | ||
| 53 | 58 | |
| 54 | 59 | # full repo ingest |
| 55 | 60 | result = ingester.ingest("./src") |
| 56 | 61 | print(f"{result.nodes_created} nodes, {result.edges_created} edges") |
| 57 | 62 | |
| 63 | +# incremental ingest — only reprocesses files whose content hash has changed | |
| 64 | +result = ingester.ingest("./src", incremental=True) | |
| 65 | + | |
| 58 | 66 | # incremental: single file |
| 59 | 67 | result = ingester.ingest_file("./src/auth/service.py") |
| 60 | 68 | |
| 61 | 69 | # wipe + rebuild |
| 62 | 70 | result = ingester.ingest("./src", clear=True) |
| 71 | + | |
| 72 | +# redact sensitive content (strips tokens, passwords, keys from string literals) | |
| 73 | +result = ingester.ingest("./src", redact=True) | |
| 74 | + | |
| 75 | +# monorepo — traverse workspace sub-packages | |
| 76 | +result = ingester.ingest("./monorepo", monorepo=True) | |
| 63 | 77 | ``` |
| 64 | 78 | |
| 65 | 79 | ### Supported languages |
| 66 | 80 | |
| 67 | -| Language | File extensions | Parser | | |
| 68 | -|---|---|---| | |
| 69 | -| Python | `.py` | tree-sitter-python | | |
| 70 | -| TypeScript | `.ts`, `.tsx` | tree-sitter-typescript | | |
| 71 | -| JavaScript | `.js`, `.jsx` | tree-sitter-javascript | | |
| 81 | +| Language | File extensions | Parser | Extra required | | |
| 82 | +|---|---|---|---| | |
| 83 | +| Python | `.py` | tree-sitter-python | — (included) | | |
| 84 | +| TypeScript | `.ts`, `.tsx` | tree-sitter-typescript | — (included) | | |
| 85 | +| JavaScript | `.js`, `.jsx` | tree-sitter-javascript | — (included) | | |
| 86 | +| Go | `.go` | tree-sitter-go | — (included) | | |
| 87 | +| Rust | `.rs` | tree-sitter-rust | — (included) | | |
| 88 | +| Java | `.java` | tree-sitter-java | — (included) | | |
| 89 | +| Kotlin | `.kt`, `.kts` | tree-sitter-kotlin | `navegador[languages]` | | |
| 90 | +| C# | `.cs` | tree-sitter-c-sharp | `navegador[languages]` | | |
| 91 | +| PHP | `.php` | tree-sitter-php | `navegador[languages]` | | |
| 92 | +| Ruby | `.rb` | tree-sitter-ruby | `navegador[languages]` | | |
| 93 | +| Swift | `.swift` | tree-sitter-swift | `navegador[languages]` | | |
| 94 | +| C | `.c`, `.h` | tree-sitter-c | `navegador[languages]` | | |
| 95 | +| C++ | `.cpp`, `.cc`, `.cxx`, `.hpp` | tree-sitter-cpp | `navegador[languages]` | | |
| 72 | 96 | |
| 73 | 97 | ### Adding a new language parser |
| 74 | 98 | |
| 75 | 99 | 1. Install the tree-sitter grammar: `pip install tree-sitter-<lang>` |
| 76 | 100 | 2. Subclass `navegador.ingest.base.LanguageParser`: |
| @@ -95,10 +119,25 @@ | ||
| 95 | 119 | PARSERS["ruby"] = RubyParser |
| 96 | 120 | ``` |
| 97 | 121 | |
| 98 | 122 | `RepoIngester` dispatches to registered parsers by file extension. |
| 99 | 123 | |
| 124 | +### Framework enrichers | |
| 125 | + | |
| 126 | +After parsing, `RepoIngester` runs framework-specific enrichers that annotate nodes with framework context. Enrichers are discovered automatically based on what frameworks are detected in the repo. | |
| 127 | + | |
| 128 | +| Framework | What gets enriched | | |
| 129 | +|---|---| | |
| 130 | +| Django | Models, views, URL patterns, admin registrations | | |
| 131 | +| FastAPI | Route handlers, dependency injections, Pydantic schemas | | |
| 132 | +| React | Components, hooks, prop types | | |
| 133 | +| Express | Route handlers, middleware chains | | |
| 134 | +| React Native | Screens, navigators | | |
| 135 | +| Rails | Controllers, models, routes | | |
| 136 | +| Spring Boot | Beans, controllers, repositories | | |
| 137 | +| Laravel | Controllers, models, routes | | |
| 138 | + | |
| 100 | 139 | --- |
| 101 | 140 | |
| 102 | 141 | ## KnowledgeIngester |
| 103 | 142 | |
| 104 | 143 | Writes knowledge layer nodes. Wraps the `navegador add` commands programmatically. |
| @@ -266,5 +305,55 @@ | ||
| 266 | 305 | ``` |
| 267 | 306 | |
| 268 | 307 | `input_type` values: `"auto"`, `"manifest"`, `"kg"`, `"interchange"`, `"batch"`. |
| 269 | 308 | |
| 270 | 309 | See [Planopticon guide](../guide/planopticon.md) for format details and entity mapping. |
| 310 | + | |
| 311 | +--- | |
| 312 | + | |
| 313 | +## Export and import | |
| 314 | + | |
| 315 | +Navegador can export the full graph (or a subset) to JSONL for backup, migration, or sharing. The JSONL format is one JSON object per line, where each object is either a node or an edge. | |
| 316 | + | |
| 317 | +```bash | |
| 318 | +navegador export > graph.jsonl | |
| 319 | +navegador export --nodes-only > nodes.jsonl | |
| 320 | +navegador import graph.jsonl | |
| 321 | +``` | |
| 322 | + | |
| 323 | +Python API: | |
| 324 | + | |
| 325 | +```python | |
| 326 | +from navegador.graph import GraphStore | |
| 327 | + | |
| 328 | +store = GraphStore.sqlite(".navegador/navegador.db") | |
| 329 | + | |
| 330 | +# export | |
| 331 | +with open("graph.jsonl", "w") as f: | |
| 332 | + store.export_jsonl(f) | |
| 333 | + | |
| 334 | +# import into a new store | |
| 335 | +new_store = GraphStore.sqlite(".navegador/new.db") | |
| 336 | +with open("graph.jsonl") as f: | |
| 337 | + new_store.import_jsonl(f) | |
| 338 | +``` | |
| 339 | + | |
| 340 | +--- | |
| 341 | + | |
| 342 | +## Schema migrations | |
| 343 | + | |
| 344 | +When upgrading navegador, run `navegador migrate` before re-ingesting to apply schema changes (new node properties, new edge types, index updates): | |
| 345 | + | |
| 346 | +```bash | |
| 347 | +navegador migrate | |
| 348 | +``` | |
| 349 | + | |
| 350 | +Migrations are idempotent — safe to run multiple times. The migration state is stored in the graph itself under a `_MigrationState` node. | |
| 351 | + | |
| 352 | +Python API: | |
| 353 | + | |
| 354 | +```python | |
| 355 | +from navegador.graph import GraphStore, migrate | |
| 356 | + | |
| 357 | +store = GraphStore.sqlite(".navegador/navegador.db") | |
| 358 | +migrate(store) # applies any pending migrations | |
| 359 | +``` | |
| 271 | 360 |
| --- docs/api/ingestion.md | |
| +++ docs/api/ingestion.md | |
| @@ -35,15 +35,20 @@ | |
| 35 | def ingest( |
| 36 | self, |
| 37 | path: str | Path, |
| 38 | *, |
| 39 | clear: bool = False, |
| 40 | ) -> IngestionResult: ... |
| 41 | |
| 42 | def ingest_file( |
| 43 | self, |
| 44 | path: str | Path, |
| 45 | ) -> IngestionResult: ... |
| 46 | ``` |
| 47 | |
| 48 | ### Usage |
| 49 | |
| @@ -53,24 +58,43 @@ | |
| 53 | |
| 54 | # full repo ingest |
| 55 | result = ingester.ingest("./src") |
| 56 | print(f"{result.nodes_created} nodes, {result.edges_created} edges") |
| 57 | |
| 58 | # incremental: single file |
| 59 | result = ingester.ingest_file("./src/auth/service.py") |
| 60 | |
| 61 | # wipe + rebuild |
| 62 | result = ingester.ingest("./src", clear=True) |
| 63 | ``` |
| 64 | |
| 65 | ### Supported languages |
| 66 | |
| 67 | | Language | File extensions | Parser | |
| 68 | |---|---|---| |
| 69 | | Python | `.py` | tree-sitter-python | |
| 70 | | TypeScript | `.ts`, `.tsx` | tree-sitter-typescript | |
| 71 | | JavaScript | `.js`, `.jsx` | tree-sitter-javascript | |
| 72 | |
| 73 | ### Adding a new language parser |
| 74 | |
| 75 | 1. Install the tree-sitter grammar: `pip install tree-sitter-<lang>` |
| 76 | 2. Subclass `navegador.ingest.base.LanguageParser`: |
| @@ -95,10 +119,25 @@ | |
| 95 | PARSERS["ruby"] = RubyParser |
| 96 | ``` |
| 97 | |
| 98 | `RepoIngester` dispatches to registered parsers by file extension. |
| 99 | |
| 100 | --- |
| 101 | |
| 102 | ## KnowledgeIngester |
| 103 | |
| 104 | Writes knowledge layer nodes. Wraps the `navegador add` commands programmatically. |
| @@ -266,5 +305,55 @@ | |
| 266 | ``` |
| 267 | |
| 268 | `input_type` values: `"auto"`, `"manifest"`, `"kg"`, `"interchange"`, `"batch"`. |
| 269 | |
| 270 | See [Planopticon guide](../guide/planopticon.md) for format details and entity mapping. |
| 271 |
| --- docs/api/ingestion.md | |
| +++ docs/api/ingestion.md | |
| @@ -35,15 +35,20 @@ | |
| 35 | def ingest( |
| 36 | self, |
| 37 | path: str | Path, |
| 38 | *, |
| 39 | clear: bool = False, |
| 40 | incremental: bool = False, |
| 41 | redact: bool = False, |
| 42 | monorepo: bool = False, |
| 43 | ) -> IngestionResult: ... |
| 44 | |
| 45 | def ingest_file( |
| 46 | self, |
| 47 | path: str | Path, |
| 48 | *, |
| 49 | redact: bool = False, |
| 50 | ) -> IngestionResult: ... |
| 51 | ``` |
| 52 | |
| 53 | ### Usage |
| 54 | |
| @@ -53,24 +58,43 @@ | |
| 58 | |
| 59 | # full repo ingest |
| 60 | result = ingester.ingest("./src") |
| 61 | print(f"{result.nodes_created} nodes, {result.edges_created} edges") |
| 62 | |
| 63 | # incremental ingest — only reprocesses files whose content hash has changed |
| 64 | result = ingester.ingest("./src", incremental=True) |
| 65 | |
| 66 | # incremental: single file |
| 67 | result = ingester.ingest_file("./src/auth/service.py") |
| 68 | |
| 69 | # wipe + rebuild |
| 70 | result = ingester.ingest("./src", clear=True) |
| 71 | |
| 72 | # redact sensitive content (strips tokens, passwords, keys from string literals) |
| 73 | result = ingester.ingest("./src", redact=True) |
| 74 | |
| 75 | # monorepo — traverse workspace sub-packages |
| 76 | result = ingester.ingest("./monorepo", monorepo=True) |
| 77 | ``` |
| 78 | |
| 79 | ### Supported languages |
| 80 | |
| 81 | | Language | File extensions | Parser | Extra required | |
| 82 | |---|---|---|---| |
| 83 | | Python | `.py` | tree-sitter-python | — (included) | |
| 84 | | TypeScript | `.ts`, `.tsx` | tree-sitter-typescript | — (included) | |
| 85 | | JavaScript | `.js`, `.jsx` | tree-sitter-javascript | — (included) | |
| 86 | | Go | `.go` | tree-sitter-go | — (included) | |
| 87 | | Rust | `.rs` | tree-sitter-rust | — (included) | |
| 88 | | Java | `.java` | tree-sitter-java | — (included) | |
| 89 | | Kotlin | `.kt`, `.kts` | tree-sitter-kotlin | `navegador[languages]` | |
| 90 | | C# | `.cs` | tree-sitter-c-sharp | `navegador[languages]` | |
| 91 | | PHP | `.php` | tree-sitter-php | `navegador[languages]` | |
| 92 | | Ruby | `.rb` | tree-sitter-ruby | `navegador[languages]` | |
| 93 | | Swift | `.swift` | tree-sitter-swift | `navegador[languages]` | |
| 94 | | C | `.c`, `.h` | tree-sitter-c | `navegador[languages]` | |
| 95 | | C++ | `.cpp`, `.cc`, `.cxx`, `.hpp` | tree-sitter-cpp | `navegador[languages]` | |
| 96 | |
| 97 | ### Adding a new language parser |
| 98 | |
| 99 | 1. Install the tree-sitter grammar: `pip install tree-sitter-<lang>` |
| 100 | 2. Subclass `navegador.ingest.base.LanguageParser`: |
| @@ -95,10 +119,25 @@ | |
| 119 | PARSERS["ruby"] = RubyParser |
| 120 | ``` |
| 121 | |
| 122 | `RepoIngester` dispatches to registered parsers by file extension. |
| 123 | |
| 124 | ### Framework enrichers |
| 125 | |
| 126 | After parsing, `RepoIngester` runs framework-specific enrichers that annotate nodes with framework context. Enrichers are discovered automatically based on what frameworks are detected in the repo. |
| 127 | |
| 128 | | Framework | What gets enriched | |
| 129 | |---|---| |
| 130 | | Django | Models, views, URL patterns, admin registrations | |
| 131 | | FastAPI | Route handlers, dependency injections, Pydantic schemas | |
| 132 | | React | Components, hooks, prop types | |
| 133 | | Express | Route handlers, middleware chains | |
| 134 | | React Native | Screens, navigators | |
| 135 | | Rails | Controllers, models, routes | |
| 136 | | Spring Boot | Beans, controllers, repositories | |
| 137 | | Laravel | Controllers, models, routes | |
| 138 | |
| 139 | --- |
| 140 | |
| 141 | ## KnowledgeIngester |
| 142 | |
| 143 | Writes knowledge layer nodes. Wraps the `navegador add` commands programmatically. |
| @@ -266,5 +305,55 @@ | |
| 305 | ``` |
| 306 | |
| 307 | `input_type` values: `"auto"`, `"manifest"`, `"kg"`, `"interchange"`, `"batch"`. |
| 308 | |
| 309 | See [Planopticon guide](../guide/planopticon.md) for format details and entity mapping. |
| 310 | |
| 311 | --- |
| 312 | |
| 313 | ## Export and import |
| 314 | |
| 315 | Navegador can export the full graph (or a subset) to JSONL for backup, migration, or sharing. The JSONL format is one JSON object per line, where each object is either a node or an edge. |
| 316 | |
| 317 | ```bash |
| 318 | navegador export > graph.jsonl |
| 319 | navegador export --nodes-only > nodes.jsonl |
| 320 | navegador import graph.jsonl |
| 321 | ``` |
| 322 | |
| 323 | Python API: |
| 324 | |
| 325 | ```python |
| 326 | from navegador.graph import GraphStore |
| 327 | |
| 328 | store = GraphStore.sqlite(".navegador/navegador.db") |
| 329 | |
| 330 | # export |
| 331 | with open("graph.jsonl", "w") as f: |
| 332 | store.export_jsonl(f) |
| 333 | |
| 334 | # import into a new store |
| 335 | new_store = GraphStore.sqlite(".navegador/new.db") |
| 336 | with open("graph.jsonl") as f: |
| 337 | new_store.import_jsonl(f) |
| 338 | ``` |
| 339 | |
| 340 | --- |
| 341 | |
| 342 | ## Schema migrations |
| 343 | |
| 344 | When upgrading navegador, run `navegador migrate` before re-ingesting to apply schema changes (new node properties, new edge types, index updates): |
| 345 | |
| 346 | ```bash |
| 347 | navegador migrate |
| 348 | ``` |
| 349 | |
| 350 | Migrations are idempotent — safe to run multiple times. The migration state is stored in the graph itself under a `_MigrationState` node. |
| 351 | |
| 352 | Python API: |
| 353 | |
| 354 | ```python |
| 355 | from navegador.graph import GraphStore, migrate |
| 356 | |
| 357 | store = GraphStore.sqlite(".navegador/navegador.db") |
| 358 | migrate(store) # applies any pending migrations |
| 359 | ``` |
| 360 |
+152
-3
| --- docs/api/mcp.md | ||
| +++ docs/api/mcp.md | ||
| @@ -12,16 +12,21 @@ | ||
| 12 | 12 | |
| 13 | 13 | ```python |
| 14 | 14 | def create_mcp_server( |
| 15 | 15 | store: GraphStore | None = None, |
| 16 | 16 | db_path: str = "", |
| 17 | + read_only: bool = False, | |
| 18 | + max_query_complexity: int = 0, | |
| 17 | 19 | ) -> MCPServer: ... |
| 18 | 20 | ``` |
| 19 | 21 | |
| 20 | 22 | Creates and returns an MCP server instance. If `store` is not provided, opens a `GraphStore.sqlite()` at `db_path` (or `NAVEGADOR_DB` env var, or `./navegador.db`). |
| 21 | 23 | |
| 22 | -The CLI command `navegador mcp [--db path]` calls this function and runs the server loop. | |
| 24 | +- `read_only`: when `True`, disables the `ingest` tool and restricts the `query` tool to `MATCH`/`RETURN` only. Corresponds to `navegador mcp --read-only`. | |
| 25 | +- `max_query_complexity`: if non-zero, rejects Cypher queries whose estimated complexity exceeds this value. Prevents runaway traversals in multi-agent environments. | |
| 26 | + | |
| 27 | +The CLI command `navegador mcp [--db path] [--read-only]` calls this function and runs the server loop. | |
| 23 | 28 | |
| 24 | 29 | ### Usage |
| 25 | 30 | |
| 26 | 31 | ```python |
| 27 | 32 | from navegador.graph import GraphStore |
| @@ -42,11 +47,11 @@ | ||
| 42 | 47 | |
| 43 | 48 | --- |
| 44 | 49 | |
| 45 | 50 | ## Available MCP tools |
| 46 | 51 | |
| 47 | -All tools accept JSON input and return JSON output. | |
| 52 | +All tools accept JSON input and return JSON output. There are 11 tools in total. | |
| 48 | 53 | |
| 49 | 54 | --- |
| 50 | 55 | |
| 51 | 56 | ### `ingest` |
| 52 | 57 | |
| @@ -246,6 +251,150 @@ | ||
| 246 | 251 | ``` |
| 247 | 252 | |
| 248 | 253 | **Output:** Array of result rows as JSON. |
| 249 | 254 | |
| 250 | 255 | !!! warning |
| 251 | - This tool executes writes as well as reads. Agents should use read-only queries (`MATCH` / `RETURN`) unless explicitly performing graph updates. | |
| 256 | + This tool executes writes as well as reads. Agents should use read-only queries (`MATCH` / `RETURN`) unless explicitly performing graph updates. Use `--read-only` mode or `read_only=True` in `create_mcp_server()` to enforce this at the server level. | |
| 257 | + | |
| 258 | +--- | |
| 259 | + | |
| 260 | +### `get_rationale` | |
| 261 | + | |
| 262 | +Return the rationale, decisions, and rules that govern a named code node. | |
| 263 | + | |
| 264 | +**Input schema:** | |
| 265 | +```json | |
| 266 | +{ | |
| 267 | + "type": "object", | |
| 268 | + "properties": { | |
| 269 | + "name": { | |
| 270 | + "type": "string", | |
| 271 | + "description": "Function, class, or file name" | |
| 272 | + }, | |
| 273 | + "file": { | |
| 274 | + "type": "string", | |
| 275 | + "description": "Optional file path to disambiguate" | |
| 276 | + } | |
| 277 | + }, | |
| 278 | + "required": ["name"] | |
| 279 | +} | |
| 280 | +``` | |
| 281 | + | |
| 282 | +**Output:** Array of `Decision` and `Rule` nodes with `rationale` fields, as JSON. | |
| 283 | + | |
| 284 | +--- | |
| 285 | + | |
| 286 | +### `find_owners` | |
| 287 | + | |
| 288 | +Return the people and domains that own a code node, based on CODEOWNERS, annotation, and domain membership. | |
| 289 | + | |
| 290 | +**Input schema:** | |
| 291 | +```json | |
| 292 | +{ | |
| 293 | + "type": "object", | |
| 294 | + "properties": { | |
| 295 | + "name": { | |
| 296 | + "type": "string", | |
| 297 | + "description": "Function, class, or file name" | |
| 298 | + }, | |
| 299 | + "file": { | |
| 300 | + "type": "string", | |
| 301 | + "description": "Optional file path" | |
| 302 | + } | |
| 303 | + }, | |
| 304 | + "required": ["name"] | |
| 305 | +} | |
| 306 | +``` | |
| 307 | + | |
| 308 | +**Output:** Array of `Person` and `Domain` nodes as JSON. | |
| 309 | + | |
| 310 | +--- | |
| 311 | + | |
| 312 | +### `search_knowledge` | |
| 313 | + | |
| 314 | +Search the knowledge layer only (concepts, rules, decisions, wiki pages, domains). | |
| 315 | + | |
| 316 | +**Input schema:** | |
| 317 | +```json | |
| 318 | +{ | |
| 319 | + "type": "object", | |
| 320 | + "properties": { | |
| 321 | + "query": { | |
| 322 | + "type": "string", | |
| 323 | + "description": "Search query" | |
| 324 | + }, | |
| 325 | + "limit": { | |
| 326 | + "type": "integer", | |
| 327 | + "description": "Maximum results to return", | |
| 328 | + "default": 20 | |
| 329 | + } | |
| 330 | + }, | |
| 331 | + "required": ["query"] | |
| 332 | +} | |
| 333 | +``` | |
| 334 | + | |
| 335 | +**Output:** Array of knowledge layer `ContextNode` objects as JSON. | |
| 336 | + | |
| 337 | +--- | |
| 338 | + | |
| 339 | +### `blast_radius` | |
| 340 | + | |
| 341 | +Return all code nodes transitively reachable from a given node via CALLS, IMPORTS, and INHERITS edges — the set of things that could break if this node changes. | |
| 342 | + | |
| 343 | +**Input schema:** | |
| 344 | +```json | |
| 345 | +{ | |
| 346 | + "type": "object", | |
| 347 | + "properties": { | |
| 348 | + "name": { | |
| 349 | + "type": "string", | |
| 350 | + "description": "Starting node name" | |
| 351 | + }, | |
| 352 | + "file": { | |
| 353 | + "type": "string", | |
| 354 | + "description": "Optional file path to disambiguate" | |
| 355 | + }, | |
| 356 | + "depth": { | |
| 357 | + "type": "integer", | |
| 358 | + "description": "Maximum traversal depth", | |
| 359 | + "default": 3 | |
| 360 | + } | |
| 361 | + }, | |
| 362 | + "required": ["name"] | |
| 363 | +} | |
| 364 | +``` | |
| 365 | + | |
| 366 | +**Output:** Array of `ContextNode` objects ordered by distance from the starting node. | |
| 367 | + | |
| 368 | +--- | |
| 369 | + | |
| 370 | +## Security | |
| 371 | + | |
| 372 | +### Read-only mode | |
| 373 | + | |
| 374 | +Start the server in read-only mode to prevent agents from modifying the graph: | |
| 375 | + | |
| 376 | +```bash | |
| 377 | +navegador mcp --read-only | |
| 378 | +``` | |
| 379 | + | |
| 380 | +In read-only mode: | |
| 381 | +- The `ingest` tool is disabled (returns an error if called) | |
| 382 | +- The `query` tool validates that queries contain only `MATCH`, `RETURN`, `WITH`, `WHERE`, `ORDER BY`, `LIMIT`, and `SKIP` clauses | |
| 383 | +- Write Cypher keywords (`CREATE`, `MERGE`, `SET`, `DELETE`, `DETACH`) are rejected | |
| 384 | + | |
| 385 | +### Query complexity limits | |
| 386 | + | |
| 387 | +Set a maximum query complexity to prevent runaway traversals: | |
| 388 | + | |
| 389 | +```bash | |
| 390 | +navegador mcp --max-query-complexity 100 | |
| 391 | +``` | |
| 392 | + | |
| 393 | +Or in `.navegador/config.toml`: | |
| 394 | + | |
| 395 | +```toml | |
| 396 | +[mcp] | |
| 397 | +max_query_complexity = 100 | |
| 398 | +``` | |
| 399 | + | |
| 400 | +Queries that exceed the complexity threshold return an error rather than executing. | |
| 252 | 401 | |
| 253 | 402 | ADDED docs/api/sdk.md |
| --- docs/api/mcp.md | |
| +++ docs/api/mcp.md | |
| @@ -12,16 +12,21 @@ | |
| 12 | |
| 13 | ```python |
| 14 | def create_mcp_server( |
| 15 | store: GraphStore | None = None, |
| 16 | db_path: str = "", |
| 17 | ) -> MCPServer: ... |
| 18 | ``` |
| 19 | |
| 20 | Creates and returns an MCP server instance. If `store` is not provided, opens a `GraphStore.sqlite()` at `db_path` (or `NAVEGADOR_DB` env var, or `./navegador.db`). |
| 21 | |
| 22 | The CLI command `navegador mcp [--db path]` calls this function and runs the server loop. |
| 23 | |
| 24 | ### Usage |
| 25 | |
| 26 | ```python |
| 27 | from navegador.graph import GraphStore |
| @@ -42,11 +47,11 @@ | |
| 42 | |
| 43 | --- |
| 44 | |
| 45 | ## Available MCP tools |
| 46 | |
| 47 | All tools accept JSON input and return JSON output. |
| 48 | |
| 49 | --- |
| 50 | |
| 51 | ### `ingest` |
| 52 | |
| @@ -246,6 +251,150 @@ | |
| 246 | ``` |
| 247 | |
| 248 | **Output:** Array of result rows as JSON. |
| 249 | |
| 250 | !!! warning |
| 251 | This tool executes writes as well as reads. Agents should use read-only queries (`MATCH` / `RETURN`) unless explicitly performing graph updates. |
| 252 | |
| 253 | DDED docs/api/sdk.md |
| --- docs/api/mcp.md | |
| +++ docs/api/mcp.md | |
| @@ -12,16 +12,21 @@ | |
| 12 | |
| 13 | ```python |
| 14 | def create_mcp_server( |
| 15 | store: GraphStore | None = None, |
| 16 | db_path: str = "", |
| 17 | read_only: bool = False, |
| 18 | max_query_complexity: int = 0, |
| 19 | ) -> MCPServer: ... |
| 20 | ``` |
| 21 | |
| 22 | Creates and returns an MCP server instance. If `store` is not provided, opens a `GraphStore.sqlite()` at `db_path` (or `NAVEGADOR_DB` env var, or `./navegador.db`). |
| 23 | |
| 24 | - `read_only`: when `True`, disables the `ingest` tool and restricts the `query` tool to `MATCH`/`RETURN` only. Corresponds to `navegador mcp --read-only`. |
| 25 | - `max_query_complexity`: if non-zero, rejects Cypher queries whose estimated complexity exceeds this value. Prevents runaway traversals in multi-agent environments. |
| 26 | |
| 27 | The CLI command `navegador mcp [--db path] [--read-only]` calls this function and runs the server loop. |
| 28 | |
| 29 | ### Usage |
| 30 | |
| 31 | ```python |
| 32 | from navegador.graph import GraphStore |
| @@ -42,11 +47,11 @@ | |
| 47 | |
| 48 | --- |
| 49 | |
| 50 | ## Available MCP tools |
| 51 | |
| 52 | All tools accept JSON input and return JSON output. There are 11 tools in total. |
| 53 | |
| 54 | --- |
| 55 | |
| 56 | ### `ingest` |
| 57 | |
| @@ -246,6 +251,150 @@ | |
| 251 | ``` |
| 252 | |
| 253 | **Output:** Array of result rows as JSON. |
| 254 | |
| 255 | !!! warning |
| 256 | This tool executes writes as well as reads. Agents should use read-only queries (`MATCH` / `RETURN`) unless explicitly performing graph updates. Use `--read-only` mode or `read_only=True` in `create_mcp_server()` to enforce this at the server level. |
| 257 | |
| 258 | --- |
| 259 | |
| 260 | ### `get_rationale` |
| 261 | |
| 262 | Return the rationale, decisions, and rules that govern a named code node. |
| 263 | |
| 264 | **Input schema:** |
| 265 | ```json |
| 266 | { |
| 267 | "type": "object", |
| 268 | "properties": { |
| 269 | "name": { |
| 270 | "type": "string", |
| 271 | "description": "Function, class, or file name" |
| 272 | }, |
| 273 | "file": { |
| 274 | "type": "string", |
| 275 | "description": "Optional file path to disambiguate" |
| 276 | } |
| 277 | }, |
| 278 | "required": ["name"] |
| 279 | } |
| 280 | ``` |
| 281 | |
| 282 | **Output:** Array of `Decision` and `Rule` nodes with `rationale` fields, as JSON. |
| 283 | |
| 284 | --- |
| 285 | |
| 286 | ### `find_owners` |
| 287 | |
| 288 | Return the people and domains that own a code node, based on CODEOWNERS, annotation, and domain membership. |
| 289 | |
| 290 | **Input schema:** |
| 291 | ```json |
| 292 | { |
| 293 | "type": "object", |
| 294 | "properties": { |
| 295 | "name": { |
| 296 | "type": "string", |
| 297 | "description": "Function, class, or file name" |
| 298 | }, |
| 299 | "file": { |
| 300 | "type": "string", |
| 301 | "description": "Optional file path" |
| 302 | } |
| 303 | }, |
| 304 | "required": ["name"] |
| 305 | } |
| 306 | ``` |
| 307 | |
| 308 | **Output:** Array of `Person` and `Domain` nodes as JSON. |
| 309 | |
| 310 | --- |
| 311 | |
| 312 | ### `search_knowledge` |
| 313 | |
| 314 | Search the knowledge layer only (concepts, rules, decisions, wiki pages, domains). |
| 315 | |
| 316 | **Input schema:** |
| 317 | ```json |
| 318 | { |
| 319 | "type": "object", |
| 320 | "properties": { |
| 321 | "query": { |
| 322 | "type": "string", |
| 323 | "description": "Search query" |
| 324 | }, |
| 325 | "limit": { |
| 326 | "type": "integer", |
| 327 | "description": "Maximum results to return", |
| 328 | "default": 20 |
| 329 | } |
| 330 | }, |
| 331 | "required": ["query"] |
| 332 | } |
| 333 | ``` |
| 334 | |
| 335 | **Output:** Array of knowledge layer `ContextNode` objects as JSON. |
| 336 | |
| 337 | --- |
| 338 | |
| 339 | ### `blast_radius` |
| 340 | |
| 341 | Return all code nodes transitively reachable from a given node via CALLS, IMPORTS, and INHERITS edges — the set of things that could break if this node changes. |
| 342 | |
| 343 | **Input schema:** |
| 344 | ```json |
| 345 | { |
| 346 | "type": "object", |
| 347 | "properties": { |
| 348 | "name": { |
| 349 | "type": "string", |
| 350 | "description": "Starting node name" |
| 351 | }, |
| 352 | "file": { |
| 353 | "type": "string", |
| 354 | "description": "Optional file path to disambiguate" |
| 355 | }, |
| 356 | "depth": { |
| 357 | "type": "integer", |
| 358 | "description": "Maximum traversal depth", |
| 359 | "default": 3 |
| 360 | } |
| 361 | }, |
| 362 | "required": ["name"] |
| 363 | } |
| 364 | ``` |
| 365 | |
| 366 | **Output:** Array of `ContextNode` objects ordered by distance from the starting node. |
| 367 | |
| 368 | --- |
| 369 | |
| 370 | ## Security |
| 371 | |
| 372 | ### Read-only mode |
| 373 | |
| 374 | Start the server in read-only mode to prevent agents from modifying the graph: |
| 375 | |
| 376 | ```bash |
| 377 | navegador mcp --read-only |
| 378 | ``` |
| 379 | |
| 380 | In read-only mode: |
| 381 | - The `ingest` tool is disabled (returns an error if called) |
| 382 | - The `query` tool validates that queries contain only `MATCH`, `RETURN`, `WITH`, `WHERE`, `ORDER BY`, `LIMIT`, and `SKIP` clauses |
| 383 | - Write Cypher keywords (`CREATE`, `MERGE`, `SET`, `DELETE`, `DETACH`) are rejected |
| 384 | |
| 385 | ### Query complexity limits |
| 386 | |
| 387 | Set a maximum query complexity to prevent runaway traversals: |
| 388 | |
| 389 | ```bash |
| 390 | navegador mcp --max-query-complexity 100 |
| 391 | ``` |
| 392 | |
| 393 | Or in `.navegador/config.toml`: |
| 394 | |
| 395 | ```toml |
| 396 | [mcp] |
| 397 | max_query_complexity = 100 |
| 398 | ``` |
| 399 | |
| 400 | Queries that exceed the complexity threshold return an error rather than executing. |
| 401 | |
| 402 | DDED docs/api/sdk.md |
+486
| --- a/docs/api/sdk.md | ||
| +++ b/docs/api/sdk.md | ||
| @@ -0,0 +1,486 @@ | ||
| 1 | +# Python SDK Reference | |
| 2 | + | |
| 3 | +Full API reference for the navegador Python SDK. | |
| 4 | + | |
| 5 | +```python | |
| 6 | +from navegador.graph import GraphStore | |
| 7 | +from navegador.context import ContextLoader, ContextBundle, ContextNode, ContextEdge | |
| 8 | +from navegador.ingest import RepoIngester, KnowledgeIngester, WikiIngester, PlanopticonIngester | |
| 9 | +``` | |
| 10 | + | |
| 11 | +--- | |
| 12 | + | |
| 13 | +## GraphStore | |
| 14 | + | |
| 15 | +Database abstraction layer. Both SQLite and Redis backends implement this interface. | |
| 16 | + | |
| 17 | +```python | |
| 18 | +class GraphStore: | |
| 19 | + @classmethod | |
| 20 | + def sqlite(cls, path: str | Path = "navegador.db") -> "GraphStore": ... | |
| 21 | + | |
| 22 | + @classmethod | |
| 23 | + def redis(cls, url: str = "redis://localhost:6379") -> "GraphStore": ... | |
| 24 | +``` | |
| 25 | + | |
| 26 | +### Class methods | |
| 27 | + | |
| 28 | +#### `GraphStore.sqlite` | |
| 29 | + | |
| 30 | +```python | |
| 31 | +@classmethod | |
| 32 | +def sqlite(cls, path: str | Path = "navegador.db") -> "GraphStore" | |
| 33 | +``` | |
| 34 | + | |
| 35 | +Open a local SQLite-backed graph. The file is created if it does not exist. Uses `falkordblite` (embedded engine — no daemon required). | |
| 36 | + | |
| 37 | +**Parameters:** | |
| 38 | + | |
| 39 | +| Name | Type | Default | Description | | |
| 40 | +|---|---|---|---| | |
| 41 | +| `path` | `str \| Path` | `"navegador.db"` | Path to the `.db` file | | |
| 42 | + | |
| 43 | +**Returns:** `GraphStore` | |
| 44 | + | |
| 45 | +#### `GraphStore.redis` | |
| 46 | + | |
| 47 | +```python | |
| 48 | +@classmethod | |
| 49 | +def redis(cls, url: str = "redis://localhost:6379") -> "GraphStore" | |
| 50 | +``` | |
| 51 | + | |
| 52 | +Connect to a Redis-backed FalkorDB instance. | |
| 53 | + | |
| 54 | +**Parameters:** | |
| 55 | + | |
| 56 | +| Name | Type | Default | Description | | |
| 57 | +|---|---|---|---| | |
| 58 | +| `url` | `str` | `"redis://localhost:6379"` | Redis connection URL | | |
| 59 | + | |
| 60 | +**Returns:** `GraphStore` | |
| 61 | + | |
| 62 | +--- | |
| 63 | + | |
| 64 | +### Instance methods | |
| 65 | + | |
| 66 | +#### `query` | |
| 67 | + | |
| 68 | +```python | |
| 69 | +def query(self, cypher: str, params: dict | None = None) -> list[dict] | |
| 70 | +``` | |
| 71 | + | |
| 72 | +Execute a Cypher query and return all result rows. | |
| 73 | + | |
| 74 | +**Parameters:** | |
| 75 | + | |
| 76 | +| Name | Type | Default | Description | | |
| 77 | +|---|---|---|---| | |
| 78 | +| `cypher` | `str` | — | Cypher query string | | |
| 79 | +| `params` | `dict \| None` | `None` | Optional query parameters | | |
| 80 | + | |
| 81 | +**Returns:** `list[dict]` — one dict per result row, keyed by return variable name. | |
| 82 | + | |
| 83 | +--- | |
| 84 | + | |
| 85 | +#### `create_node` | |
| 86 | + | |
| 87 | +```python | |
| 88 | +def create_node(self, label: str, properties: dict) -> str | |
| 89 | +``` | |
| 90 | + | |
| 91 | +Create a new node and return its ID. | |
| 92 | + | |
| 93 | +**Parameters:** | |
| 94 | + | |
| 95 | +| Name | Type | Description | | |
| 96 | +|---|---|---| | |
| 97 | +| `label` | `str` | Node label (e.g., `"Function"`, `"Concept"`) | | |
| 98 | +| `properties` | `dict` | Node properties | | |
| 99 | + | |
| 100 | +**Returns:** `str` — node ID | |
| 101 | + | |
| 102 | +--- | |
| 103 | + | |
| 104 | +#### `merge_node` | |
| 105 | + | |
| 106 | +```python | |
| 107 | +def merge_node( | |
| 108 | + self, | |
| 109 | + label: str, | |
| 110 | + match_properties: dict, | |
| 111 | + set_properties: dict | None = None, | |
| 112 | +) -> str | |
| 113 | +``` | |
| 114 | + | |
| 115 | +Upsert a node: create it if no node with `match_properties` exists; otherwise update `set_properties` on the existing node. | |
| 116 | + | |
| 117 | +**Parameters:** | |
| 118 | + | |
| 119 | +| Name | Type | Default | Description | | |
| 120 | +|---|---|---|---| | |
| 121 | +| `label` | `str` | — | Node label | | |
| 122 | +| `match_properties` | `dict` | — | Properties to match on (identity key) | | |
| 123 | +| `set_properties` | `dict \| None` | `None` | Properties to set on create or update | | |
| 124 | + | |
| 125 | +**Returns:** `str` — node ID | |
| 126 | + | |
| 127 | +--- | |
| 128 | + | |
| 129 | +#### `create_edge` | |
| 130 | + | |
| 131 | +```python | |
| 132 | +def create_edge( | |
| 133 | + self, | |
| 134 | + from_id: str, | |
| 135 | + to_id: str, | |
| 136 | + edge_type: str, | |
| 137 | + properties: dict | None = None, | |
| 138 | +) -> None | |
| 139 | +``` | |
| 140 | + | |
| 141 | +Create a directed edge between two nodes. | |
| 142 | + | |
| 143 | +**Parameters:** | |
| 144 | + | |
| 145 | +| Name | Type | Default | Description | | |
| 146 | +|---|---|---|---| | |
| 147 | +| `from_id` | `str` | — | Source node ID | | |
| 148 | +| `to_id` | `str` | — | Target node ID | | |
| 149 | +| `edge_type` | `str` | — | Relationship type (e.g., `"CALLS"`, `"ANNOTATES"`) | | |
| 150 | +| `properties` | `dict \| None` | `None` | Optional edge properties | | |
| 151 | + | |
| 152 | +--- | |
| 153 | + | |
| 154 | +#### `merge_edge` | |
| 155 | + | |
| 156 | +```python | |
| 157 | +def merge_edge( | |
| 158 | + self, | |
| 159 | + from_label: str, | |
| 160 | + from_match: dict, | |
| 161 | + to_label: str, | |
| 162 | + to_match: dict, | |
| 163 | + edge_type: str, | |
| 164 | + properties: dict | None = None, | |
| 165 | +) -> None | |
| 166 | +``` | |
| 167 | + | |
| 168 | +Upsert an edge between two nodes matched by label and properties. | |
| 169 | + | |
| 170 | +--- | |
| 171 | + | |
| 172 | +#### `clear` | |
| 173 | + | |
| 174 | +```python | |
| 175 | +def clear(self) -> None | |
| 176 | +``` | |
| 177 | + | |
| 178 | +Delete all nodes and edges from the graph. | |
| 179 | + | |
| 180 | +--- | |
| 181 | + | |
| 182 | +#### `close` | |
| 183 | + | |
| 184 | +```python | |
| 185 | +def close(self) -> None | |
| 186 | +``` | |
| 187 | + | |
| 188 | +Release the database connection. Called automatically when used as a context manager. | |
| 189 | + | |
| 190 | +--- | |
| 191 | + | |
| 192 | +#### Context manager | |
| 193 | + | |
| 194 | +`GraphStore` implements `__enter__` / `__exit__`. Use with `with` to ensure the connection is closed: | |
| 195 | + | |
| 196 | +```python | |
| 197 | +with GraphStore.sqlite(".navegador/navegador.db") as store: | |
| 198 | + results = store.query("MATCH (n) RETURN count(n) AS total") | |
| 199 | +``` | |
| 200 | + | |
| 201 | +--- | |
| 202 | + | |
| 203 | +## ContextLoader | |
| 204 | + | |
| 205 | +Builds structured context bundles from graph queries. | |
| 206 | + | |
| 207 | +```python | |
| 208 | +class ContextLoader: | |
| 209 | + def __init__(self, store: GraphStore) -> None: ... | |
| 210 | +``` | |
| 211 | + | |
| 212 | +### Methods | |
| 213 | + | |
| 214 | +#### `load_file` | |
| 215 | + | |
| 216 | +```python | |
| 217 | +def load_file(self, path: str) -> ContextBundle | |
| 218 | +``` | |
| 219 | + | |
| 220 | +Return the full context bundle for a source file: the file node, its modules, classes, functions, imports, and their relationships. | |
| 221 | + | |
| 222 | +**Parameters:** | |
| 223 | + | |
| 224 | +| Name | Type | Description | | |
| 225 | +|---|---|---| | |
| 226 | +| `path` | `str` | Relative or absolute path to the source file | | |
| 227 | + | |
| 228 | +**Returns:** `ContextBundle` | |
| 229 | + | |
| 230 | +--- | |
| 231 | + | |
| 232 | +#### `load_function` | |
| 233 | + | |
| 234 | +```python | |
| 235 | +def load_function( | |
| 236 | + self, | |
| 237 | + name: str, | |
| 238 | + *, | |
| 239 | + file: str = "", | |
| 240 | + depth: int = 1, | |
| 241 | +) -> ContextBundle | |
| 242 | +``` | |
| 243 | + | |
| 244 | +Return a function node with its callers, callees, decorators, containing class, and source. | |
| 245 | + | |
| 246 | +**Parameters:** | |
| 247 | + | |
| 248 | +| Name | Type | Default | Description | | |
| 249 | +|---|---|---|---| | |
| 250 | +| `name` | `str` | — | Function name | | |
| 251 | +| `file` | `str` | `""` | Optional file path to disambiguate | | |
| 252 | +| `depth` | `int` | `1` | Call graph traversal depth (1 = direct callers/callees only) | | |
| 253 | + | |
| 254 | +**Returns:** `ContextBundle` | |
| 255 | + | |
| 256 | +--- | |
| 257 | + | |
| 258 | +#### `load_class` | |
| 259 | + | |
| 260 | +```python | |
| 261 | +def load_class( | |
| 262 | + self, | |
| 263 | + name: str, | |
| 264 | + *, | |
| 265 | + file: str = "", | |
| 266 | +) -> ContextBundle | |
| 267 | +``` | |
| 268 | + | |
| 269 | +Return a class node with its methods, base classes, subclasses, and references from other files. | |
| 270 | + | |
| 271 | +**Parameters:** | |
| 272 | + | |
| 273 | +| Name | Type | Default | Description | | |
| 274 | +|---|---|---|---| | |
| 275 | +| `name` | `str` | — | Class name | | |
| 276 | +| `file` | `str` | `""` | Optional file path to disambiguate | | |
| 277 | + | |
| 278 | +**Returns:** `ContextBundle` | |
| 279 | + | |
| 280 | +--- | |
| 281 | + | |
| 282 | +#### `explain` | |
| 283 | + | |
| 284 | +```python | |
| 285 | +def explain( | |
| 286 | + self, | |
| 287 | + name: str, | |
| 288 | + *, | |
| 289 | + file: str = "", | |
| 290 | +) -> ContextBundle | |
| 291 | +``` | |
| 292 | + | |
| 293 | +Universal lookup: explain any node (function, class, file, concept, rule, decision) by name. | |
| 294 | + | |
| 295 | +**Parameters:** | |
| 296 | + | |
| 297 | +| Name | Type | Default | Description | | |
| 298 | +|---|---|---|---| | |
| 299 | +| `name` | `str` | — | Node name or file path | | |
| 300 | +| `file` | `str` | `""` | Optional file path to disambiguate code nodes | | |
| 301 | + | |
| 302 | +**Returns:** `ContextBundle` | |
| 303 | + | |
| 304 | +--- | |
| 305 | + | |
| 306 | +#### `load_concept` | |
| 307 | + | |
| 308 | +```python | |
| 309 | +def load_concept(self, name: str) -> ContextBundle | |
| 310 | +``` | |
| 311 | + | |
| 312 | +Return a concept node with its rules, linked wiki pages, and annotated code nodes. | |
| 313 | + | |
| 314 | +--- | |
| 315 | + | |
| 316 | +#### `load_domain` | |
| 317 | + | |
| 318 | +```python | |
| 319 | +def load_domain(self, name: str) -> ContextBundle | |
| 320 | +``` | |
| 321 | + | |
| 322 | +Return a domain and all nodes belonging to it: concepts, rules, decisions, people, and annotated code. | |
| 323 | + | |
| 324 | +--- | |
| 325 | + | |
| 326 | +#### `search` | |
| 327 | + | |
| 328 | +```python | |
| 329 | +def search( | |
| 330 | + self, | |
| 331 | + query: str, | |
| 332 | + *, | |
| 333 | + all_layers: bool = False, | |
| 334 | + docs_only: bool = False, | |
| 335 | + limit: int = 20, | |
| 336 | +) -> list[ContextNode] | |
| 337 | +``` | |
| 338 | + | |
| 339 | +Search the graph by text query. | |
| 340 | + | |
| 341 | +**Parameters:** | |
| 342 | + | |
| 343 | +| Name | Type | Default | Description | | |
| 344 | +|---|---|---|---| | |
| 345 | +| `query` | `str` | — | Search string | | |
| 346 | +| `all_layers` | `bool` | `False` | Search all layers including knowledge and docs | | |
| 347 | +| `docs_only` | `bool` | `False` | Search docstrings and wiki content only | | |
| 348 | +| `limit` | `int` | `20` | Maximum number of results | | |
| 349 | + | |
| 350 | +**Returns:** `list[ContextNode]` | |
| 351 | + | |
| 352 | +--- | |
| 353 | + | |
| 354 | +#### `search_by_docstring` | |
| 355 | + | |
| 356 | +```python | |
| 357 | +def search_by_docstring(self, query: str, *, limit: int = 20) -> list[ContextNode] | |
| 358 | +``` | |
| 359 | + | |
| 360 | +Search docstrings and wiki page content. Equivalent to `search(query, docs_only=True)`. | |
| 361 | + | |
| 362 | +--- | |
| 363 | + | |
| 364 | +#### `decorated_by` | |
| 365 | + | |
| 366 | +```python | |
| 367 | +def decorated_by(self, decorator: str) -> list[ContextNode] | |
| 368 | +``` | |
| 369 | + | |
| 370 | +Find all functions and classes that use a specific decorator. | |
| 371 | + | |
| 372 | +**Parameters:** | |
| 373 | + | |
| 374 | +| Name | Type | Description | | |
| 375 | +|---|---|---| | |
| 376 | +| `decorator` | `str` | Decorator name (e.g., `"login_required"`, `"pytest.mark.parametrize"`) | | |
| 377 | + | |
| 378 | +**Returns:** `list[ContextNode]` | |
| 379 | + | |
| 380 | +--- | |
| 381 | + | |
| 382 | +## ContextBundle | |
| 383 | + | |
| 384 | +Structured result returned by `ContextLoader` methods. | |
| 385 | + | |
| 386 | +```python | |
| 387 | +@dataclass | |
| 388 | +class ContextBundle: | |
| 389 | + root: ContextNode | |
| 390 | + nodes: list[ContextNode] | |
| 391 | + edges: list[ContextEdge] | |
| 392 | + metadata: dict | |
| 393 | +``` | |
| 394 | + | |
| 395 | +### Fields | |
| 396 | + | |
| 397 | +| Field | Type | Description | | |
| 398 | +|---|---|---| | |
| 399 | +| `root` | `ContextNode` | The primary node (function, class, file, etc.) | | |
| 400 | +| `nodes` | `list[ContextNode]` | All nodes in the bundle, including `root` | | |
| 401 | +| `edges` | `list[ContextEdge]` | All edges between nodes in the bundle | | |
| 402 | +| `metadata` | `dict` | Query metadata (depth, timing, node counts) | | |
| 403 | + | |
| 404 | +### Methods | |
| 405 | + | |
| 406 | +#### `to_json` | |
| 407 | + | |
| 408 | +```python | |
| 409 | +def to_json(self) -> str | |
| 410 | +``` | |
| 411 | + | |
| 412 | +Serialize the bundle to a JSON string. | |
| 413 | + | |
| 414 | +#### `to_markdown` | |
| 415 | + | |
| 416 | +```python | |
| 417 | +def to_markdown(self) -> str | |
| 418 | +``` | |
| 419 | + | |
| 420 | +Render the bundle as a Markdown string. Suitable for pasting into agent context. | |
| 421 | + | |
| 422 | +#### `to_dict` | |
| 423 | + | |
| 424 | +```python | |
| 425 | +def to_dict(self) -> dict | |
| 426 | +``` | |
| 427 | + | |
| 428 | +Return the bundle as a plain Python dict. | |
| 429 | + | |
| 430 | +--- | |
| 431 | + | |
| 432 | +## ContextNode | |
| 433 | + | |
| 434 | +A single node in a context bundle. | |
| 435 | + | |
| 436 | +```python | |
| 437 | +@dataclass | |
| 438 | +class ContextNode: | |
| 439 | + id: str | |
| 440 | + label: str # e.g. "Function", "Concept" | |
| 441 | + name: str | |
| 442 | + properties: dict # all node properties from the graph | |
| 443 | + layer: str # "code" or "knowledge" | |
| 444 | + score: float # relevance score (search results only) | |
| 445 | +``` | |
| 446 | + | |
| 447 | +--- | |
| 448 | + | |
| 449 | +## ContextEdge | |
| 450 | + | |
| 451 | +A relationship between two nodes in a context bundle. | |
| 452 | + | |
| 453 | +```python | |
| 454 | +@dataclass | |
| 455 | +class ContextEdge: | |
| 456 | + from_id: str | |
| 457 | + to_id: str | |
| 458 | + edge_type: str # e.g. "CALLS", "ANNOTATES", "INHERITS" | |
| 459 | + properties: dict | |
| 460 | +``` | |
| 461 | + | |
| 462 | +--- | |
| 463 | + | |
| 464 | +## IngestionResult | |
| 465 | + | |
| 466 | +Returned by all ingest methods. | |
| 467 | + | |
| 468 | +```python | |
| 469 | +@dataclass | |
| 470 | +class IngestionResult: | |
| 471 | + nodes_created: int | |
| 472 | + nodes_updated: int | |
| 473 | + edges_created: int | |
| 474 | + files_processed: int | |
| 475 | + errors: list[str] | |
| 476 | + duration_seconds: float | |
| 477 | +``` | |
| 478 | + | |
| 479 | +| Field | Type | Description | | |
| 480 | +|---|---|---| | |
| 481 | +| `nodes_created` | `int` | New nodes written to the graph | | |
| 482 | +| `nodes_updated` | `int` | Existing nodes updated | | |
| 483 | +| `edges_created` | `int` | New edges written | | |
| 484 | +| `files_processed` | `int` | Source files walked | | |
| 485 | +| `errors` | `list[str]` | Per-file parse errors (non-fatal) | | |
| 486 | +| `duration_seconds` | `float` | Wall time for the ingest operation | |
| --- a/docs/api/sdk.md | |
| +++ b/docs/api/sdk.md | |
| @@ -0,0 +1,486 @@ | |
| --- a/docs/api/sdk.md | |
| +++ b/docs/api/sdk.md | |
| @@ -0,0 +1,486 @@ | |
| 1 | # Python SDK Reference |
| 2 | |
| 3 | Full API reference for the navegador Python SDK. |
| 4 | |
| 5 | ```python |
| 6 | from navegador.graph import GraphStore |
| 7 | from navegador.context import ContextLoader, ContextBundle, ContextNode, ContextEdge |
| 8 | from navegador.ingest import RepoIngester, KnowledgeIngester, WikiIngester, PlanopticonIngester |
| 9 | ``` |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## GraphStore |
| 14 | |
| 15 | Database abstraction layer. Both SQLite and Redis backends implement this interface. |
| 16 | |
| 17 | ```python |
| 18 | class GraphStore: |
| 19 | @classmethod |
| 20 | def sqlite(cls, path: str | Path = "navegador.db") -> "GraphStore": ... |
| 21 | |
| 22 | @classmethod |
| 23 | def redis(cls, url: str = "redis://localhost:6379") -> "GraphStore": ... |
| 24 | ``` |
| 25 | |
| 26 | ### Class methods |
| 27 | |
| 28 | #### `GraphStore.sqlite` |
| 29 | |
| 30 | ```python |
| 31 | @classmethod |
| 32 | def sqlite(cls, path: str | Path = "navegador.db") -> "GraphStore" |
| 33 | ``` |
| 34 | |
| 35 | Open a local SQLite-backed graph. The file is created if it does not exist. Uses `falkordblite` (embedded engine — no daemon required). |
| 36 | |
| 37 | **Parameters:** |
| 38 | |
| 39 | | Name | Type | Default | Description | |
| 40 | |---|---|---|---| |
| 41 | | `path` | `str \| Path` | `"navegador.db"` | Path to the `.db` file | |
| 42 | |
| 43 | **Returns:** `GraphStore` |
| 44 | |
| 45 | #### `GraphStore.redis` |
| 46 | |
| 47 | ```python |
| 48 | @classmethod |
| 49 | def redis(cls, url: str = "redis://localhost:6379") -> "GraphStore" |
| 50 | ``` |
| 51 | |
| 52 | Connect to a Redis-backed FalkorDB instance. |
| 53 | |
| 54 | **Parameters:** |
| 55 | |
| 56 | | Name | Type | Default | Description | |
| 57 | |---|---|---|---| |
| 58 | | `url` | `str` | `"redis://localhost:6379"` | Redis connection URL | |
| 59 | |
| 60 | **Returns:** `GraphStore` |
| 61 | |
| 62 | --- |
| 63 | |
| 64 | ### Instance methods |
| 65 | |
| 66 | #### `query` |
| 67 | |
| 68 | ```python |
| 69 | def query(self, cypher: str, params: dict | None = None) -> list[dict] |
| 70 | ``` |
| 71 | |
| 72 | Execute a Cypher query and return all result rows. |
| 73 | |
| 74 | **Parameters:** |
| 75 | |
| 76 | | Name | Type | Default | Description | |
| 77 | |---|---|---|---| |
| 78 | | `cypher` | `str` | — | Cypher query string | |
| 79 | | `params` | `dict \| None` | `None` | Optional query parameters | |
| 80 | |
| 81 | **Returns:** `list[dict]` — one dict per result row, keyed by return variable name. |
| 82 | |
| 83 | --- |
| 84 | |
| 85 | #### `create_node` |
| 86 | |
| 87 | ```python |
| 88 | def create_node(self, label: str, properties: dict) -> str |
| 89 | ``` |
| 90 | |
| 91 | Create a new node and return its ID. |
| 92 | |
| 93 | **Parameters:** |
| 94 | |
| 95 | | Name | Type | Description | |
| 96 | |---|---|---| |
| 97 | | `label` | `str` | Node label (e.g., `"Function"`, `"Concept"`) | |
| 98 | | `properties` | `dict` | Node properties | |
| 99 | |
| 100 | **Returns:** `str` — node ID |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | #### `merge_node` |
| 105 | |
| 106 | ```python |
| 107 | def merge_node( |
| 108 | self, |
| 109 | label: str, |
| 110 | match_properties: dict, |
| 111 | set_properties: dict | None = None, |
| 112 | ) -> str |
| 113 | ``` |
| 114 | |
| 115 | Upsert a node: create it if no node with `match_properties` exists; otherwise update `set_properties` on the existing node. |
| 116 | |
| 117 | **Parameters:** |
| 118 | |
| 119 | | Name | Type | Default | Description | |
| 120 | |---|---|---|---| |
| 121 | | `label` | `str` | — | Node label | |
| 122 | | `match_properties` | `dict` | — | Properties to match on (identity key) | |
| 123 | | `set_properties` | `dict \| None` | `None` | Properties to set on create or update | |
| 124 | |
| 125 | **Returns:** `str` — node ID |
| 126 | |
| 127 | --- |
| 128 | |
| 129 | #### `create_edge` |
| 130 | |
| 131 | ```python |
| 132 | def create_edge( |
| 133 | self, |
| 134 | from_id: str, |
| 135 | to_id: str, |
| 136 | edge_type: str, |
| 137 | properties: dict | None = None, |
| 138 | ) -> None |
| 139 | ``` |
| 140 | |
| 141 | Create a directed edge between two nodes. |
| 142 | |
| 143 | **Parameters:** |
| 144 | |
| 145 | | Name | Type | Default | Description | |
| 146 | |---|---|---|---| |
| 147 | | `from_id` | `str` | — | Source node ID | |
| 148 | | `to_id` | `str` | — | Target node ID | |
| 149 | | `edge_type` | `str` | — | Relationship type (e.g., `"CALLS"`, `"ANNOTATES"`) | |
| 150 | | `properties` | `dict \| None` | `None` | Optional edge properties | |
| 151 | |
| 152 | --- |
| 153 | |
| 154 | #### `merge_edge` |
| 155 | |
| 156 | ```python |
| 157 | def merge_edge( |
| 158 | self, |
| 159 | from_label: str, |
| 160 | from_match: dict, |
| 161 | to_label: str, |
| 162 | to_match: dict, |
| 163 | edge_type: str, |
| 164 | properties: dict | None = None, |
| 165 | ) -> None |
| 166 | ``` |
| 167 | |
| 168 | Upsert an edge between two nodes matched by label and properties. |
| 169 | |
| 170 | --- |
| 171 | |
| 172 | #### `clear` |
| 173 | |
| 174 | ```python |
| 175 | def clear(self) -> None |
| 176 | ``` |
| 177 | |
| 178 | Delete all nodes and edges from the graph. |
| 179 | |
| 180 | --- |
| 181 | |
| 182 | #### `close` |
| 183 | |
| 184 | ```python |
| 185 | def close(self) -> None |
| 186 | ``` |
| 187 | |
| 188 | Release the database connection. Called automatically when used as a context manager. |
| 189 | |
| 190 | --- |
| 191 | |
| 192 | #### Context manager |
| 193 | |
| 194 | `GraphStore` implements `__enter__` / `__exit__`. Use with `with` to ensure the connection is closed: |
| 195 | |
| 196 | ```python |
| 197 | with GraphStore.sqlite(".navegador/navegador.db") as store: |
| 198 | results = store.query("MATCH (n) RETURN count(n) AS total") |
| 199 | ``` |
| 200 | |
| 201 | --- |
| 202 | |
| 203 | ## ContextLoader |
| 204 | |
| 205 | Builds structured context bundles from graph queries. |
| 206 | |
| 207 | ```python |
| 208 | class ContextLoader: |
| 209 | def __init__(self, store: GraphStore) -> None: ... |
| 210 | ``` |
| 211 | |
| 212 | ### Methods |
| 213 | |
| 214 | #### `load_file` |
| 215 | |
| 216 | ```python |
| 217 | def load_file(self, path: str) -> ContextBundle |
| 218 | ``` |
| 219 | |
| 220 | Return the full context bundle for a source file: the file node, its modules, classes, functions, imports, and their relationships. |
| 221 | |
| 222 | **Parameters:** |
| 223 | |
| 224 | | Name | Type | Description | |
| 225 | |---|---|---| |
| 226 | | `path` | `str` | Relative or absolute path to the source file | |
| 227 | |
| 228 | **Returns:** `ContextBundle` |
| 229 | |
| 230 | --- |
| 231 | |
| 232 | #### `load_function` |
| 233 | |
| 234 | ```python |
| 235 | def load_function( |
| 236 | self, |
| 237 | name: str, |
| 238 | *, |
| 239 | file: str = "", |
| 240 | depth: int = 1, |
| 241 | ) -> ContextBundle |
| 242 | ``` |
| 243 | |
| 244 | Return a function node with its callers, callees, decorators, containing class, and source. |
| 245 | |
| 246 | **Parameters:** |
| 247 | |
| 248 | | Name | Type | Default | Description | |
| 249 | |---|---|---|---| |
| 250 | | `name` | `str` | — | Function name | |
| 251 | | `file` | `str` | `""` | Optional file path to disambiguate | |
| 252 | | `depth` | `int` | `1` | Call graph traversal depth (1 = direct callers/callees only) | |
| 253 | |
| 254 | **Returns:** `ContextBundle` |
| 255 | |
| 256 | --- |
| 257 | |
| 258 | #### `load_class` |
| 259 | |
| 260 | ```python |
| 261 | def load_class( |
| 262 | self, |
| 263 | name: str, |
| 264 | *, |
| 265 | file: str = "", |
| 266 | ) -> ContextBundle |
| 267 | ``` |
| 268 | |
| 269 | Return a class node with its methods, base classes, subclasses, and references from other files. |
| 270 | |
| 271 | **Parameters:** |
| 272 | |
| 273 | | Name | Type | Default | Description | |
| 274 | |---|---|---|---| |
| 275 | | `name` | `str` | — | Class name | |
| 276 | | `file` | `str` | `""` | Optional file path to disambiguate | |
| 277 | |
| 278 | **Returns:** `ContextBundle` |
| 279 | |
| 280 | --- |
| 281 | |
| 282 | #### `explain` |
| 283 | |
| 284 | ```python |
| 285 | def explain( |
| 286 | self, |
| 287 | name: str, |
| 288 | *, |
| 289 | file: str = "", |
| 290 | ) -> ContextBundle |
| 291 | ``` |
| 292 | |
| 293 | Universal lookup: explain any node (function, class, file, concept, rule, decision) by name. |
| 294 | |
| 295 | **Parameters:** |
| 296 | |
| 297 | | Name | Type | Default | Description | |
| 298 | |---|---|---|---| |
| 299 | | `name` | `str` | — | Node name or file path | |
| 300 | | `file` | `str` | `""` | Optional file path to disambiguate code nodes | |
| 301 | |
| 302 | **Returns:** `ContextBundle` |
| 303 | |
| 304 | --- |
| 305 | |
| 306 | #### `load_concept` |
| 307 | |
| 308 | ```python |
| 309 | def load_concept(self, name: str) -> ContextBundle |
| 310 | ``` |
| 311 | |
| 312 | Return a concept node with its rules, linked wiki pages, and annotated code nodes. |
| 313 | |
| 314 | --- |
| 315 | |
| 316 | #### `load_domain` |
| 317 | |
| 318 | ```python |
| 319 | def load_domain(self, name: str) -> ContextBundle |
| 320 | ``` |
| 321 | |
| 322 | Return a domain and all nodes belonging to it: concepts, rules, decisions, people, and annotated code. |
| 323 | |
| 324 | --- |
| 325 | |
| 326 | #### `search` |
| 327 | |
| 328 | ```python |
| 329 | def search( |
| 330 | self, |
| 331 | query: str, |
| 332 | *, |
| 333 | all_layers: bool = False, |
| 334 | docs_only: bool = False, |
| 335 | limit: int = 20, |
| 336 | ) -> list[ContextNode] |
| 337 | ``` |
| 338 | |
| 339 | Search the graph by text query. |
| 340 | |
| 341 | **Parameters:** |
| 342 | |
| 343 | | Name | Type | Default | Description | |
| 344 | |---|---|---|---| |
| 345 | | `query` | `str` | — | Search string | |
| 346 | | `all_layers` | `bool` | `False` | Search all layers including knowledge and docs | |
| 347 | | `docs_only` | `bool` | `False` | Search docstrings and wiki content only | |
| 348 | | `limit` | `int` | `20` | Maximum number of results | |
| 349 | |
| 350 | **Returns:** `list[ContextNode]` |
| 351 | |
| 352 | --- |
| 353 | |
| 354 | #### `search_by_docstring` |
| 355 | |
| 356 | ```python |
| 357 | def search_by_docstring(self, query: str, *, limit: int = 20) -> list[ContextNode] |
| 358 | ``` |
| 359 | |
| 360 | Search docstrings and wiki page content. Equivalent to `search(query, docs_only=True)`. |
| 361 | |
| 362 | --- |
| 363 | |
| 364 | #### `decorated_by` |
| 365 | |
| 366 | ```python |
| 367 | def decorated_by(self, decorator: str) -> list[ContextNode] |
| 368 | ``` |
| 369 | |
| 370 | Find all functions and classes that use a specific decorator. |
| 371 | |
| 372 | **Parameters:** |
| 373 | |
| 374 | | Name | Type | Description | |
| 375 | |---|---|---| |
| 376 | | `decorator` | `str` | Decorator name (e.g., `"login_required"`, `"pytest.mark.parametrize"`) | |
| 377 | |
| 378 | **Returns:** `list[ContextNode]` |
| 379 | |
| 380 | --- |
| 381 | |
| 382 | ## ContextBundle |
| 383 | |
| 384 | Structured result returned by `ContextLoader` methods. |
| 385 | |
| 386 | ```python |
| 387 | @dataclass |
| 388 | class ContextBundle: |
| 389 | root: ContextNode |
| 390 | nodes: list[ContextNode] |
| 391 | edges: list[ContextEdge] |
| 392 | metadata: dict |
| 393 | ``` |
| 394 | |
| 395 | ### Fields |
| 396 | |
| 397 | | Field | Type | Description | |
| 398 | |---|---|---| |
| 399 | | `root` | `ContextNode` | The primary node (function, class, file, etc.) | |
| 400 | | `nodes` | `list[ContextNode]` | All nodes in the bundle, including `root` | |
| 401 | | `edges` | `list[ContextEdge]` | All edges between nodes in the bundle | |
| 402 | | `metadata` | `dict` | Query metadata (depth, timing, node counts) | |
| 403 | |
| 404 | ### Methods |
| 405 | |
| 406 | #### `to_json` |
| 407 | |
| 408 | ```python |
| 409 | def to_json(self) -> str |
| 410 | ``` |
| 411 | |
| 412 | Serialize the bundle to a JSON string. |
| 413 | |
| 414 | #### `to_markdown` |
| 415 | |
| 416 | ```python |
| 417 | def to_markdown(self) -> str |
| 418 | ``` |
| 419 | |
| 420 | Render the bundle as a Markdown string. Suitable for pasting into agent context. |
| 421 | |
| 422 | #### `to_dict` |
| 423 | |
| 424 | ```python |
| 425 | def to_dict(self) -> dict |
| 426 | ``` |
| 427 | |
| 428 | Return the bundle as a plain Python dict. |
| 429 | |
| 430 | --- |
| 431 | |
| 432 | ## ContextNode |
| 433 | |
| 434 | A single node in a context bundle. |
| 435 | |
| 436 | ```python |
| 437 | @dataclass |
| 438 | class ContextNode: |
| 439 | id: str |
| 440 | label: str # e.g. "Function", "Concept" |
| 441 | name: str |
| 442 | properties: dict # all node properties from the graph |
| 443 | layer: str # "code" or "knowledge" |
| 444 | score: float # relevance score (search results only) |
| 445 | ``` |
| 446 | |
| 447 | --- |
| 448 | |
| 449 | ## ContextEdge |
| 450 | |
| 451 | A relationship between two nodes in a context bundle. |
| 452 | |
| 453 | ```python |
| 454 | @dataclass |
| 455 | class ContextEdge: |
| 456 | from_id: str |
| 457 | to_id: str |
| 458 | edge_type: str # e.g. "CALLS", "ANNOTATES", "INHERITS" |
| 459 | properties: dict |
| 460 | ``` |
| 461 | |
| 462 | --- |
| 463 | |
| 464 | ## IngestionResult |
| 465 | |
| 466 | Returned by all ingest methods. |
| 467 | |
| 468 | ```python |
| 469 | @dataclass |
| 470 | class IngestionResult: |
| 471 | nodes_created: int |
| 472 | nodes_updated: int |
| 473 | edges_created: int |
| 474 | files_processed: int |
| 475 | errors: list[str] |
| 476 | duration_seconds: float |
| 477 | ``` |
| 478 | |
| 479 | | Field | Type | Description | |
| 480 | |---|---|---| |
| 481 | | `nodes_created` | `int` | New nodes written to the graph | |
| 482 | | `nodes_updated` | `int` | Existing nodes updated | |
| 483 | | `edges_created` | `int` | New edges written | |
| 484 | | `files_processed` | `int` | Source files walked | |
| 485 | | `errors` | `list[str]` | Per-file parse errors (non-fatal) | |
| 486 | | `duration_seconds` | `float` | Wall time for the ingest operation | |
| --- docs/architecture/graph-schema.md | ||
| +++ docs/architecture/graph-schema.md | ||
| @@ -47,10 +47,13 @@ | ||
| 47 | 47 | | `GOVERNS` | Rule | Function, Method, Class, File | Rule applies to code node | |
| 48 | 48 | | `DOCUMENTS` | WikiPage | Concept, Function, Class, File | Documentation relationship | |
| 49 | 49 | | `ANNOTATES` | Concept, Rule | Function, Method, Class, File, Module | Knowledge node annotates code node | |
| 50 | 50 | | `ASSIGNED_TO` | Rule, Decision | Person | Work item or decision assigned to person | |
| 51 | 51 | | `DECIDED_BY` | Decision | Person | Decision was made by person | |
| 52 | +| `TESTS` | Function, Method | Function, Method, Class | Test function exercises a source function or class | | |
| 53 | +| `COUPLED_WITH` | File, Module | File, Module | Files that change together frequently (from churn analysis) | | |
| 54 | +| `OWNS` | Person | File, Module, Class | Ownership from CODEOWNERS or annotation | | |
| 52 | 55 | |
| 53 | 56 | --- |
| 54 | 57 | |
| 55 | 58 | ## Schema diagram |
| 56 | 59 | |
| @@ -101,10 +104,14 @@ | ||
| 101 | 104 | Concept -->|ANNOTATES| Function |
| 102 | 105 | Concept -->|ANNOTATES| Class |
| 103 | 106 | Class -->|IMPLEMENTS| Concept |
| 104 | 107 | Decision -->|DECIDED_BY| Person |
| 105 | 108 | Rule -->|ASSIGNED_TO| Person |
| 109 | + Function -->|TESTS| Function | |
| 110 | + Function -->|TESTS| Class | |
| 111 | + File -->|COUPLED_WITH| File | |
| 112 | + Person -->|OWNS| File | |
| 106 | 113 | ``` |
| 107 | 114 | |
| 108 | 115 | --- |
| 109 | 116 | |
| 110 | 117 | ## Code vs knowledge layer distinction |
| @@ -118,5 +125,9 @@ | ||
| 118 | 125 | | Change rate | Fast (changes with every commit) | Slow (changes with design decisions) | |
| 119 | 126 | | Authorship | Derived from code | Human or meeting-authored | |
| 120 | 127 | | Queryability | Names, signatures, call graphs | Domain semantics, rationale, history | |
| 121 | 128 | |
| 122 | 129 | Cross-layer edges (`ANNOTATES`, `GOVERNS`, `IMPLEMENTS`, `DOCUMENTS`) are the join points between the two layers. They are created explicitly by humans via `navegador annotate` or inferred during wiki/Planopticon ingestion. |
| 130 | + | |
| 131 | +Analysis edges (`TESTS`, `COUPLED_WITH`) are created by analysis commands (`navegador testmap`, `navegador churn`) and are derived rather than curated. They are refreshed whenever those commands are re-run. | |
| 132 | + | |
| 133 | +`OWNS` edges are populated from CODEOWNERS file parsing (`navegador codeowners`) and from explicit `navegador annotate --owner` assignments. | |
| 123 | 134 |
| --- docs/architecture/graph-schema.md | |
| +++ docs/architecture/graph-schema.md | |
| @@ -47,10 +47,13 @@ | |
| 47 | | `GOVERNS` | Rule | Function, Method, Class, File | Rule applies to code node | |
| 48 | | `DOCUMENTS` | WikiPage | Concept, Function, Class, File | Documentation relationship | |
| 49 | | `ANNOTATES` | Concept, Rule | Function, Method, Class, File, Module | Knowledge node annotates code node | |
| 50 | | `ASSIGNED_TO` | Rule, Decision | Person | Work item or decision assigned to person | |
| 51 | | `DECIDED_BY` | Decision | Person | Decision was made by person | |
| 52 | |
| 53 | --- |
| 54 | |
| 55 | ## Schema diagram |
| 56 | |
| @@ -101,10 +104,14 @@ | |
| 101 | Concept -->|ANNOTATES| Function |
| 102 | Concept -->|ANNOTATES| Class |
| 103 | Class -->|IMPLEMENTS| Concept |
| 104 | Decision -->|DECIDED_BY| Person |
| 105 | Rule -->|ASSIGNED_TO| Person |
| 106 | ``` |
| 107 | |
| 108 | --- |
| 109 | |
| 110 | ## Code vs knowledge layer distinction |
| @@ -118,5 +125,9 @@ | |
| 118 | | Change rate | Fast (changes with every commit) | Slow (changes with design decisions) | |
| 119 | | Authorship | Derived from code | Human or meeting-authored | |
| 120 | | Queryability | Names, signatures, call graphs | Domain semantics, rationale, history | |
| 121 | |
| 122 | Cross-layer edges (`ANNOTATES`, `GOVERNS`, `IMPLEMENTS`, `DOCUMENTS`) are the join points between the two layers. They are created explicitly by humans via `navegador annotate` or inferred during wiki/Planopticon ingestion. |
| 123 |
| --- docs/architecture/graph-schema.md | |
| +++ docs/architecture/graph-schema.md | |
| @@ -47,10 +47,13 @@ | |
| 47 | | `GOVERNS` | Rule | Function, Method, Class, File | Rule applies to code node | |
| 48 | | `DOCUMENTS` | WikiPage | Concept, Function, Class, File | Documentation relationship | |
| 49 | | `ANNOTATES` | Concept, Rule | Function, Method, Class, File, Module | Knowledge node annotates code node | |
| 50 | | `ASSIGNED_TO` | Rule, Decision | Person | Work item or decision assigned to person | |
| 51 | | `DECIDED_BY` | Decision | Person | Decision was made by person | |
| 52 | | `TESTS` | Function, Method | Function, Method, Class | Test function exercises a source function or class | |
| 53 | | `COUPLED_WITH` | File, Module | File, Module | Files that change together frequently (from churn analysis) | |
| 54 | | `OWNS` | Person | File, Module, Class | Ownership from CODEOWNERS or annotation | |
| 55 | |
| 56 | --- |
| 57 | |
| 58 | ## Schema diagram |
| 59 | |
| @@ -101,10 +104,14 @@ | |
| 104 | Concept -->|ANNOTATES| Function |
| 105 | Concept -->|ANNOTATES| Class |
| 106 | Class -->|IMPLEMENTS| Concept |
| 107 | Decision -->|DECIDED_BY| Person |
| 108 | Rule -->|ASSIGNED_TO| Person |
| 109 | Function -->|TESTS| Function |
| 110 | Function -->|TESTS| Class |
| 111 | File -->|COUPLED_WITH| File |
| 112 | Person -->|OWNS| File |
| 113 | ``` |
| 114 | |
| 115 | --- |
| 116 | |
| 117 | ## Code vs knowledge layer distinction |
| @@ -118,5 +125,9 @@ | |
| 125 | | Change rate | Fast (changes with every commit) | Slow (changes with design decisions) | |
| 126 | | Authorship | Derived from code | Human or meeting-authored | |
| 127 | | Queryability | Names, signatures, call graphs | Domain semantics, rationale, history | |
| 128 | |
| 129 | Cross-layer edges (`ANNOTATES`, `GOVERNS`, `IMPLEMENTS`, `DOCUMENTS`) are the join points between the two layers. They are created explicitly by humans via `navegador annotate` or inferred during wiki/Planopticon ingestion. |
| 130 | |
| 131 | Analysis edges (`TESTS`, `COUPLED_WITH`) are created by analysis commands (`navegador testmap`, `navegador churn`) and are derived rather than curated. They are refreshed whenever those commands are re-run. |
| 132 | |
| 133 | `OWNS` edges are populated from CODEOWNERS file parsing (`navegador codeowners`) and from explicit `navegador annotate --owner` assignments. |
| 134 |
+80
-14
| --- docs/architecture/overview.md | ||
| +++ docs/architecture/overview.md | ||
| @@ -6,14 +6,22 @@ | ||
| 6 | 6 | |
| 7 | 7 | Most context tools for AI agents handle one or the other — either they parse code (AST tools, code search) or they surface docs (RAG over wikis, ADR files). Navegador stores both in the same property graph and connects them with typed edges. An agent asking "what does `process_payment` do?" gets back not just the function signature and call graph, but the business rules that govern it and the architectural decision that shaped its design — in a single structured query. |
| 8 | 8 | |
| 9 | 9 | --- |
| 10 | 10 | |
| 11 | -## Two-layer design | |
| 11 | +## Architecture layers | |
| 12 | 12 | |
| 13 | 13 | ``` |
| 14 | 14 | ┌──────────────────────────────────────────────────────────────────┐ |
| 15 | +│ INTELLIGENCE LAYER │ | |
| 16 | +│ LLM providers (Anthropic · OpenAI · Ollama) │ | |
| 17 | +│ Semantic search · NLP queries · Doc generation │ | |
| 18 | +├──────────────────────────────────────────────────────────────────┤ | |
| 19 | +│ ANALYSIS LAYER │ | |
| 20 | +│ Impact · Trace · Churn · Deadcode · Cycles · Testmap │ | |
| 21 | +│ Diff · Rename · Communities · Blast radius │ | |
| 22 | +├──────────────────────────────────────────────────────────────────┤ | |
| 15 | 23 | │ KNOWLEDGE LAYER │ |
| 16 | 24 | │ │ |
| 17 | 25 | │ Domain ──BELONGS_TO── Concept ──RELATED_TO── Rule │ |
| 18 | 26 | │ │ │ │ │ |
| 19 | 27 | │ └──BELONGS_TO── Decision GOVERNS │ │ |
| @@ -29,24 +37,43 @@ | ||
| 29 | 37 | │ Repository ──CONTAINS── File ──CONTAINS── Class │ |
| 30 | 38 | │ │ │ │ |
| 31 | 39 | │ CONTAINS DEFINES │ |
| 32 | 40 | │ ↓ ↓ │ |
| 33 | 41 | │ Function ──CALLS── Function │ |
| 34 | -│ │ │ | |
| 35 | -│ DECORATES── Decorator │ | |
| 42 | +│ │ │ │ | |
| 43 | +│ DECORATES─Decorator TESTS──TestFn │ | |
| 36 | 44 | │ │ │ |
| 37 | 45 | │ IMPORTS── Import │ |
| 46 | +├──────────────────────────────────────────────────────────────────┤ | |
| 47 | +│ ENRICHMENT LAYER │ | |
| 48 | +│ Framework metadata (Django · FastAPI · React · Rails · Spring) │ | |
| 49 | +│ VCS (Git · Fossil) · CODEOWNERS · ADRs · OpenAPI · GraphQL │ | |
| 50 | +├──────────────────────────────────────────────────────────────────┤ | |
| 51 | +│ STORE LAYER │ | |
| 52 | +│ FalkorDB (falkordblite SQLite local / Redis cluster) │ | |
| 38 | 53 | └──────────────────────────────────────────────────────────────────┘ |
| 39 | 54 | ``` |
| 40 | 55 | |
| 41 | 56 | ### Code layer |
| 42 | 57 | |
| 43 | -Populated automatically by `navegador ingest`. Contains the structural facts extracted from source code: which functions exist, what they call, which classes inherit from which, what decorators are applied. This layer changes whenever code changes; re-ingest is the refresh mechanism. | |
| 58 | +Populated automatically by `navegador ingest`. Contains the structural facts extracted from source code across 13 languages: which functions exist, what they call, which classes inherit from which, what decorators are applied. Supports incremental ingestion (content hashing), watch mode, and parallel processing. This layer changes whenever code changes; re-ingest is the refresh mechanism. | |
| 44 | 59 | |
| 45 | 60 | ### Knowledge layer |
| 46 | 61 | |
| 47 | -Populated by humans (via `navegador add`) or semi-automatically (via wiki and Planopticon ingestion). Contains the *why*: business concepts, architectural rules, recorded decisions, domain ownership, and documentation. This layer changes slowly and deliberately. | |
| 62 | +Populated by humans (via `navegador add`) or semi-automatically (via wiki, Planopticon, ADR, OpenAPI, and PM ingestion). Contains the *why*: business concepts, architectural rules, recorded decisions, domain ownership, and documentation. This layer changes slowly and deliberately. | |
| 63 | + | |
| 64 | +### Enrichment layer | |
| 65 | + | |
| 66 | +Framework enrichers run after AST parsing to add framework-specific metadata — Django model field types, FastAPI route paths, React component display names, etc. VCS adapters (Git, Fossil) provide blame, history, and churn data. CODEOWNERS files are parsed to populate `Person`→`File` ownership edges. | |
| 67 | + | |
| 68 | +### Analysis layer | |
| 69 | + | |
| 70 | +Graph analysis commands operate over the populated code and knowledge layers without additional ingestion. They run Cypher traversals for impact analysis, cycle detection, dead code detection, test mapping, and community detection. | |
| 71 | + | |
| 72 | +### Intelligence layer | |
| 73 | + | |
| 74 | +NLP and LLM commands (`navegador ask`, `navegador semantic-search`, `navegador docs`) use configurable LLM providers (Anthropic, OpenAI, Ollama) to answer natural language queries grounded in graph data. | |
| 48 | 75 | |
| 49 | 76 | ### Cross-layer edges |
| 50 | 77 | |
| 51 | 78 | | Edge | Meaning | Direction | |
| 52 | 79 | |---|---|---| |
| @@ -73,18 +100,26 @@ | ||
| 73 | 100 | --- |
| 74 | 101 | |
| 75 | 102 | ## Ingestion pipeline |
| 76 | 103 | |
| 77 | 104 | ``` |
| 78 | -Source code (Python · TypeScript · JavaScript · Go · Rust · Java) | |
| 105 | +Source code (13 languages via tree-sitter) | |
| 79 | 106 | │ |
| 80 | 107 | ▼ |
| 81 | 108 | tree-sitter parser (per-language grammar) |
| 109 | + + incremental parsing (LRU cache, content hashing) | |
| 110 | + + parallel ingestion (worker pool) | |
| 82 | 111 | │ |
| 83 | 112 | ▼ |
| 84 | 113 | AST visitor (extract nodes + relationships) |
| 85 | 114 | │ |
| 115 | + ▼ | |
| 116 | + Framework enrichers (Django · FastAPI · React · Rails · Spring · Laravel · …) | |
| 117 | + │ | |
| 118 | + ▼ | |
| 119 | + Graph diffing (only write changed nodes/edges) | |
| 120 | + │ | |
| 86 | 121 | ▼ |
| 87 | 122 | GraphStore.merge_node / create_edge |
| 88 | 123 | │ |
| 89 | 124 | ▼ |
| 90 | 125 | FalkorDB (SQLite or Redis) |
| @@ -92,13 +127,18 @@ | ||
| 92 | 127 | |
| 93 | 128 | ``` |
| 94 | 129 | Human curation (navegador add) |
| 95 | 130 | Wiki pages (navegador wiki ingest) |
| 96 | 131 | Planopticon output (navegador planopticon ingest) |
| 132 | +ADRs (navegador adr ingest) | |
| 133 | +OpenAPI / GraphQL schemas (navegador api ingest) | |
| 134 | +PM issues (navegador pm ingest --github) | |
| 135 | +External deps (navegador deps ingest) | |
| 136 | +Submodules (navegador submodules ingest) | |
| 97 | 137 | │ |
| 98 | 138 | ▼ |
| 99 | - KnowledgeIngester / WikiIngester / PlanopticonIngester | |
| 139 | + KnowledgeIngester / WikiIngester / PlanopticonIngester / … | |
| 100 | 140 | │ |
| 101 | 141 | ▼ |
| 102 | 142 | GraphStore.merge_node / create_edge |
| 103 | 143 | │ |
| 104 | 144 | ▼ |
| @@ -107,30 +147,56 @@ | ||
| 107 | 147 | |
| 108 | 148 | All ingesters write to the same graph. There is no separate code database and knowledge database. |
| 109 | 149 | |
| 110 | 150 | --- |
| 111 | 151 | |
| 112 | -## Query path | |
| 152 | +## Query and analysis path | |
| 113 | 153 | |
| 114 | 154 | ``` |
| 115 | 155 | User / agent |
| 116 | 156 | │ |
| 117 | - ▼ CLI command or MCP tool call | |
| 118 | -navegador context / function / explain / search / ... | |
| 157 | + ▼ CLI command, MCP tool call, or Python SDK | |
| 158 | +navegador context / function / explain / search / impact / trace / ... | |
| 119 | 159 | │ |
| 120 | 160 | ▼ |
| 121 | -ContextLoader (builds Cypher query) | |
| 161 | +ContextLoader / AnalysisEngine (builds Cypher query) | |
| 122 | 162 | │ |
| 123 | 163 | ▼ |
| 124 | -GraphStore.query(cypher) | |
| 164 | +GraphStore.query(cypher) ← MCP: query validation + complexity check | |
| 125 | 165 | │ |
| 126 | 166 | ▼ |
| 127 | 167 | FalkorDB (SQLite or Redis) |
| 128 | 168 | │ |
| 129 | 169 | ▼ |
| 130 | -ContextBundle (structured result) | |
| 170 | +ContextBundle / AnalysisResult (structured result) | |
| 131 | 171 | │ |
| 132 | 172 | ▼ |
| 133 | 173 | JSON / Markdown / rich terminal output |
| 134 | 174 | ``` |
| 135 | 175 | |
| 136 | -`ContextLoader` is the query abstraction layer. Each command (`function`, `class`, `explain`, etc.) corresponds to a `ContextLoader` method that constructs the appropriate Cypher query, fetches results, and assembles a `ContextBundle`. The CLI formats the bundle for output; the MCP server returns it as JSON. | |
| 176 | +`ContextLoader` handles context retrieval commands (`function`, `class`, `explain`, etc.). `AnalysisEngine` handles graph analysis commands (`impact`, `trace`, `deadcode`, `cycles`, `churn`, `testmap`). Both construct Cypher queries, fetch results from `GraphStore`, and return structured output. The CLI formats output for the terminal; the MCP server returns JSON; the Python SDK returns typed Python objects. | |
| 177 | + | |
| 178 | +--- | |
| 179 | + | |
| 180 | +## Cluster architecture | |
| 181 | + | |
| 182 | +For team and CI environments, navegador supports a cluster mode backed by a shared Redis graph: | |
| 183 | + | |
| 184 | +``` | |
| 185 | +Multiple agents / CI runners | |
| 186 | + │ | |
| 187 | + ▼ | |
| 188 | + navegador (each instance) | |
| 189 | + │ | |
| 190 | + ▼ | |
| 191 | + ┌────────────────────────────────────────┐ | |
| 192 | + │ Shared Redis │ | |
| 193 | + │ ├── FalkorDB graph (shared state) │ | |
| 194 | + │ ├── Pub/sub (event broadcast) │ | |
| 195 | + │ ├── Task queue (ingest jobs) │ | |
| 196 | + │ ├── Sessions (agent state) │ | |
| 197 | + │ ├── Checkpoints (long-running tasks) │ | |
| 198 | + │ └── Observability (metrics, traces) │ | |
| 199 | + └────────────────────────────────────────┘ | |
| 200 | +``` | |
| 201 | + | |
| 202 | +Configure with `[cluster]` in `.navegador/config.toml`. See [Configuration](../getting-started/configuration.md) for details. | |
| 137 | 203 |
| --- docs/architecture/overview.md | |
| +++ docs/architecture/overview.md | |
| @@ -6,14 +6,22 @@ | |
| 6 | |
| 7 | Most context tools for AI agents handle one or the other — either they parse code (AST tools, code search) or they surface docs (RAG over wikis, ADR files). Navegador stores both in the same property graph and connects them with typed edges. An agent asking "what does `process_payment` do?" gets back not just the function signature and call graph, but the business rules that govern it and the architectural decision that shaped its design — in a single structured query. |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## Two-layer design |
| 12 | |
| 13 | ``` |
| 14 | ┌──────────────────────────────────────────────────────────────────┐ |
| 15 | │ KNOWLEDGE LAYER │ |
| 16 | │ │ |
| 17 | │ Domain ──BELONGS_TO── Concept ──RELATED_TO── Rule │ |
| 18 | │ │ │ │ │ |
| 19 | │ └──BELONGS_TO── Decision GOVERNS │ │ |
| @@ -29,24 +37,43 @@ | |
| 29 | │ Repository ──CONTAINS── File ──CONTAINS── Class │ |
| 30 | │ │ │ │ |
| 31 | │ CONTAINS DEFINES │ |
| 32 | │ ↓ ↓ │ |
| 33 | │ Function ──CALLS── Function │ |
| 34 | │ │ │ |
| 35 | │ DECORATES── Decorator │ |
| 36 | │ │ │ |
| 37 | │ IMPORTS── Import │ |
| 38 | └──────────────────────────────────────────────────────────────────┘ |
| 39 | ``` |
| 40 | |
| 41 | ### Code layer |
| 42 | |
| 43 | Populated automatically by `navegador ingest`. Contains the structural facts extracted from source code: which functions exist, what they call, which classes inherit from which, what decorators are applied. This layer changes whenever code changes; re-ingest is the refresh mechanism. |
| 44 | |
| 45 | ### Knowledge layer |
| 46 | |
| 47 | Populated by humans (via `navegador add`) or semi-automatically (via wiki and Planopticon ingestion). Contains the *why*: business concepts, architectural rules, recorded decisions, domain ownership, and documentation. This layer changes slowly and deliberately. |
| 48 | |
| 49 | ### Cross-layer edges |
| 50 | |
| 51 | | Edge | Meaning | Direction | |
| 52 | |---|---|---| |
| @@ -73,18 +100,26 @@ | |
| 73 | --- |
| 74 | |
| 75 | ## Ingestion pipeline |
| 76 | |
| 77 | ``` |
| 78 | Source code (Python · TypeScript · JavaScript · Go · Rust · Java) |
| 79 | │ |
| 80 | ▼ |
| 81 | tree-sitter parser (per-language grammar) |
| 82 | │ |
| 83 | ▼ |
| 84 | AST visitor (extract nodes + relationships) |
| 85 | │ |
| 86 | ▼ |
| 87 | GraphStore.merge_node / create_edge |
| 88 | │ |
| 89 | ▼ |
| 90 | FalkorDB (SQLite or Redis) |
| @@ -92,13 +127,18 @@ | |
| 92 | |
| 93 | ``` |
| 94 | Human curation (navegador add) |
| 95 | Wiki pages (navegador wiki ingest) |
| 96 | Planopticon output (navegador planopticon ingest) |
| 97 | │ |
| 98 | ▼ |
| 99 | KnowledgeIngester / WikiIngester / PlanopticonIngester |
| 100 | │ |
| 101 | ▼ |
| 102 | GraphStore.merge_node / create_edge |
| 103 | │ |
| 104 | ▼ |
| @@ -107,30 +147,56 @@ | |
| 107 | |
| 108 | All ingesters write to the same graph. There is no separate code database and knowledge database. |
| 109 | |
| 110 | --- |
| 111 | |
| 112 | ## Query path |
| 113 | |
| 114 | ``` |
| 115 | User / agent |
| 116 | │ |
| 117 | ▼ CLI command or MCP tool call |
| 118 | navegador context / function / explain / search / ... |
| 119 | │ |
| 120 | ▼ |
| 121 | ContextLoader (builds Cypher query) |
| 122 | │ |
| 123 | ▼ |
| 124 | GraphStore.query(cypher) |
| 125 | │ |
| 126 | ▼ |
| 127 | FalkorDB (SQLite or Redis) |
| 128 | │ |
| 129 | ▼ |
| 130 | ContextBundle (structured result) |
| 131 | │ |
| 132 | ▼ |
| 133 | JSON / Markdown / rich terminal output |
| 134 | ``` |
| 135 | |
| 136 | `ContextLoader` is the query abstraction layer. Each command (`function`, `class`, `explain`, etc.) corresponds to a `ContextLoader` method that constructs the appropriate Cypher query, fetches results, and assembles a `ContextBundle`. The CLI formats the bundle for output; the MCP server returns it as JSON. |
| 137 |
| --- docs/architecture/overview.md | |
| +++ docs/architecture/overview.md | |
| @@ -6,14 +6,22 @@ | |
| 6 | |
| 7 | Most context tools for AI agents handle one or the other — either they parse code (AST tools, code search) or they surface docs (RAG over wikis, ADR files). Navegador stores both in the same property graph and connects them with typed edges. An agent asking "what does `process_payment` do?" gets back not just the function signature and call graph, but the business rules that govern it and the architectural decision that shaped its design — in a single structured query. |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## Architecture layers |
| 12 | |
| 13 | ``` |
| 14 | ┌──────────────────────────────────────────────────────────────────┐ |
| 15 | │ INTELLIGENCE LAYER │ |
| 16 | │ LLM providers (Anthropic · OpenAI · Ollama) │ |
| 17 | │ Semantic search · NLP queries · Doc generation │ |
| 18 | ├──────────────────────────────────────────────────────────────────┤ |
| 19 | │ ANALYSIS LAYER │ |
| 20 | │ Impact · Trace · Churn · Deadcode · Cycles · Testmap │ |
| 21 | │ Diff · Rename · Communities · Blast radius │ |
| 22 | ├──────────────────────────────────────────────────────────────────┤ |
| 23 | │ KNOWLEDGE LAYER │ |
| 24 | │ │ |
| 25 | │ Domain ──BELONGS_TO── Concept ──RELATED_TO── Rule │ |
| 26 | │ │ │ │ │ |
| 27 | │ └──BELONGS_TO── Decision GOVERNS │ │ |
| @@ -29,24 +37,43 @@ | |
| 37 | │ Repository ──CONTAINS── File ──CONTAINS── Class │ |
| 38 | │ │ │ │ |
| 39 | │ CONTAINS DEFINES │ |
| 40 | │ ↓ ↓ │ |
| 41 | │ Function ──CALLS── Function │ |
| 42 | │ │ │ │ |
| 43 | │ DECORATES─Decorator TESTS──TestFn │ |
| 44 | │ │ │ |
| 45 | │ IMPORTS── Import │ |
| 46 | ├──────────────────────────────────────────────────────────────────┤ |
| 47 | │ ENRICHMENT LAYER │ |
| 48 | │ Framework metadata (Django · FastAPI · React · Rails · Spring) │ |
| 49 | │ VCS (Git · Fossil) · CODEOWNERS · ADRs · OpenAPI · GraphQL │ |
| 50 | ├──────────────────────────────────────────────────────────────────┤ |
| 51 | │ STORE LAYER │ |
| 52 | │ FalkorDB (falkordblite SQLite local / Redis cluster) │ |
| 53 | └──────────────────────────────────────────────────────────────────┘ |
| 54 | ``` |
| 55 | |
| 56 | ### Code layer |
| 57 | |
| 58 | Populated automatically by `navegador ingest`. Contains the structural facts extracted from source code across 13 languages: which functions exist, what they call, which classes inherit from which, what decorators are applied. Supports incremental ingestion (content hashing), watch mode, and parallel processing. This layer changes whenever code changes; re-ingest is the refresh mechanism. |
| 59 | |
| 60 | ### Knowledge layer |
| 61 | |
| 62 | Populated by humans (via `navegador add`) or semi-automatically (via wiki, Planopticon, ADR, OpenAPI, and PM ingestion). Contains the *why*: business concepts, architectural rules, recorded decisions, domain ownership, and documentation. This layer changes slowly and deliberately. |
| 63 | |
| 64 | ### Enrichment layer |
| 65 | |
| 66 | Framework enrichers run after AST parsing to add framework-specific metadata — Django model field types, FastAPI route paths, React component display names, etc. VCS adapters (Git, Fossil) provide blame, history, and churn data. CODEOWNERS files are parsed to populate `Person`→`File` ownership edges. |
| 67 | |
| 68 | ### Analysis layer |
| 69 | |
| 70 | Graph analysis commands operate over the populated code and knowledge layers without additional ingestion. They run Cypher traversals for impact analysis, cycle detection, dead code detection, test mapping, and community detection. |
| 71 | |
| 72 | ### Intelligence layer |
| 73 | |
| 74 | NLP and LLM commands (`navegador ask`, `navegador semantic-search`, `navegador docs`) use configurable LLM providers (Anthropic, OpenAI, Ollama) to answer natural language queries grounded in graph data. |
| 75 | |
| 76 | ### Cross-layer edges |
| 77 | |
| 78 | | Edge | Meaning | Direction | |
| 79 | |---|---|---| |
| @@ -73,18 +100,26 @@ | |
| 100 | --- |
| 101 | |
| 102 | ## Ingestion pipeline |
| 103 | |
| 104 | ``` |
| 105 | Source code (13 languages via tree-sitter) |
| 106 | │ |
| 107 | ▼ |
| 108 | tree-sitter parser (per-language grammar) |
| 109 | + incremental parsing (LRU cache, content hashing) |
| 110 | + parallel ingestion (worker pool) |
| 111 | │ |
| 112 | ▼ |
| 113 | AST visitor (extract nodes + relationships) |
| 114 | │ |
| 115 | ▼ |
| 116 | Framework enrichers (Django · FastAPI · React · Rails · Spring · Laravel · …) |
| 117 | │ |
| 118 | ▼ |
| 119 | Graph diffing (only write changed nodes/edges) |
| 120 | │ |
| 121 | ▼ |
| 122 | GraphStore.merge_node / create_edge |
| 123 | │ |
| 124 | ▼ |
| 125 | FalkorDB (SQLite or Redis) |
| @@ -92,13 +127,18 @@ | |
| 127 | |
| 128 | ``` |
| 129 | Human curation (navegador add) |
| 130 | Wiki pages (navegador wiki ingest) |
| 131 | Planopticon output (navegador planopticon ingest) |
| 132 | ADRs (navegador adr ingest) |
| 133 | OpenAPI / GraphQL schemas (navegador api ingest) |
| 134 | PM issues (navegador pm ingest --github) |
| 135 | External deps (navegador deps ingest) |
| 136 | Submodules (navegador submodules ingest) |
| 137 | │ |
| 138 | ▼ |
| 139 | KnowledgeIngester / WikiIngester / PlanopticonIngester / … |
| 140 | │ |
| 141 | ▼ |
| 142 | GraphStore.merge_node / create_edge |
| 143 | │ |
| 144 | ▼ |
| @@ -107,30 +147,56 @@ | |
| 147 | |
| 148 | All ingesters write to the same graph. There is no separate code database and knowledge database. |
| 149 | |
| 150 | --- |
| 151 | |
| 152 | ## Query and analysis path |
| 153 | |
| 154 | ``` |
| 155 | User / agent |
| 156 | │ |
| 157 | ▼ CLI command, MCP tool call, or Python SDK |
| 158 | navegador context / function / explain / search / impact / trace / ... |
| 159 | │ |
| 160 | ▼ |
| 161 | ContextLoader / AnalysisEngine (builds Cypher query) |
| 162 | │ |
| 163 | ▼ |
| 164 | GraphStore.query(cypher) ← MCP: query validation + complexity check |
| 165 | │ |
| 166 | ▼ |
| 167 | FalkorDB (SQLite or Redis) |
| 168 | │ |
| 169 | ▼ |
| 170 | ContextBundle / AnalysisResult (structured result) |
| 171 | │ |
| 172 | ▼ |
| 173 | JSON / Markdown / rich terminal output |
| 174 | ``` |
| 175 | |
| 176 | `ContextLoader` handles context retrieval commands (`function`, `class`, `explain`, etc.). `AnalysisEngine` handles graph analysis commands (`impact`, `trace`, `deadcode`, `cycles`, `churn`, `testmap`). Both construct Cypher queries, fetch results from `GraphStore`, and return structured output. The CLI formats output for the terminal; the MCP server returns JSON; the Python SDK returns typed Python objects. |
| 177 | |
| 178 | --- |
| 179 | |
| 180 | ## Cluster architecture |
| 181 | |
| 182 | For team and CI environments, navegador supports a cluster mode backed by a shared Redis graph: |
| 183 | |
| 184 | ``` |
| 185 | Multiple agents / CI runners |
| 186 | │ |
| 187 | ▼ |
| 188 | navegador (each instance) |
| 189 | │ |
| 190 | ▼ |
| 191 | ┌────────────────────────────────────────┐ |
| 192 | │ Shared Redis │ |
| 193 | │ ├── FalkorDB graph (shared state) │ |
| 194 | │ ├── Pub/sub (event broadcast) │ |
| 195 | │ ├── Task queue (ingest jobs) │ |
| 196 | │ ├── Sessions (agent state) │ |
| 197 | │ ├── Checkpoints (long-running tasks) │ |
| 198 | │ └── Observability (metrics, traces) │ |
| 199 | └────────────────────────────────────────┘ |
| 200 | ``` |
| 201 | |
| 202 | Configure with `[cluster]` in `.navegador/config.toml`. See [Configuration](../getting-started/configuration.md) for details. |
| 203 |
+65
-11
| --- docs/getting-started/configuration.md | ||
| +++ docs/getting-started/configuration.md | ||
| @@ -78,28 +78,81 @@ | ||
| 78 | 78 | |
| 79 | 79 | For public repos, wiki ingestion works without a token but will hit GitHub's unauthenticated rate limit (60 req/hr). |
| 80 | 80 | |
| 81 | 81 | --- |
| 82 | 82 | |
| 83 | -## Environment variable reference | |
| 84 | - | |
| 85 | -| Variable | Default | Description | | |
| 86 | -|---|---|---| | |
| 87 | -| `NAVEGADOR_DB` | `./navegador.db` | Path to SQLite file or `redis://` URL | | |
| 88 | -| `GITHUB_TOKEN` | — | GitHub personal access token for wiki ingestion | | |
| 89 | - | |
| ---- | ||
| 90 | - | |
| 91 | 83 | ## Project-local config |
| 92 | 84 | |
| 93 | -Drop a `.navegador/config.toml` in your project root for project-specific defaults (optional): | |
| 85 | +Drop a `.navegador/config.toml` in your project root for project-specific defaults: | |
| 94 | 86 | |
| 95 | 87 | ```toml |
| 96 | 88 | [database] |
| 97 | 89 | path = ".navegador/navegador.db" |
| 98 | 90 | |
| 99 | 91 | [ingest] |
| 100 | 92 | exclude = ["node_modules", "dist", ".venv", "migrations"] |
| 93 | +incremental = true # use content hashing by default | |
| 94 | +redact = false # strip secrets from ingested content | |
| 95 | + | |
| 96 | +[mcp] | |
| 97 | +read_only = false # set true to prevent agents from writing to the graph | |
| 98 | +max_query_complexity = 100 # Cypher query complexity limit | |
| 99 | +``` | |
| 100 | + | |
| 101 | +--- | |
| 102 | + | |
| 103 | +## LLM provider config | |
| 104 | + | |
| 105 | +Configure LLM providers used by `navegador ask`, `navegador docs`, and `navegador semantic-search`. Requires `pip install "navegador[llm]"`. | |
| 106 | + | |
| 107 | +```toml | |
| 108 | +[llm] | |
| 109 | +provider = "anthropic" # "anthropic", "openai", or "ollama" | |
| 110 | +model = "claude-3-5-haiku-20241022" | |
| 111 | + | |
| 112 | +[llm.anthropic] | |
| 113 | +api_key_env = "ANTHROPIC_API_KEY" # env var name (not the key itself) | |
| 114 | + | |
| 115 | +[llm.openai] | |
| 116 | +api_key_env = "OPENAI_API_KEY" | |
| 117 | +model = "gpt-4o-mini" | |
| 118 | + | |
| 119 | +[llm.ollama] | |
| 120 | +base_url = "http://localhost:11434" | |
| 121 | +model = "llama3" | |
| 122 | +``` | |
| 123 | + | |
| 124 | +--- | |
| 125 | + | |
| 126 | +## Cluster config | |
| 127 | + | |
| 128 | +For team deployments using a shared Redis graph with pub/sub, task queue, and session coordination: | |
| 129 | + | |
| 130 | +```toml | |
| 131 | +[cluster] | |
| 132 | +enabled = true | |
| 133 | +redis_url = "redis://redis.internal:6379" | |
| 134 | +graph_name = "navegador-team" | |
| 135 | + | |
| 136 | +[cluster.pubsub] | |
| 137 | +channel = "navegador:events" | |
| 138 | + | |
| 139 | +[cluster.queue] | |
| 140 | +name = "navegador:tasks" | |
| 141 | + | |
| 142 | +[cluster.sessions] | |
| 143 | +ttl_seconds = 3600 | |
| 101 | 144 | ``` |
| 102 | 145 | |
| 103 | -!!! note | |
| 104 | - `.navegador/config.toml` support is planned. Check `navegador --version` to confirm it is available in your installed version. | |
| 146 | +See the [Cluster mode](../guide/cluster.md) guide for full setup instructions. | |
| 147 | + | |
| 148 | +--- | |
| 149 | + | |
| 150 | +## Environment variable reference | |
| 151 | + | |
| 152 | +| Variable | Default | Description | | |
| 153 | +|---|---|---| | |
| 154 | +| `NAVEGADOR_DB` | `./navegador.db` | Path to SQLite file or `redis://` URL | | |
| 155 | +| `GITHUB_TOKEN` | — | GitHub personal access token for wiki ingestion | | |
| 156 | +| `ANTHROPIC_API_KEY` | — | Anthropic API key for LLM features | | |
| 157 | +| `OPENAI_API_KEY` | — | OpenAI API key for LLM features | | |
| 158 | +| `NAVEGADOR_CONFIG` | `.navegador/config.toml` | Override config file path | | |
| 105 | 159 |
| --- docs/getting-started/configuration.md | |
| +++ docs/getting-started/configuration.md | |
| @@ -78,28 +78,81 @@ | |
| 78 | |
| 79 | For public repos, wiki ingestion works without a token but will hit GitHub's unauthenticated rate limit (60 req/hr). |
| 80 | |
| 81 | --- |
| 82 | |
| 83 | ## Environment variable reference |
| 84 | |
| 85 | | Variable | Default | Description | |
| 86 | |---|---|---| |
| 87 | | `NAVEGADOR_DB` | `./navegador.db` | Path to SQLite file or `redis://` URL | |
| 88 | | `GITHUB_TOKEN` | — | GitHub personal access token for wiki ingestion | |
| 89 | |
| ---- | |
| 90 | |
| 91 | ## Project-local config |
| 92 | |
| 93 | Drop a `.navegador/config.toml` in your project root for project-specific defaults (optional): |
| 94 | |
| 95 | ```toml |
| 96 | [database] |
| 97 | path = ".navegador/navegador.db" |
| 98 | |
| 99 | [ingest] |
| 100 | exclude = ["node_modules", "dist", ".venv", "migrations"] |
| 101 | ``` |
| 102 | |
| 103 | !!! note |
| 104 | `.navegador/config.toml` support is planned. Check `navegador --version` to confirm it is available in your installed version. |
| 105 |
| --- docs/getting-started/configuration.md | |
| +++ docs/getting-started/configuration.md | |
| @@ -78,28 +78,81 @@ | |
| 78 | |
| 79 | For public repos, wiki ingestion works without a token but will hit GitHub's unauthenticated rate limit (60 req/hr). |
| 80 | |
| 81 | --- |
| 82 | |
| ---- | |
| 83 | ## Project-local config |
| 84 | |
| 85 | Drop a `.navegador/config.toml` in your project root for project-specific defaults: |
| 86 | |
| 87 | ```toml |
| 88 | [database] |
| 89 | path = ".navegador/navegador.db" |
| 90 | |
| 91 | [ingest] |
| 92 | exclude = ["node_modules", "dist", ".venv", "migrations"] |
| 93 | incremental = true # use content hashing by default |
| 94 | redact = false # strip secrets from ingested content |
| 95 | |
| 96 | [mcp] |
| 97 | read_only = false # set true to prevent agents from writing to the graph |
| 98 | max_query_complexity = 100 # Cypher query complexity limit |
| 99 | ``` |
| 100 | |
| 101 | --- |
| 102 | |
| 103 | ## LLM provider config |
| 104 | |
| 105 | Configure LLM providers used by `navegador ask`, `navegador docs`, and `navegador semantic-search`. Requires `pip install "navegador[llm]"`. |
| 106 | |
| 107 | ```toml |
| 108 | [llm] |
| 109 | provider = "anthropic" # "anthropic", "openai", or "ollama" |
| 110 | model = "claude-3-5-haiku-20241022" |
| 111 | |
| 112 | [llm.anthropic] |
| 113 | api_key_env = "ANTHROPIC_API_KEY" # env var name (not the key itself) |
| 114 | |
| 115 | [llm.openai] |
| 116 | api_key_env = "OPENAI_API_KEY" |
| 117 | model = "gpt-4o-mini" |
| 118 | |
| 119 | [llm.ollama] |
| 120 | base_url = "http://localhost:11434" |
| 121 | model = "llama3" |
| 122 | ``` |
| 123 | |
| 124 | --- |
| 125 | |
| 126 | ## Cluster config |
| 127 | |
| 128 | For team deployments using a shared Redis graph with pub/sub, task queue, and session coordination: |
| 129 | |
| 130 | ```toml |
| 131 | [cluster] |
| 132 | enabled = true |
| 133 | redis_url = "redis://redis.internal:6379" |
| 134 | graph_name = "navegador-team" |
| 135 | |
| 136 | [cluster.pubsub] |
| 137 | channel = "navegador:events" |
| 138 | |
| 139 | [cluster.queue] |
| 140 | name = "navegador:tasks" |
| 141 | |
| 142 | [cluster.sessions] |
| 143 | ttl_seconds = 3600 |
| 144 | ``` |
| 145 | |
| 146 | See the [Cluster mode](../guide/cluster.md) guide for full setup instructions. |
| 147 | |
| 148 | --- |
| 149 | |
| 150 | ## Environment variable reference |
| 151 | |
| 152 | | Variable | Default | Description | |
| 153 | |---|---|---| |
| 154 | | `NAVEGADOR_DB` | `./navegador.db` | Path to SQLite file or `redis://` URL | |
| 155 | | `GITHUB_TOKEN` | — | GitHub personal access token for wiki ingestion | |
| 156 | | `ANTHROPIC_API_KEY` | — | Anthropic API key for LLM features | |
| 157 | | `OPENAI_API_KEY` | — | OpenAI API key for LLM features | |
| 158 | | `NAVEGADOR_CONFIG` | `.navegador/config.toml` | Override config file path | |
| 159 |
+59
-4
| --- docs/getting-started/installation.md | ||
| +++ docs/getting-started/installation.md | ||
| @@ -40,10 +40,38 @@ | ||
| 40 | 40 | export NAVEGADOR_DB=redis://localhost:6379 |
| 41 | 41 | navegador ingest ./repo |
| 42 | 42 | ``` |
| 43 | 43 | |
| 44 | 44 | See [Configuration](configuration.md) for full Redis setup details. |
| 45 | + | |
| 46 | +=== "[languages]" | |
| 47 | + | |
| 48 | + Additional tree-sitter grammars for Kotlin, C#, PHP, Ruby, Swift, C, and C++. The default install includes Python, TypeScript, JavaScript, Go, Rust, and Java. | |
| 49 | + | |
| 50 | + ```bash | |
| 51 | + pip install "navegador[languages]" | |
| 52 | + ``` | |
| 53 | + | |
| 54 | + After installing, all 13 languages are parsed automatically by `navegador ingest`. No additional configuration is required. | |
| 55 | + | |
| 56 | +=== "[llm]" | |
| 57 | + | |
| 58 | + LLM provider integrations for Anthropic, OpenAI, and Ollama. Required for `navegador ask`, `navegador docs`, and `navegador semantic-search`. | |
| 59 | + | |
| 60 | + ```bash | |
| 61 | + pip install "navegador[llm]" | |
| 62 | + ``` | |
| 63 | + | |
| 64 | + Configure the provider in `.navegador/config.toml` or via environment variables. See [Configuration](configuration.md) for details. | |
| 65 | + | |
| 66 | +=== "all extras" | |
| 67 | + | |
| 68 | + Install everything at once: | |
| 69 | + | |
| 70 | + ```bash | |
| 71 | + pip install "navegador[sqlite,redis,languages,llm]" | |
| 72 | + ``` | |
| 45 | 73 | |
| 46 | 74 | ## Verify |
| 47 | 75 | |
| 48 | 76 | ```bash |
| 49 | 77 | navegador --version |
| @@ -50,27 +78,54 @@ | ||
| 50 | 78 | ``` |
| 51 | 79 | |
| 52 | 80 | Expected output: |
| 53 | 81 | |
| 54 | 82 | ``` |
| 55 | -navegador, version 0.x.y | |
| 83 | +navegador, version 0.7.0 | |
| 84 | +``` | |
| 85 | + | |
| 86 | +## Shell completions | |
| 87 | + | |
| 88 | +Install shell completions for tab-completion of commands and flags: | |
| 89 | + | |
| 90 | +```bash | |
| 91 | +navegador completions bash >> ~/.bashrc | |
| 92 | +navegador completions zsh >> ~/.zshrc | |
| 93 | +navegador completions fish > ~/.config/fish/completions/navegador.fish | |
| 94 | +``` | |
| 95 | + | |
| 96 | +## Python SDK | |
| 97 | + | |
| 98 | +The Python SDK wraps all CLI functionality in a single `Navegador` class: | |
| 99 | + | |
| 100 | +```python | |
| 101 | +from navegador import Navegador | |
| 102 | + | |
| 103 | +nav = Navegador(".navegador/navegador.db") | |
| 104 | +nav.ingest("./src") | |
| 105 | +bundle = nav.explain("AuthService") | |
| 106 | +print(bundle.to_markdown()) | |
| 56 | 107 | ``` |
| 108 | + | |
| 109 | +The SDK is included in the base install — no extra is required. | |
| 57 | 110 | |
| 58 | 111 | ## Development install |
| 59 | 112 | |
| 60 | 113 | ```bash |
| 61 | 114 | git clone https://github.com/ConflictHQ/navegador |
| 62 | 115 | cd navegador |
| 63 | -pip install -e ".[sqlite,redis]" | |
| 116 | +pip install -e ".[sqlite,redis,languages,llm,dev]" | |
| 64 | 117 | ``` |
| 65 | 118 | |
| 66 | 119 | ## Upgrading |
| 67 | 120 | |
| 68 | 121 | ```bash |
| 69 | 122 | pip install --upgrade navegador |
| 70 | 123 | ``` |
| 71 | 124 | |
| 72 | -After upgrading, re-ingest any existing repos to pick up new parser features or schema changes: | |
| 125 | +After upgrading, run schema migrations first, then re-ingest to pick up new parser features: | |
| 73 | 126 | |
| 74 | 127 | ```bash |
| 75 | -navegador ingest ./repo --clear | |
| 128 | +navegador migrate # apply any schema changes from the new version | |
| 129 | +navegador ingest ./repo # re-ingest with incremental updates (preferred) | |
| 130 | +navegador ingest ./repo --clear # full rebuild if you prefer a clean slate | |
| 76 | 131 | ``` |
| 77 | 132 |
| --- docs/getting-started/installation.md | |
| +++ docs/getting-started/installation.md | |
| @@ -40,10 +40,38 @@ | |
| 40 | export NAVEGADOR_DB=redis://localhost:6379 |
| 41 | navegador ingest ./repo |
| 42 | ``` |
| 43 | |
| 44 | See [Configuration](configuration.md) for full Redis setup details. |
| 45 | |
| 46 | ## Verify |
| 47 | |
| 48 | ```bash |
| 49 | navegador --version |
| @@ -50,27 +78,54 @@ | |
| 50 | ``` |
| 51 | |
| 52 | Expected output: |
| 53 | |
| 54 | ``` |
| 55 | navegador, version 0.x.y |
| 56 | ``` |
| 57 | |
| 58 | ## Development install |
| 59 | |
| 60 | ```bash |
| 61 | git clone https://github.com/ConflictHQ/navegador |
| 62 | cd navegador |
| 63 | pip install -e ".[sqlite,redis]" |
| 64 | ``` |
| 65 | |
| 66 | ## Upgrading |
| 67 | |
| 68 | ```bash |
| 69 | pip install --upgrade navegador |
| 70 | ``` |
| 71 | |
| 72 | After upgrading, re-ingest any existing repos to pick up new parser features or schema changes: |
| 73 | |
| 74 | ```bash |
| 75 | navegador ingest ./repo --clear |
| 76 | ``` |
| 77 |
| --- docs/getting-started/installation.md | |
| +++ docs/getting-started/installation.md | |
| @@ -40,10 +40,38 @@ | |
| 40 | export NAVEGADOR_DB=redis://localhost:6379 |
| 41 | navegador ingest ./repo |
| 42 | ``` |
| 43 | |
| 44 | See [Configuration](configuration.md) for full Redis setup details. |
| 45 | |
| 46 | === "[languages]" |
| 47 | |
| 48 | Additional tree-sitter grammars for Kotlin, C#, PHP, Ruby, Swift, C, and C++. The default install includes Python, TypeScript, JavaScript, Go, Rust, and Java. |
| 49 | |
| 50 | ```bash |
| 51 | pip install "navegador[languages]" |
| 52 | ``` |
| 53 | |
| 54 | After installing, all 13 languages are parsed automatically by `navegador ingest`. No additional configuration is required. |
| 55 | |
| 56 | === "[llm]" |
| 57 | |
| 58 | LLM provider integrations for Anthropic, OpenAI, and Ollama. Required for `navegador ask`, `navegador docs`, and `navegador semantic-search`. |
| 59 | |
| 60 | ```bash |
| 61 | pip install "navegador[llm]" |
| 62 | ``` |
| 63 | |
| 64 | Configure the provider in `.navegador/config.toml` or via environment variables. See [Configuration](configuration.md) for details. |
| 65 | |
| 66 | === "all extras" |
| 67 | |
| 68 | Install everything at once: |
| 69 | |
| 70 | ```bash |
| 71 | pip install "navegador[sqlite,redis,languages,llm]" |
| 72 | ``` |
| 73 | |
| 74 | ## Verify |
| 75 | |
| 76 | ```bash |
| 77 | navegador --version |
| @@ -50,27 +78,54 @@ | |
| 78 | ``` |
| 79 | |
| 80 | Expected output: |
| 81 | |
| 82 | ``` |
| 83 | navegador, version 0.7.0 |
| 84 | ``` |
| 85 | |
| 86 | ## Shell completions |
| 87 | |
| 88 | Install shell completions for tab-completion of commands and flags: |
| 89 | |
| 90 | ```bash |
| 91 | navegador completions bash >> ~/.bashrc |
| 92 | navegador completions zsh >> ~/.zshrc |
| 93 | navegador completions fish > ~/.config/fish/completions/navegador.fish |
| 94 | ``` |
| 95 | |
| 96 | ## Python SDK |
| 97 | |
| 98 | The Python SDK wraps all CLI functionality in a single `Navegador` class: |
| 99 | |
| 100 | ```python |
| 101 | from navegador import Navegador |
| 102 | |
| 103 | nav = Navegador(".navegador/navegador.db") |
| 104 | nav.ingest("./src") |
| 105 | bundle = nav.explain("AuthService") |
| 106 | print(bundle.to_markdown()) |
| 107 | ``` |
| 108 | |
| 109 | The SDK is included in the base install — no extra is required. |
| 110 | |
| 111 | ## Development install |
| 112 | |
| 113 | ```bash |
| 114 | git clone https://github.com/ConflictHQ/navegador |
| 115 | cd navegador |
| 116 | pip install -e ".[sqlite,redis,languages,llm,dev]" |
| 117 | ``` |
| 118 | |
| 119 | ## Upgrading |
| 120 | |
| 121 | ```bash |
| 122 | pip install --upgrade navegador |
| 123 | ``` |
| 124 | |
| 125 | After upgrading, run schema migrations first, then re-ingest to pick up new parser features: |
| 126 | |
| 127 | ```bash |
| 128 | navegador migrate # apply any schema changes from the new version |
| 129 | navegador ingest ./repo # re-ingest with incremental updates (preferred) |
| 130 | navegador ingest ./repo --clear # full rebuild if you prefer a clean slate |
| 131 | ``` |
| 132 |
+70
-3
| --- docs/getting-started/quickstart.md | ||
| +++ docs/getting-started/quickstart.md | ||
| @@ -9,11 +9,11 @@ | ||
| 9 | 9 | ```bash |
| 10 | 10 | pip install navegador |
| 11 | 11 | navegador --version |
| 12 | 12 | ``` |
| 13 | 13 | |
| 14 | -Python 3.12+ is required. See [Installation](installation.md) for extras and Redis setup. | |
| 14 | +Python 3.12+ is required. For additional languages (Kotlin, C#, PHP, Ruby, Swift, C, C++) install the `[languages]` extra. See [Installation](installation.md) for all extras and Redis setup. | |
| 15 | 15 | |
| 16 | 16 | --- |
| 17 | 17 | |
| 18 | 18 | ## Step 2: Ingest a repo |
| 19 | 19 | |
| @@ -21,11 +21,23 @@ | ||
| 21 | 21 | |
| 22 | 22 | ```bash |
| 23 | 23 | navegador ingest ./my-repo |
| 24 | 24 | ``` |
| 25 | 25 | |
| 26 | -On first run this builds the graph from scratch. Re-run anytime to pick up changes. Use `--clear` to wipe and rebuild: | |
| 26 | +On first run this builds the graph from scratch. Re-run anytime to pick up changes. Use `--incremental` to skip files that haven't changed (based on content hashing — much faster on large repos): | |
| 27 | + | |
| 28 | +```bash | |
| 29 | +navegador ingest ./my-repo --incremental | |
| 30 | +``` | |
| 31 | + | |
| 32 | +Use `--watch` to keep the graph in sync as files change: | |
| 33 | + | |
| 34 | +```bash | |
| 35 | +navegador ingest ./my-repo --watch | |
| 36 | +``` | |
| 37 | + | |
| 38 | +Use `--clear` to wipe and rebuild from scratch: | |
| 27 | 39 | |
| 28 | 40 | ```bash |
| 29 | 41 | navegador ingest ./my-repo --clear |
| 30 | 42 | ``` |
| 31 | 43 | |
| @@ -33,11 +45,11 @@ | ||
| 33 | 45 | |
| 34 | 46 | ```bash |
| 35 | 47 | navegador ingest ./my-repo --json |
| 36 | 48 | ``` |
| 37 | 49 | |
| 38 | -Navegador walks the tree, parses every `.py` and `.ts`/`.tsx` file with tree-sitter, and writes nodes and edges for: files, modules, classes, functions, methods, imports, decorators, and call relationships. | |
| 50 | +Navegador walks the tree, parses source files in 13 languages with tree-sitter, and writes nodes and edges for: files, modules, classes, functions, methods, imports, decorators, and call relationships. Framework enrichers automatically detect and annotate Django models, FastAPI routes, React components, Rails controllers, Spring Boot beans, and more. | |
| 39 | 51 | |
| 40 | 52 | --- |
| 41 | 53 | |
| 42 | 54 | ## Step 3: Query the graph |
| 43 | 55 | |
| @@ -136,5 +148,60 @@ | ||
| 136 | 148 | | `--agent claude` | Install `.claude/hooks/claude-hook.py` | |
| 137 | 149 | | `--agent gemini` | Install `.gemini/hooks/gemini-hook.py` | |
| 138 | 150 | | `--agent openai` | Install `openai-hook.py` + `openai-tools.json` | |
| 139 | 151 | |
| 140 | 152 | After bootstrap, every file the agent edits triggers a re-ingest so the graph stays in sync. See [Agent Hooks](../guide/agent-hooks.md) for manual setup and the `NAVEGADOR.md` template. |
| 153 | + | |
| 154 | +--- | |
| 155 | + | |
| 156 | +## Step 6 (optional): SDK quick start | |
| 157 | + | |
| 158 | +All CLI functionality is available through the Python SDK: | |
| 159 | + | |
| 160 | +```python | |
| 161 | +from navegador import Navegador | |
| 162 | + | |
| 163 | +nav = Navegador(".navegador/navegador.db") | |
| 164 | + | |
| 165 | +# ingest (incremental by default in SDK) | |
| 166 | +nav.ingest("./my-repo", incremental=True) | |
| 167 | + | |
| 168 | +# query | |
| 169 | +bundle = nav.explain("AuthService") | |
| 170 | +print(bundle.to_markdown()) | |
| 171 | + | |
| 172 | +# analysis | |
| 173 | +impact = nav.impact("validate_token") | |
| 174 | +churn = nav.churn(days=30) | |
| 175 | +cycles = nav.cycles() | |
| 176 | +``` | |
| 177 | + | |
| 178 | +The `Navegador` class wraps `GraphStore`, `ContextLoader`, all ingesters, and the analysis commands into one interface. | |
| 179 | + | |
| 180 | +--- | |
| 181 | + | |
| 182 | +## Step 7 (optional): Code analysis | |
| 183 | + | |
| 184 | +Once the graph is populated, use the analysis commands to understand your codebase: | |
| 185 | + | |
| 186 | +```bash | |
| 187 | +# impact of changing a function | |
| 188 | +navegador impact validate_token | |
| 189 | + | |
| 190 | +# trace execution flow | |
| 191 | +navegador trace process_payment --depth 3 | |
| 192 | + | |
| 193 | +# find dead code | |
| 194 | +navegador deadcode | |
| 195 | + | |
| 196 | +# detect dependency cycles | |
| 197 | +navegador cycles | |
| 198 | + | |
| 199 | +# map tests to source files | |
| 200 | +navegador testmap | |
| 201 | + | |
| 202 | +# code churn (files that change most) | |
| 203 | +navegador churn --days 30 | |
| 204 | + | |
| 205 | +# diff — what changed between two refs | |
| 206 | +navegador diff HEAD~1 HEAD | |
| 207 | +``` | |
| 141 | 208 | |
| 142 | 209 | ADDED docs/guide/analysis.md |
| 143 | 210 | ADDED docs/guide/ci-cd.md |
| 144 | 211 | ADDED docs/guide/cluster.md |
| --- docs/getting-started/quickstart.md | |
| +++ docs/getting-started/quickstart.md | |
| @@ -9,11 +9,11 @@ | |
| 9 | ```bash |
| 10 | pip install navegador |
| 11 | navegador --version |
| 12 | ``` |
| 13 | |
| 14 | Python 3.12+ is required. See [Installation](installation.md) for extras and Redis setup. |
| 15 | |
| 16 | --- |
| 17 | |
| 18 | ## Step 2: Ingest a repo |
| 19 | |
| @@ -21,11 +21,23 @@ | |
| 21 | |
| 22 | ```bash |
| 23 | navegador ingest ./my-repo |
| 24 | ``` |
| 25 | |
| 26 | On first run this builds the graph from scratch. Re-run anytime to pick up changes. Use `--clear` to wipe and rebuild: |
| 27 | |
| 28 | ```bash |
| 29 | navegador ingest ./my-repo --clear |
| 30 | ``` |
| 31 | |
| @@ -33,11 +45,11 @@ | |
| 33 | |
| 34 | ```bash |
| 35 | navegador ingest ./my-repo --json |
| 36 | ``` |
| 37 | |
| 38 | Navegador walks the tree, parses every `.py` and `.ts`/`.tsx` file with tree-sitter, and writes nodes and edges for: files, modules, classes, functions, methods, imports, decorators, and call relationships. |
| 39 | |
| 40 | --- |
| 41 | |
| 42 | ## Step 3: Query the graph |
| 43 | |
| @@ -136,5 +148,60 @@ | |
| 136 | | `--agent claude` | Install `.claude/hooks/claude-hook.py` | |
| 137 | | `--agent gemini` | Install `.gemini/hooks/gemini-hook.py` | |
| 138 | | `--agent openai` | Install `openai-hook.py` + `openai-tools.json` | |
| 139 | |
| 140 | After bootstrap, every file the agent edits triggers a re-ingest so the graph stays in sync. See [Agent Hooks](../guide/agent-hooks.md) for manual setup and the `NAVEGADOR.md` template. |
| 141 | |
| 142 | DDED docs/guide/analysis.md |
| 143 | DDED docs/guide/ci-cd.md |
| 144 | DDED docs/guide/cluster.md |
| --- docs/getting-started/quickstart.md | |
| +++ docs/getting-started/quickstart.md | |
| @@ -9,11 +9,11 @@ | |
| 9 | ```bash |
| 10 | pip install navegador |
| 11 | navegador --version |
| 12 | ``` |
| 13 | |
| 14 | Python 3.12+ is required. For additional languages (Kotlin, C#, PHP, Ruby, Swift, C, C++) install the `[languages]` extra. See [Installation](installation.md) for all extras and Redis setup. |
| 15 | |
| 16 | --- |
| 17 | |
| 18 | ## Step 2: Ingest a repo |
| 19 | |
| @@ -21,11 +21,23 @@ | |
| 21 | |
| 22 | ```bash |
| 23 | navegador ingest ./my-repo |
| 24 | ``` |
| 25 | |
| 26 | On first run this builds the graph from scratch. Re-run anytime to pick up changes. Use `--incremental` to skip files that haven't changed (based on content hashing — much faster on large repos): |
| 27 | |
| 28 | ```bash |
| 29 | navegador ingest ./my-repo --incremental |
| 30 | ``` |
| 31 | |
| 32 | Use `--watch` to keep the graph in sync as files change: |
| 33 | |
| 34 | ```bash |
| 35 | navegador ingest ./my-repo --watch |
| 36 | ``` |
| 37 | |
| 38 | Use `--clear` to wipe and rebuild from scratch: |
| 39 | |
| 40 | ```bash |
| 41 | navegador ingest ./my-repo --clear |
| 42 | ``` |
| 43 | |
| @@ -33,11 +45,11 @@ | |
| 45 | |
| 46 | ```bash |
| 47 | navegador ingest ./my-repo --json |
| 48 | ``` |
| 49 | |
| 50 | Navegador walks the tree, parses source files in 13 languages with tree-sitter, and writes nodes and edges for: files, modules, classes, functions, methods, imports, decorators, and call relationships. Framework enrichers automatically detect and annotate Django models, FastAPI routes, React components, Rails controllers, Spring Boot beans, and more. |
| 51 | |
| 52 | --- |
| 53 | |
| 54 | ## Step 3: Query the graph |
| 55 | |
| @@ -136,5 +148,60 @@ | |
| 148 | | `--agent claude` | Install `.claude/hooks/claude-hook.py` | |
| 149 | | `--agent gemini` | Install `.gemini/hooks/gemini-hook.py` | |
| 150 | | `--agent openai` | Install `openai-hook.py` + `openai-tools.json` | |
| 151 | |
| 152 | After bootstrap, every file the agent edits triggers a re-ingest so the graph stays in sync. See [Agent Hooks](../guide/agent-hooks.md) for manual setup and the `NAVEGADOR.md` template. |
| 153 | |
| 154 | --- |
| 155 | |
| 156 | ## Step 6 (optional): SDK quick start |
| 157 | |
| 158 | All CLI functionality is available through the Python SDK: |
| 159 | |
| 160 | ```python |
| 161 | from navegador import Navegador |
| 162 | |
| 163 | nav = Navegador(".navegador/navegador.db") |
| 164 | |
| 165 | # ingest (incremental by default in SDK) |
| 166 | nav.ingest("./my-repo", incremental=True) |
| 167 | |
| 168 | # query |
| 169 | bundle = nav.explain("AuthService") |
| 170 | print(bundle.to_markdown()) |
| 171 | |
| 172 | # analysis |
| 173 | impact = nav.impact("validate_token") |
| 174 | churn = nav.churn(days=30) |
| 175 | cycles = nav.cycles() |
| 176 | ``` |
| 177 | |
| 178 | The `Navegador` class wraps `GraphStore`, `ContextLoader`, all ingesters, and the analysis commands into one interface. |
| 179 | |
| 180 | --- |
| 181 | |
| 182 | ## Step 7 (optional): Code analysis |
| 183 | |
| 184 | Once the graph is populated, use the analysis commands to understand your codebase: |
| 185 | |
| 186 | ```bash |
| 187 | # impact of changing a function |
| 188 | navegador impact validate_token |
| 189 | |
| 190 | # trace execution flow |
| 191 | navegador trace process_payment --depth 3 |
| 192 | |
| 193 | # find dead code |
| 194 | navegador deadcode |
| 195 | |
| 196 | # detect dependency cycles |
| 197 | navegador cycles |
| 198 | |
| 199 | # map tests to source files |
| 200 | navegador testmap |
| 201 | |
| 202 | # code churn (files that change most) |
| 203 | navegador churn --days 30 |
| 204 | |
| 205 | # diff — what changed between two refs |
| 206 | navegador diff HEAD~1 HEAD |
| 207 | ``` |
| 208 | |
| 209 | DDED docs/guide/analysis.md |
| 210 | DDED docs/guide/ci-cd.md |
| 211 | DDED docs/guide/cluster.md |
+287
| --- a/docs/guide/analysis.md | ||
| +++ b/docs/guide/analysis.md | ||
| @@ -0,0 +1,287 @@ | ||
| 1 | +# Structural Analysis | |
| 2 | + | |
| 3 | +Navegador's analysis commands answer questions about how code fits together: what breaks if this function changes, where does data flow, which code is never called, and which tests cover what. | |
| 4 | + | |
| 5 | +All analysis commands work against the live graph. Run `navegador ingest` first to populate it. | |
| 6 | + | |
| 7 | +--- | |
| 8 | + | |
| 9 | +## Impact analysis | |
| 10 | + | |
| 11 | +`navegador impact` traces the downstream effect of changing a function, class, or file. It follows `CALLS`, `INHERITS`, and `IMPORTS` edges to find everything that depends on the target — directly or transitively. | |
| 12 | + | |
| 13 | +```bash | |
| 14 | +navegador impact validate_token | |
| 15 | +navegador impact PaymentProcessor --depth 3 | |
| 16 | +navegador impact src/auth/service.py --format json | |
| 17 | +``` | |
| 18 | + | |
| 19 | +### Options | |
| 20 | + | |
| 21 | +| Flag | Effect | | |
| 22 | +|---|---| | |
| 23 | +| `--depth N` | How many hops to follow (default: unlimited) | | |
| 24 | +| `--format json` | Machine-readable output | | |
| 25 | +| `--include-tests` | Include test files in the impact set | | |
| 26 | + | |
| 27 | +### Output | |
| 28 | + | |
| 29 | +``` | |
| 30 | +validate_token (Function — src/auth/service.py:42) | |
| 31 | + Direct dependents (3): | |
| 32 | + check_permissions src/auth/permissions.py:18 | |
| 33 | + require_auth src/auth/decorators.py:7 | |
| 34 | + middleware_auth src/middleware/auth.py:31 | |
| 35 | + | |
| 36 | + Transitive dependents (11): | |
| 37 | + process_payment src/payments/processor.py:56 | |
| 38 | + create_order src/orders/service.py:23 | |
| 39 | + ... (8 more) | |
| 40 | + | |
| 41 | + Affected files (5): | |
| 42 | + src/auth/permissions.py | |
| 43 | + src/auth/decorators.py | |
| 44 | + src/middleware/auth.py | |
| 45 | + src/payments/processor.py | |
| 46 | + src/orders/service.py | |
| 47 | +``` | |
| 48 | + | |
| 49 | +### Use cases | |
| 50 | + | |
| 51 | +- Before refactoring: understand the blast radius before changing a shared utility | |
| 52 | +- Code review: verify a PR's changes are limited to the expected scope | |
| 53 | +- Dependency triage: identify high-fan-out functions that deserve extra test coverage | |
| 54 | + | |
| 55 | +--- | |
| 56 | + | |
| 57 | +## Flow tracing | |
| 58 | + | |
| 59 | +`navegador flow` traces the execution path from one function to another, returning every call chain that connects them. | |
| 60 | + | |
| 61 | +```bash | |
| 62 | +navegador flow create_order process_payment | |
| 63 | +navegador flow handle_request save_to_db --max-paths 5 | |
| 64 | +``` | |
| 65 | + | |
| 66 | +### Options | |
| 67 | + | |
| 68 | +| Flag | Effect | | |
| 69 | +|---|---| | |
| 70 | +| `--max-paths N` | Maximum number of paths to return (default: 3) | | |
| 71 | +| `--format json` | Machine-readable output | | |
| 72 | + | |
| 73 | +### Output | |
| 74 | + | |
| 75 | +``` | |
| 76 | +Paths from create_order to process_payment: | |
| 77 | + | |
| 78 | +Path 1 (3 hops): | |
| 79 | + create_order → validate_cart → charge_card → process_payment | |
| 80 | + | |
| 81 | +Path 2 (4 hops): | |
| 82 | + create_order → apply_discount → charge_card → process_payment | |
| 83 | +``` | |
| 84 | + | |
| 85 | +### Use cases | |
| 86 | + | |
| 87 | +- Debugging: find all code paths that reach a problematic function | |
| 88 | +- Security review: trace every path to a sensitive operation (e.g., `delete_user`, `transfer_funds`) | |
| 89 | +- Onboarding: understand how a high-level action maps to low-level implementation | |
| 90 | + | |
| 91 | +--- | |
| 92 | + | |
| 93 | +## Dead code detection | |
| 94 | + | |
| 95 | +`navegador dead-code` finds functions and classes that are never called, never imported, and not decorated as entry points. | |
| 96 | + | |
| 97 | +```bash | |
| 98 | +navegador dead-code ./src | |
| 99 | +navegador dead-code ./src --exclude-tests --format json | |
| 100 | +``` | |
| 101 | + | |
| 102 | +### Options | |
| 103 | + | |
| 104 | +| Flag | Effect | | |
| 105 | +|---|---| | |
| 106 | +| `--exclude-tests` | Skip test files | | |
| 107 | +| `--min-age-days N` | Only report code not called in the last N days (requires git history) | | |
| 108 | +| `--format json` | Machine-readable output | | |
| 109 | +| `--threshold N` | Minimum confidence score to report (0–100, default: 80) | | |
| 110 | + | |
| 111 | +### Output | |
| 112 | + | |
| 113 | +``` | |
| 114 | +Potentially dead code (12 items): | |
| 115 | + | |
| 116 | + [Function] legacy_hash_password src/auth/legacy.py:14 | |
| 117 | + [Function] _format_receipt_v1 src/payments/receipt.py:88 | |
| 118 | + [Class] OldPaymentAdapter src/payments/adapters.py:201 | |
| 119 | + ... | |
| 120 | +``` | |
| 121 | + | |
| 122 | +!!! note | |
| 123 | + Navegador performs static call graph analysis. Dynamic dispatch, `getattr`, and string-based imports are not traced. Review candidates before deleting them. | |
| 124 | + | |
| 125 | +### Use cases | |
| 126 | + | |
| 127 | +- Codebase cleanup: identify safe-to-delete code before a release | |
| 128 | +- Migration audits: find old adapter classes after a library upgrade | |
| 129 | + | |
| 130 | +--- | |
| 131 | + | |
| 132 | +## Cycle detection | |
| 133 | + | |
| 134 | +`navegador cycles` finds circular dependency chains in the call graph and import graph. | |
| 135 | + | |
| 136 | +```bash | |
| 137 | +navegador cycles ./src | |
| 138 | +navegador cycles ./src --type imports | |
| 139 | +navegador cycles ./src --type calls --format json | |
| 140 | +``` | |
| 141 | + | |
| 142 | +### Options | |
| 143 | + | |
| 144 | +| Flag | Effect | | |
| 145 | +|---|---| | |
| 146 | +| `--type calls` | Find circular call chains (default) | | |
| 147 | +| `--type imports` | Find circular import chains | | |
| 148 | +| `--type both` | Find both | | |
| 149 | +| `--min-length N` | Only report cycles with at least N nodes (default: 2) | | |
| 150 | +| `--format json` | Machine-readable output | | |
| 151 | + | |
| 152 | +### Output | |
| 153 | + | |
| 154 | +``` | |
| 155 | +Import cycles (2 found): | |
| 156 | + | |
| 157 | + Cycle 1 (length 3): | |
| 158 | + src/payments/processor.py | |
| 159 | + → src/payments/validators.py | |
| 160 | + → src/payments/utils.py | |
| 161 | + → src/payments/processor.py | |
| 162 | + | |
| 163 | + Cycle 2 (length 2): | |
| 164 | + src/auth/service.py | |
| 165 | + → src/auth/models.py | |
| 166 | + → src/auth/service.py | |
| 167 | +``` | |
| 168 | + | |
| 169 | +### Use cases | |
| 170 | + | |
| 171 | +- CI gate: fail builds that introduce new circular imports | |
| 172 | +- Refactoring prep: identify modules to split before a large restructure | |
| 173 | + | |
| 174 | +--- | |
| 175 | + | |
| 176 | +## Test mapping | |
| 177 | + | |
| 178 | +`navegador test-map` maps test functions to the production code they exercise, using call graph analysis. | |
| 179 | + | |
| 180 | +```bash | |
| 181 | +navegador test-map ./src ./tests | |
| 182 | +navegador test-map ./src ./tests --target process_payment | |
| 183 | +navegador test-map ./src ./tests --format json | |
| 184 | +``` | |
| 185 | + | |
| 186 | +### Options | |
| 187 | + | |
| 188 | +| Flag | Effect | | |
| 189 | +|---|---| | |
| 190 | +| `--target <name>` | Only show tests that cover a specific function | | |
| 191 | +| `--uncovered` | Show production functions with no covering tests | | |
| 192 | +| `--format json` | Machine-readable output | | |
| 193 | + | |
| 194 | +### Output | |
| 195 | + | |
| 196 | +``` | |
| 197 | +Test coverage map: | |
| 198 | + | |
| 199 | + process_payment (src/payments/processor.py:56) | |
| 200 | + tests/payments/test_processor.py::test_process_payment_success | |
| 201 | + tests/payments/test_processor.py::test_process_payment_duplicate | |
| 202 | + tests/integration/test_checkout.py::test_full_checkout_flow | |
| 203 | + | |
| 204 | + validate_token (src/auth/service.py:42) | |
| 205 | + tests/auth/test_service.py::test_validate_token_valid | |
| 206 | + tests/auth/test_service.py::test_validate_token_expired | |
| 207 | + | |
| 208 | +Uncovered functions (4): | |
| 209 | + legacy_hash_password src/auth/legacy.py:14 | |
| 210 | + _format_receipt_v1 src/payments/receipt.py:88 | |
| 211 | + ... | |
| 212 | +``` | |
| 213 | + | |
| 214 | +### Use cases | |
| 215 | + | |
| 216 | +- Coverage by semantics, not just lines: see which tests actually call a function | |
| 217 | +- Regression targeting: when a function changes, which tests should run? | |
| 218 | +- Review prep: check that new code has corresponding tests before merging | |
| 219 | + | |
| 220 | +--- | |
| 221 | + | |
| 222 | +## Combining analysis with knowledge | |
| 223 | + | |
| 224 | +All analysis commands understand the knowledge layer. Add `--include-knowledge` to see rules, concepts, and decisions linked to the affected nodes: | |
| 225 | + | |
| 226 | +```bash | |
| 227 | +navegador impact process_payment --include-knowledge | |
| 228 | +``` | |
| 229 | + | |
| 230 | +Output will include knowledge nodes like: | |
| 231 | + | |
| 232 | +``` | |
| 233 | + Governed by: | |
| 234 | + Rule: RequireIdempotencyKey (critical) | |
| 235 | + Concept: Idempotency | |
| 236 | + Decisions: | |
| 237 | + UseStripeForPayments (accepted, 2025-01-15) | |
| 238 | +``` | |
| 239 | + | |
| 240 | +--- | |
| 241 | + | |
| 242 | +## Python API | |
| 243 | + | |
| 244 | +```python | |
| 245 | +from navegador.graph import GraphStore | |
| 246 | +from navegador.analysis import ( | |
| 247 | + ImpactAnalyzer, | |
| 248 | + FlowTracer, | |
| 249 | + DeadCodeDetector, | |
| 250 | + CycleDetector, | |
| 251 | + TestMapper, | |
| 252 | +) | |
| 253 | + | |
| 254 | +store = GraphStore.sqlite(".navegador/navegador.db") | |
| 255 | + | |
| 256 | +# impact analysis | |
| 257 | +analyzer = ImpactAnalyzer(store) | |
| 258 | +result = analyzer.analyze("validate_token", depth=3) | |
| 259 | +print(result.direct_dependents) | |
| 260 | +print(result.transitive_dependents) | |
| 261 | + | |
| 262 | +# flow tracing | |
| 263 | +tracer = FlowTracer(store) | |
| 264 | +paths = tracer.trace("create_order", "process_payment", max_paths=5) | |
| 265 | +for path in paths: | |
| 266 | + print(" -> ".join(path.nodes)) | |
| 267 | + | |
| 268 | +# dead code | |
| 269 | +detector = DeadCodeDetector(store) | |
| 270 | +candidates = detector.find("./src", exclude_tests=True) | |
| 271 | +for item in candidates: | |
| 272 | + print(f"{item.label}: {item.name} {item.file}:{item.line}") | |
| 273 | + | |
| 274 | +# cycle detection | |
| 275 | +cycle_detector = CycleDetector(store) | |
| 276 | +cycles = cycle_detector.find_import_cycles("./src") | |
| 277 | +for cycle in cycles: | |
| 278 | + print(" -> ".join(cycle.path)) | |
| 279 | + | |
| 280 | +# test mapping | |
| 281 | +mapper = TestMapper(store) | |
| 282 | +coverage = mapper.map("./src", "./tests") | |
| 283 | +for fn, tests in coverage.items(): | |
| 284 | + print(f"{fn}: {len(tests)} tests") | |
| 285 | +``` | |
| 286 | + | |
| 287 | +See the [Analysis API reference](../api/analysis.md) for full method signatures. |
| --- a/docs/guide/analysis.md | |
| +++ b/docs/guide/analysis.md | |
| @@ -0,0 +1,287 @@ | |
| --- a/docs/guide/analysis.md | |
| +++ b/docs/guide/analysis.md | |
| @@ -0,0 +1,287 @@ | |
| 1 | # Structural Analysis |
| 2 | |
| 3 | Navegador's analysis commands answer questions about how code fits together: what breaks if this function changes, where does data flow, which code is never called, and which tests cover what. |
| 4 | |
| 5 | All analysis commands work against the live graph. Run `navegador ingest` first to populate it. |
| 6 | |
| 7 | --- |
| 8 | |
| 9 | ## Impact analysis |
| 10 | |
| 11 | `navegador impact` traces the downstream effect of changing a function, class, or file. It follows `CALLS`, `INHERITS`, and `IMPORTS` edges to find everything that depends on the target — directly or transitively. |
| 12 | |
| 13 | ```bash |
| 14 | navegador impact validate_token |
| 15 | navegador impact PaymentProcessor --depth 3 |
| 16 | navegador impact src/auth/service.py --format json |
| 17 | ``` |
| 18 | |
| 19 | ### Options |
| 20 | |
| 21 | | Flag | Effect | |
| 22 | |---|---| |
| 23 | | `--depth N` | How many hops to follow (default: unlimited) | |
| 24 | | `--format json` | Machine-readable output | |
| 25 | | `--include-tests` | Include test files in the impact set | |
| 26 | |
| 27 | ### Output |
| 28 | |
| 29 | ``` |
| 30 | validate_token (Function — src/auth/service.py:42) |
| 31 | Direct dependents (3): |
| 32 | check_permissions src/auth/permissions.py:18 |
| 33 | require_auth src/auth/decorators.py:7 |
| 34 | middleware_auth src/middleware/auth.py:31 |
| 35 | |
| 36 | Transitive dependents (11): |
| 37 | process_payment src/payments/processor.py:56 |
| 38 | create_order src/orders/service.py:23 |
| 39 | ... (8 more) |
| 40 | |
| 41 | Affected files (5): |
| 42 | src/auth/permissions.py |
| 43 | src/auth/decorators.py |
| 44 | src/middleware/auth.py |
| 45 | src/payments/processor.py |
| 46 | src/orders/service.py |
| 47 | ``` |
| 48 | |
| 49 | ### Use cases |
| 50 | |
| 51 | - Before refactoring: understand the blast radius before changing a shared utility |
| 52 | - Code review: verify a PR's changes are limited to the expected scope |
| 53 | - Dependency triage: identify high-fan-out functions that deserve extra test coverage |
| 54 | |
| 55 | --- |
| 56 | |
| 57 | ## Flow tracing |
| 58 | |
| 59 | `navegador flow` traces the execution path from one function to another, returning every call chain that connects them. |
| 60 | |
| 61 | ```bash |
| 62 | navegador flow create_order process_payment |
| 63 | navegador flow handle_request save_to_db --max-paths 5 |
| 64 | ``` |
| 65 | |
| 66 | ### Options |
| 67 | |
| 68 | | Flag | Effect | |
| 69 | |---|---| |
| 70 | | `--max-paths N` | Maximum number of paths to return (default: 3) | |
| 71 | | `--format json` | Machine-readable output | |
| 72 | |
| 73 | ### Output |
| 74 | |
| 75 | ``` |
| 76 | Paths from create_order to process_payment: |
| 77 | |
| 78 | Path 1 (3 hops): |
| 79 | create_order → validate_cart → charge_card → process_payment |
| 80 | |
| 81 | Path 2 (4 hops): |
| 82 | create_order → apply_discount → charge_card → process_payment |
| 83 | ``` |
| 84 | |
| 85 | ### Use cases |
| 86 | |
| 87 | - Debugging: find all code paths that reach a problematic function |
| 88 | - Security review: trace every path to a sensitive operation (e.g., `delete_user`, `transfer_funds`) |
| 89 | - Onboarding: understand how a high-level action maps to low-level implementation |
| 90 | |
| 91 | --- |
| 92 | |
| 93 | ## Dead code detection |
| 94 | |
| 95 | `navegador dead-code` finds functions and classes that are never called, never imported, and not decorated as entry points. |
| 96 | |
| 97 | ```bash |
| 98 | navegador dead-code ./src |
| 99 | navegador dead-code ./src --exclude-tests --format json |
| 100 | ``` |
| 101 | |
| 102 | ### Options |
| 103 | |
| 104 | | Flag | Effect | |
| 105 | |---|---| |
| 106 | | `--exclude-tests` | Skip test files | |
| 107 | | `--min-age-days N` | Only report code not called in the last N days (requires git history) | |
| 108 | | `--format json` | Machine-readable output | |
| 109 | | `--threshold N` | Minimum confidence score to report (0–100, default: 80) | |
| 110 | |
| 111 | ### Output |
| 112 | |
| 113 | ``` |
| 114 | Potentially dead code (12 items): |
| 115 | |
| 116 | [Function] legacy_hash_password src/auth/legacy.py:14 |
| 117 | [Function] _format_receipt_v1 src/payments/receipt.py:88 |
| 118 | [Class] OldPaymentAdapter src/payments/adapters.py:201 |
| 119 | ... |
| 120 | ``` |
| 121 | |
| 122 | !!! note |
| 123 | Navegador performs static call graph analysis. Dynamic dispatch, `getattr`, and string-based imports are not traced. Review candidates before deleting them. |
| 124 | |
| 125 | ### Use cases |
| 126 | |
| 127 | - Codebase cleanup: identify safe-to-delete code before a release |
| 128 | - Migration audits: find old adapter classes after a library upgrade |
| 129 | |
| 130 | --- |
| 131 | |
| 132 | ## Cycle detection |
| 133 | |
| 134 | `navegador cycles` finds circular dependency chains in the call graph and import graph. |
| 135 | |
| 136 | ```bash |
| 137 | navegador cycles ./src |
| 138 | navegador cycles ./src --type imports |
| 139 | navegador cycles ./src --type calls --format json |
| 140 | ``` |
| 141 | |
| 142 | ### Options |
| 143 | |
| 144 | | Flag | Effect | |
| 145 | |---|---| |
| 146 | | `--type calls` | Find circular call chains (default) | |
| 147 | | `--type imports` | Find circular import chains | |
| 148 | | `--type both` | Find both | |
| 149 | | `--min-length N` | Only report cycles with at least N nodes (default: 2) | |
| 150 | | `--format json` | Machine-readable output | |
| 151 | |
| 152 | ### Output |
| 153 | |
| 154 | ``` |
| 155 | Import cycles (2 found): |
| 156 | |
| 157 | Cycle 1 (length 3): |
| 158 | src/payments/processor.py |
| 159 | → src/payments/validators.py |
| 160 | → src/payments/utils.py |
| 161 | → src/payments/processor.py |
| 162 | |
| 163 | Cycle 2 (length 2): |
| 164 | src/auth/service.py |
| 165 | → src/auth/models.py |
| 166 | → src/auth/service.py |
| 167 | ``` |
| 168 | |
| 169 | ### Use cases |
| 170 | |
| 171 | - CI gate: fail builds that introduce new circular imports |
| 172 | - Refactoring prep: identify modules to split before a large restructure |
| 173 | |
| 174 | --- |
| 175 | |
| 176 | ## Test mapping |
| 177 | |
| 178 | `navegador test-map` maps test functions to the production code they exercise, using call graph analysis. |
| 179 | |
| 180 | ```bash |
| 181 | navegador test-map ./src ./tests |
| 182 | navegador test-map ./src ./tests --target process_payment |
| 183 | navegador test-map ./src ./tests --format json |
| 184 | ``` |
| 185 | |
| 186 | ### Options |
| 187 | |
| 188 | | Flag | Effect | |
| 189 | |---|---| |
| 190 | | `--target <name>` | Only show tests that cover a specific function | |
| 191 | | `--uncovered` | Show production functions with no covering tests | |
| 192 | | `--format json` | Machine-readable output | |
| 193 | |
| 194 | ### Output |
| 195 | |
| 196 | ``` |
| 197 | Test coverage map: |
| 198 | |
| 199 | process_payment (src/payments/processor.py:56) |
| 200 | tests/payments/test_processor.py::test_process_payment_success |
| 201 | tests/payments/test_processor.py::test_process_payment_duplicate |
| 202 | tests/integration/test_checkout.py::test_full_checkout_flow |
| 203 | |
| 204 | validate_token (src/auth/service.py:42) |
| 205 | tests/auth/test_service.py::test_validate_token_valid |
| 206 | tests/auth/test_service.py::test_validate_token_expired |
| 207 | |
| 208 | Uncovered functions (4): |
| 209 | legacy_hash_password src/auth/legacy.py:14 |
| 210 | _format_receipt_v1 src/payments/receipt.py:88 |
| 211 | ... |
| 212 | ``` |
| 213 | |
| 214 | ### Use cases |
| 215 | |
| 216 | - Coverage by semantics, not just lines: see which tests actually call a function |
| 217 | - Regression targeting: when a function changes, which tests should run? |
| 218 | - Review prep: check that new code has corresponding tests before merging |
| 219 | |
| 220 | --- |
| 221 | |
| 222 | ## Combining analysis with knowledge |
| 223 | |
| 224 | All analysis commands understand the knowledge layer. Add `--include-knowledge` to see rules, concepts, and decisions linked to the affected nodes: |
| 225 | |
| 226 | ```bash |
| 227 | navegador impact process_payment --include-knowledge |
| 228 | ``` |
| 229 | |
| 230 | Output will include knowledge nodes like: |
| 231 | |
| 232 | ``` |
| 233 | Governed by: |
| 234 | Rule: RequireIdempotencyKey (critical) |
| 235 | Concept: Idempotency |
| 236 | Decisions: |
| 237 | UseStripeForPayments (accepted, 2025-01-15) |
| 238 | ``` |
| 239 | |
| 240 | --- |
| 241 | |
| 242 | ## Python API |
| 243 | |
| 244 | ```python |
| 245 | from navegador.graph import GraphStore |
| 246 | from navegador.analysis import ( |
| 247 | ImpactAnalyzer, |
| 248 | FlowTracer, |
| 249 | DeadCodeDetector, |
| 250 | CycleDetector, |
| 251 | TestMapper, |
| 252 | ) |
| 253 | |
| 254 | store = GraphStore.sqlite(".navegador/navegador.db") |
| 255 | |
| 256 | # impact analysis |
| 257 | analyzer = ImpactAnalyzer(store) |
| 258 | result = analyzer.analyze("validate_token", depth=3) |
| 259 | print(result.direct_dependents) |
| 260 | print(result.transitive_dependents) |
| 261 | |
| 262 | # flow tracing |
| 263 | tracer = FlowTracer(store) |
| 264 | paths = tracer.trace("create_order", "process_payment", max_paths=5) |
| 265 | for path in paths: |
| 266 | print(" -> ".join(path.nodes)) |
| 267 | |
| 268 | # dead code |
| 269 | detector = DeadCodeDetector(store) |
| 270 | candidates = detector.find("./src", exclude_tests=True) |
| 271 | for item in candidates: |
| 272 | print(f"{item.label}: {item.name} {item.file}:{item.line}") |
| 273 | |
| 274 | # cycle detection |
| 275 | cycle_detector = CycleDetector(store) |
| 276 | cycles = cycle_detector.find_import_cycles("./src") |
| 277 | for cycle in cycles: |
| 278 | print(" -> ".join(cycle.path)) |
| 279 | |
| 280 | # test mapping |
| 281 | mapper = TestMapper(store) |
| 282 | coverage = mapper.map("./src", "./tests") |
| 283 | for fn, tests in coverage.items(): |
| 284 | print(f"{fn}: {len(tests)} tests") |
| 285 | ``` |
| 286 | |
| 287 | See the [Analysis API reference](../api/analysis.md) for full method signatures. |
+272
| --- a/docs/guide/ci-cd.md | ||
| +++ b/docs/guide/ci-cd.md | ||
| @@ -0,0 +1,272 @@ | ||
| 1 | +# CI/CD Integration | |
| 2 | + | |
| 3 | +Navegador's `ci` subcommand is designed for non-interactive use in pipelines. All CI commands emit structured output and use exit codes that CI systems understand. | |
| 4 | + | |
| 5 | +--- | |
| 6 | + | |
| 7 | +## CI commands | |
| 8 | + | |
| 9 | +### `navegador ci ingest` | |
| 10 | + | |
| 11 | +Ingest the repo and output a machine-readable summary. Exits non-zero on errors. | |
| 12 | + | |
| 13 | +```bash | |
| 14 | +navegador ci ingest ./src | |
| 15 | +``` | |
| 16 | + | |
| 17 | +JSON output (always on in CI mode): | |
| 18 | + | |
| 19 | +```json | |
| 20 | +{ | |
| 21 | + "status": "ok", | |
| 22 | + "nodes_created": 1240, | |
| 23 | + "nodes_updated": 38, | |
| 24 | + "edges_created": 4821, | |
| 25 | + "files_processed": 87, | |
| 26 | + "errors": [], | |
| 27 | + "duration_seconds": 4.2 | |
| 28 | +} | |
| 29 | +``` | |
| 30 | + | |
| 31 | +### `navegador ci stats` | |
| 32 | + | |
| 33 | +Print graph statistics as JSON. Use to track graph growth over time or assert a minimum coverage threshold. | |
| 34 | + | |
| 35 | +```bash | |
| 36 | +navegador ci stats | |
| 37 | +``` | |
| 38 | + | |
| 39 | +```json | |
| 40 | +{ | |
| 41 | + "repositories": 1, | |
| 42 | + "files": 87, | |
| 43 | + "classes": 143, | |
| 44 | + "functions": 891, | |
| 45 | + "methods": 412, | |
| 46 | + "concepts": 14, | |
| 47 | + "rules": 9, | |
| 48 | + "decisions": 6, | |
| 49 | + "total_edges": 4821 | |
| 50 | +} | |
| 51 | +``` | |
| 52 | + | |
| 53 | +### `navegador ci check` | |
| 54 | + | |
| 55 | +Run assertion checks against the graph. Exits non-zero if any check fails. | |
| 56 | + | |
| 57 | +```bash | |
| 58 | +navegador ci check | |
| 59 | +``` | |
| 60 | + | |
| 61 | +Checks run by default: | |
| 62 | + | |
| 63 | +| Check | Condition for failure | | |
| 64 | +|---|---| | |
| 65 | +| `no-cycles` | Circular import chains detected | | |
| 66 | +| `min-coverage` | Functions with no tests below threshold | | |
| 67 | +| `critical-rules` | Code violates a `critical`-severity rule | | |
| 68 | +| `dead-code` | High-confidence dead code above threshold | | |
| 69 | + | |
| 70 | +Configure checks in `navegador.toml`: | |
| 71 | + | |
| 72 | +```toml | |
| 73 | +[ci.checks] | |
| 74 | +no-cycles = true | |
| 75 | +min-coverage = 60 # percent of functions with tests | |
| 76 | +critical-rules = true | |
| 77 | +dead-code = false # disable dead-code check | |
| 78 | + | |
| 79 | +[ci.thresholds] | |
| 80 | +dead_code_max = 10 # fail if more than 10 dead-code candidates | |
| 81 | +uncovered_max_percent = 40 # fail if more than 40% of functions lack tests | |
| 82 | +``` | |
| 83 | + | |
| 84 | +### Running specific checks | |
| 85 | + | |
| 86 | +```bash | |
| 87 | +navegador ci check --only no-cycles | |
| 88 | +navegador ci check --only critical-rules,min-coverage | |
| 89 | +navegador ci check --skip dead-code | |
| 90 | +``` | |
| 91 | + | |
| 92 | +--- | |
| 93 | + | |
| 94 | +## Exit codes | |
| 95 | + | |
| 96 | +| Code | Meaning | | |
| 97 | +|---|---| | |
| 98 | +| `0` | Success — all checks passed | | |
| 99 | +| `1` | Check failure — one or more assertions failed | | |
| 100 | +| `2` | Ingest error — files could not be parsed (partial result) | | |
| 101 | +| `3` | Configuration error — bad flags or missing config | | |
| 102 | +| `4` | Connection error — cannot reach database or Redis | | |
| 103 | + | |
| 104 | +--- | |
| 105 | + | |
| 106 | +## GitHub Actions | |
| 107 | + | |
| 108 | +### Basic: ingest on push | |
| 109 | + | |
| 110 | +```yaml | |
| 111 | +# .github/workflows/navegador.yml | |
| 112 | +name: navegador | |
| 113 | + | |
| 114 | +on: | |
| 115 | + push: | |
| 116 | + branches: [main] | |
| 117 | + pull_request: | |
| 118 | + | |
| 119 | +jobs: | |
| 120 | + graph: | |
| 121 | + runs-on: ubuntu-latest | |
| 122 | + steps: | |
| 123 | + - uses: actions/checkout@v4 | |
| 124 | + | |
| 125 | + - name: Install navegador | |
| 126 | + run: pip install navegador | |
| 127 | + | |
| 128 | + - name: Ingest | |
| 129 | + run: navegador ci ingest ./src | |
| 130 | + | |
| 131 | + - name: Check | |
| 132 | + run: navegador ci check | |
| 133 | +``` | |
| 134 | + | |
| 135 | +### With graph caching | |
| 136 | + | |
| 137 | +Cache the SQLite database between runs to speed up incremental ingestion: | |
| 138 | + | |
| 139 | +```yaml | |
| 140 | + - name: Cache navegador graph | |
| 141 | + uses: actions/cache@v4 | |
| 142 | + with: | |
| 143 | + path: .navegador/navegador.db | |
| 144 | + key: navegador-${{ runner.os }}-${{ hashFiles('src/**') }} | |
| 145 | + restore-keys: navegador-${{ runner.os }}- | |
| 146 | + | |
| 147 | + - name: Ingest | |
| 148 | + run: navegador ci ingest ./src | |
| 149 | + | |
| 150 | + - name: Check | |
| 151 | + run: navegador ci check | |
| 152 | +``` | |
| 153 | + | |
| 154 | +### Shared graph via Redis (cluster mode) | |
| 155 | + | |
| 156 | +Use a shared Redis instance for team-wide graph persistence across branches and PRs: | |
| 157 | + | |
| 158 | +```yaml | |
| 159 | + - name: Ingest to shared graph | |
| 160 | + env: | |
| 161 | + NAVEGADOR_REDIS_URL: ${{ secrets.NAVEGADOR_REDIS_URL }} | |
| 162 | + run: | | |
| 163 | + navegador ci ingest ./src --cluster | |
| 164 | + navegador ci check --cluster | |
| 165 | +``` | |
| 166 | + | |
| 167 | +### PR impact report | |
| 168 | + | |
| 169 | +Post an impact analysis comment on pull requests: | |
| 170 | + | |
| 171 | +```yaml | |
| 172 | + - name: Impact analysis | |
| 173 | + if: github.event_name == 'pull_request' | |
| 174 | + run: | | |
| 175 | + CHANGED=$(git diff --name-only origin/main...HEAD | grep '\.py$' | head -20) | |
| 176 | + for f in $CHANGED; do | |
| 177 | + navegador ci ingest "$f" | |
| 178 | + done | |
| 179 | + navegador impact --changed-since origin/main --format json > impact.json | |
| 180 | + | |
| 181 | + - name: Comment impact | |
| 182 | + if: github.event_name == 'pull_request' | |
| 183 | + uses: actions/github-script@v7 | |
| 184 | + with: | |
| 185 | + script: | | |
| 186 | + const impact = require('./impact.json') | |
| 187 | + const body = `## Navegador impact analysis\n\n${impact.summary}` | |
| 188 | + github.rest.issues.createComment({ | |
| 189 | + issue_number: context.issue.number, | |
| 190 | + owner: context.repo.owner, | |
| 191 | + repo: context.repo.repo, | |
| 192 | + body | |
| 193 | + }) | |
| 194 | +``` | |
| 195 | + | |
| 196 | +--- | |
| 197 | + | |
| 198 | +## Editor integration | |
| 199 | + | |
| 200 | +### VS Code | |
| 201 | + | |
| 202 | +Install the [Navegador VS Code extension](https://marketplace.visualstudio.com/items?itemName=ConflictHQ.navegador) for inline context overlays and on-save re-ingest. | |
| 203 | + | |
| 204 | +Or configure a task in `.vscode/tasks.json` to run on save: | |
| 205 | + | |
| 206 | +```json | |
| 207 | +{ | |
| 208 | + "version": "2.0.0", | |
| 209 | + "tasks": [ | |
| 210 | + { | |
| 211 | + "label": "Navegador: re-ingest on save", | |
| 212 | + "type": "shell", | |
| 213 | + "command": "navegador ingest ${file}", | |
| 214 | + "group": "build", | |
| 215 | + "presentation": { | |
| 216 | + "reveal": "silent", | |
| 217 | + "panel": "shared" | |
| 218 | + }, | |
| 219 | + "runOptions": { | |
| 220 | + "runOn": "folderOpen" | |
| 221 | + } | |
| 222 | + } | |
| 223 | + ] | |
| 224 | +} | |
| 225 | +``` | |
| 226 | + | |
| 227 | +### Neovim | |
| 228 | + | |
| 229 | +Add a post-write autocmd to trigger incremental ingest: | |
| 230 | + | |
| 231 | +```lua | |
| 232 | +-- in your init.lua or a plugin config | |
| 233 | +vim.api.nvim_create_autocmd("BufWritePost", { | |
| 234 | + pattern = { "*.py", "*.ts", "*.tsx", "*.js" }, | |
| 235 | + callback = function(ev) | |
| 236 | + local file = ev.file | |
| 237 | + vim.fn.jobstart({ "navegador", "ingest", file }, { detach = true }) | |
| 238 | + end, | |
| 239 | +}) | |
| 240 | +``` | |
| 241 | + | |
| 242 | +### Pre-commit hook | |
| 243 | + | |
| 244 | +Run checks before committing: | |
| 245 | + | |
| 246 | +```yaml | |
| 247 | +# .pre-commit-config.yaml | |
| 248 | +repos: | |
| 249 | + - repo: local | |
| 250 | + hooks: | |
| 251 | + - id: navegador-check | |
| 252 | + name: Navegador graph checks | |
| 253 | + entry: navegador ci check --only no-cycles,critical-rules | |
| 254 | + language: system | |
| 255 | + pass_filenames: false | |
| 256 | + stages: [commit] | |
| 257 | +``` | |
| 258 | + | |
| 259 | +--- | |
| 260 | + | |
| 261 | +## Secrets and auth | |
| 262 | + | |
| 263 | +The graph database path and Redis URL should come from environment variables in CI, not from committed config: | |
| 264 | + | |
| 265 | +```bash | |
| 266 | +# CI environment variables | |
| 267 | +NAVEGADOR_DB=.navegador/navegador.db # SQLite path | |
| 268 | +NAVEGADOR_REDIS_URL=redis://... # Redis URL (cluster mode) | |
| 269 | +GITHUB_TOKEN=ghp_... # for wiki ingestion | |
| 270 | +``` | |
| 271 | + | |
| 272 | +Set these as [GitHub Actions secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) and reference them in your workflow with `${{ secrets.NAME }}`. |
| --- a/docs/guide/ci-cd.md | |
| +++ b/docs/guide/ci-cd.md | |
| @@ -0,0 +1,272 @@ | |
| --- a/docs/guide/ci-cd.md | |
| +++ b/docs/guide/ci-cd.md | |
| @@ -0,0 +1,272 @@ | |
| 1 | # CI/CD Integration |
| 2 | |
| 3 | Navegador's `ci` subcommand is designed for non-interactive use in pipelines. All CI commands emit structured output and use exit codes that CI systems understand. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## CI commands |
| 8 | |
| 9 | ### `navegador ci ingest` |
| 10 | |
| 11 | Ingest the repo and output a machine-readable summary. Exits non-zero on errors. |
| 12 | |
| 13 | ```bash |
| 14 | navegador ci ingest ./src |
| 15 | ``` |
| 16 | |
| 17 | JSON output (always on in CI mode): |
| 18 | |
| 19 | ```json |
| 20 | { |
| 21 | "status": "ok", |
| 22 | "nodes_created": 1240, |
| 23 | "nodes_updated": 38, |
| 24 | "edges_created": 4821, |
| 25 | "files_processed": 87, |
| 26 | "errors": [], |
| 27 | "duration_seconds": 4.2 |
| 28 | } |
| 29 | ``` |
| 30 | |
| 31 | ### `navegador ci stats` |
| 32 | |
| 33 | Print graph statistics as JSON. Use to track graph growth over time or assert a minimum coverage threshold. |
| 34 | |
| 35 | ```bash |
| 36 | navegador ci stats |
| 37 | ``` |
| 38 | |
| 39 | ```json |
| 40 | { |
| 41 | "repositories": 1, |
| 42 | "files": 87, |
| 43 | "classes": 143, |
| 44 | "functions": 891, |
| 45 | "methods": 412, |
| 46 | "concepts": 14, |
| 47 | "rules": 9, |
| 48 | "decisions": 6, |
| 49 | "total_edges": 4821 |
| 50 | } |
| 51 | ``` |
| 52 | |
| 53 | ### `navegador ci check` |
| 54 | |
| 55 | Run assertion checks against the graph. Exits non-zero if any check fails. |
| 56 | |
| 57 | ```bash |
| 58 | navegador ci check |
| 59 | ``` |
| 60 | |
| 61 | Checks run by default: |
| 62 | |
| 63 | | Check | Condition for failure | |
| 64 | |---|---| |
| 65 | | `no-cycles` | Circular import chains detected | |
| 66 | | `min-coverage` | Functions with no tests below threshold | |
| 67 | | `critical-rules` | Code violates a `critical`-severity rule | |
| 68 | | `dead-code` | High-confidence dead code above threshold | |
| 69 | |
| 70 | Configure checks in `navegador.toml`: |
| 71 | |
| 72 | ```toml |
| 73 | [ci.checks] |
| 74 | no-cycles = true |
| 75 | min-coverage = 60 # percent of functions with tests |
| 76 | critical-rules = true |
| 77 | dead-code = false # disable dead-code check |
| 78 | |
| 79 | [ci.thresholds] |
| 80 | dead_code_max = 10 # fail if more than 10 dead-code candidates |
| 81 | uncovered_max_percent = 40 # fail if more than 40% of functions lack tests |
| 82 | ``` |
| 83 | |
| 84 | ### Running specific checks |
| 85 | |
| 86 | ```bash |
| 87 | navegador ci check --only no-cycles |
| 88 | navegador ci check --only critical-rules,min-coverage |
| 89 | navegador ci check --skip dead-code |
| 90 | ``` |
| 91 | |
| 92 | --- |
| 93 | |
| 94 | ## Exit codes |
| 95 | |
| 96 | | Code | Meaning | |
| 97 | |---|---| |
| 98 | | `0` | Success — all checks passed | |
| 99 | | `1` | Check failure — one or more assertions failed | |
| 100 | | `2` | Ingest error — files could not be parsed (partial result) | |
| 101 | | `3` | Configuration error — bad flags or missing config | |
| 102 | | `4` | Connection error — cannot reach database or Redis | |
| 103 | |
| 104 | --- |
| 105 | |
| 106 | ## GitHub Actions |
| 107 | |
| 108 | ### Basic: ingest on push |
| 109 | |
| 110 | ```yaml |
| 111 | # .github/workflows/navegador.yml |
| 112 | name: navegador |
| 113 | |
| 114 | on: |
| 115 | push: |
| 116 | branches: [main] |
| 117 | pull_request: |
| 118 | |
| 119 | jobs: |
| 120 | graph: |
| 121 | runs-on: ubuntu-latest |
| 122 | steps: |
| 123 | - uses: actions/checkout@v4 |
| 124 | |
| 125 | - name: Install navegador |
| 126 | run: pip install navegador |
| 127 | |
| 128 | - name: Ingest |
| 129 | run: navegador ci ingest ./src |
| 130 | |
| 131 | - name: Check |
| 132 | run: navegador ci check |
| 133 | ``` |
| 134 | |
| 135 | ### With graph caching |
| 136 | |
| 137 | Cache the SQLite database between runs to speed up incremental ingestion: |
| 138 | |
| 139 | ```yaml |
| 140 | - name: Cache navegador graph |
| 141 | uses: actions/cache@v4 |
| 142 | with: |
| 143 | path: .navegador/navegador.db |
| 144 | key: navegador-${{ runner.os }}-${{ hashFiles('src/**') }} |
| 145 | restore-keys: navegador-${{ runner.os }}- |
| 146 | |
| 147 | - name: Ingest |
| 148 | run: navegador ci ingest ./src |
| 149 | |
| 150 | - name: Check |
| 151 | run: navegador ci check |
| 152 | ``` |
| 153 | |
| 154 | ### Shared graph via Redis (cluster mode) |
| 155 | |
| 156 | Use a shared Redis instance for team-wide graph persistence across branches and PRs: |
| 157 | |
| 158 | ```yaml |
| 159 | - name: Ingest to shared graph |
| 160 | env: |
| 161 | NAVEGADOR_REDIS_URL: ${{ secrets.NAVEGADOR_REDIS_URL }} |
| 162 | run: | |
| 163 | navegador ci ingest ./src --cluster |
| 164 | navegador ci check --cluster |
| 165 | ``` |
| 166 | |
| 167 | ### PR impact report |
| 168 | |
| 169 | Post an impact analysis comment on pull requests: |
| 170 | |
| 171 | ```yaml |
| 172 | - name: Impact analysis |
| 173 | if: github.event_name == 'pull_request' |
| 174 | run: | |
| 175 | CHANGED=$(git diff --name-only origin/main...HEAD | grep '\.py$' | head -20) |
| 176 | for f in $CHANGED; do |
| 177 | navegador ci ingest "$f" |
| 178 | done |
| 179 | navegador impact --changed-since origin/main --format json > impact.json |
| 180 | |
| 181 | - name: Comment impact |
| 182 | if: github.event_name == 'pull_request' |
| 183 | uses: actions/github-script@v7 |
| 184 | with: |
| 185 | script: | |
| 186 | const impact = require('./impact.json') |
| 187 | const body = `## Navegador impact analysis\n\n${impact.summary}` |
| 188 | github.rest.issues.createComment({ |
| 189 | issue_number: context.issue.number, |
| 190 | owner: context.repo.owner, |
| 191 | repo: context.repo.repo, |
| 192 | body |
| 193 | }) |
| 194 | ``` |
| 195 | |
| 196 | --- |
| 197 | |
| 198 | ## Editor integration |
| 199 | |
| 200 | ### VS Code |
| 201 | |
| 202 | Install the [Navegador VS Code extension](https://marketplace.visualstudio.com/items?itemName=ConflictHQ.navegador) for inline context overlays and on-save re-ingest. |
| 203 | |
| 204 | Or configure a task in `.vscode/tasks.json` to run on save: |
| 205 | |
| 206 | ```json |
| 207 | { |
| 208 | "version": "2.0.0", |
| 209 | "tasks": [ |
| 210 | { |
| 211 | "label": "Navegador: re-ingest on save", |
| 212 | "type": "shell", |
| 213 | "command": "navegador ingest ${file}", |
| 214 | "group": "build", |
| 215 | "presentation": { |
| 216 | "reveal": "silent", |
| 217 | "panel": "shared" |
| 218 | }, |
| 219 | "runOptions": { |
| 220 | "runOn": "folderOpen" |
| 221 | } |
| 222 | } |
| 223 | ] |
| 224 | } |
| 225 | ``` |
| 226 | |
| 227 | ### Neovim |
| 228 | |
| 229 | Add a post-write autocmd to trigger incremental ingest: |
| 230 | |
| 231 | ```lua |
| 232 | -- in your init.lua or a plugin config |
| 233 | vim.api.nvim_create_autocmd("BufWritePost", { |
| 234 | pattern = { "*.py", "*.ts", "*.tsx", "*.js" }, |
| 235 | callback = function(ev) |
| 236 | local file = ev.file |
| 237 | vim.fn.jobstart({ "navegador", "ingest", file }, { detach = true }) |
| 238 | end, |
| 239 | }) |
| 240 | ``` |
| 241 | |
| 242 | ### Pre-commit hook |
| 243 | |
| 244 | Run checks before committing: |
| 245 | |
| 246 | ```yaml |
| 247 | # .pre-commit-config.yaml |
| 248 | repos: |
| 249 | - repo: local |
| 250 | hooks: |
| 251 | - id: navegador-check |
| 252 | name: Navegador graph checks |
| 253 | entry: navegador ci check --only no-cycles,critical-rules |
| 254 | language: system |
| 255 | pass_filenames: false |
| 256 | stages: [commit] |
| 257 | ``` |
| 258 | |
| 259 | --- |
| 260 | |
| 261 | ## Secrets and auth |
| 262 | |
| 263 | The graph database path and Redis URL should come from environment variables in CI, not from committed config: |
| 264 | |
| 265 | ```bash |
| 266 | # CI environment variables |
| 267 | NAVEGADOR_DB=.navegador/navegador.db # SQLite path |
| 268 | NAVEGADOR_REDIS_URL=redis://... # Redis URL (cluster mode) |
| 269 | GITHUB_TOKEN=ghp_... # for wiki ingestion |
| 270 | ``` |
| 271 | |
| 272 | Set these as [GitHub Actions secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) and reference them in your workflow with `${{ secrets.NAME }}`. |
+280
| --- a/docs/guide/cluster.md | ||
| +++ b/docs/guide/cluster.md | ||
| @@ -0,0 +1,280 @@ | ||
| 1 | +# Cluster Mode | |
| 2 | + | |
| 3 | +Cluster mode lets multiple machines share a single navegador graph over Redis. Use it when your team runs large ingestion jobs, want shared context across agents and CI, or need partitioned work processing. | |
| 4 | + | |
| 5 | +--- | |
| 6 | + | |
| 7 | +## Prerequisites | |
| 8 | + | |
| 9 | +- Redis 7+ (or a managed Redis service — Upstash, Redis Cloud, etc.) | |
| 10 | +- `pip install "navegador[redis]"` | |
| 11 | + | |
| 12 | +--- | |
| 13 | + | |
| 14 | +## Setup | |
| 15 | + | |
| 16 | +### 1. Initialize cluster mode | |
| 17 | + | |
| 18 | +Point navegador at your Redis instance and run init: | |
| 19 | + | |
| 20 | +```bash | |
| 21 | +navegador init --cluster --redis redis://your-redis-host:6379 | |
| 22 | +``` | |
| 23 | + | |
| 24 | +This writes cluster config to `navegador.toml`: | |
| 25 | + | |
| 26 | +```toml | |
| 27 | +[cluster] | |
| 28 | +enabled = true | |
| 29 | +redis_url = "redis://your-redis-host:6379" | |
| 30 | +graph_name = "navegador" | |
| 31 | +node_id = "worker-1" # auto-generated; override with --node-id | |
| 32 | +``` | |
| 33 | + | |
| 34 | +### 2. Verify connectivity | |
| 35 | + | |
| 36 | +```bash | |
| 37 | +navegador cluster status | |
| 38 | +``` | |
| 39 | + | |
| 40 | +Output: | |
| 41 | + | |
| 42 | +``` | |
| 43 | +Cluster: connected | |
| 44 | +Redis: redis://your-redis-host:6379 | |
| 45 | +Graph: navegador (47,231 nodes, 189,043 edges) | |
| 46 | +Workers: 3 online (worker-1, worker-2, ci-runner-7) | |
| 47 | +Queue: 0 tasks pending | |
| 48 | +``` | |
| 49 | + | |
| 50 | +--- | |
| 51 | + | |
| 52 | +## Shared graph | |
| 53 | + | |
| 54 | +All cluster members read from and write to the same FalkorDB graph stored in Redis. Any ingestion or annotation from any node is immediately visible to all other nodes. | |
| 55 | + | |
| 56 | +```bash | |
| 57 | +# on any machine in the cluster | |
| 58 | +navegador ingest ./src | |
| 59 | + | |
| 60 | +# on any other machine — sees the result immediately | |
| 61 | +navegador explain AuthService | |
| 62 | +``` | |
| 63 | + | |
| 64 | +### Local snapshots | |
| 65 | + | |
| 66 | +To work offline or reduce Redis round-trips, snapshot the graph to a local SQLite file: | |
| 67 | + | |
| 68 | +```bash | |
| 69 | +# pull a snapshot from the shared graph | |
| 70 | +navegador cluster snapshot --pull .navegador/local.db | |
| 71 | + | |
| 72 | +# use the snapshot for queries | |
| 73 | +navegador --db .navegador/local.db explain AuthService | |
| 74 | + | |
| 75 | +# push local changes back to the shared graph | |
| 76 | +navegador cluster snapshot --push .navegador/local.db | |
| 77 | +``` | |
| 78 | + | |
| 79 | +Snapshots are point-in-time copies. They do not auto-sync. Use `--pull` to refresh and `--push` to merge back. | |
| 80 | + | |
| 81 | +--- | |
| 82 | + | |
| 83 | +## Task queue | |
| 84 | + | |
| 85 | +The cluster task queue distributes ingestion and analysis jobs across workers. Instead of running `navegador ingest` directly, submit a task: | |
| 86 | + | |
| 87 | +```bash | |
| 88 | +# submit an ingestion task | |
| 89 | +navegador cluster enqueue ingest ./src --clear | |
| 90 | + | |
| 91 | +# submit an analysis task | |
| 92 | +navegador cluster enqueue analyze impact validate_token | |
| 93 | + | |
| 94 | +# list pending and active tasks | |
| 95 | +navegador cluster queue | |
| 96 | +``` | |
| 97 | + | |
| 98 | +Workers pick up tasks from the queue automatically. See [work partitioning](#work-partitioning) for multi-worker ingestion. | |
| 99 | + | |
| 100 | +### Starting a worker | |
| 101 | + | |
| 102 | +```bash | |
| 103 | +navegador cluster worker start | |
| 104 | +``` | |
| 105 | + | |
| 106 | +The worker polls the queue and processes tasks. Run one worker per machine. | |
| 107 | + | |
| 108 | +```bash | |
| 109 | +# run in background | |
| 110 | +navegador cluster worker start --daemon | |
| 111 | + | |
| 112 | +# stop the worker | |
| 113 | +navegador cluster worker stop | |
| 114 | +``` | |
| 115 | + | |
| 116 | +--- | |
| 117 | + | |
| 118 | +## Work partitioning | |
| 119 | + | |
| 120 | +For large monorepos, split ingestion across multiple workers: | |
| 121 | + | |
| 122 | +```bash | |
| 123 | +# partition a directory across N workers | |
| 124 | +navegador cluster partition ./src --workers 4 | |
| 125 | + | |
| 126 | +# each worker then runs its assigned slice | |
| 127 | +navegador cluster worker start --partition 0 --of 4 # worker 0 | |
| 128 | +navegador cluster worker start --partition 1 --of 4 # worker 1 | |
| 129 | +# ... | |
| 130 | +``` | |
| 131 | + | |
| 132 | +Partitioning splits the file list across workers by file count. All workers write to the same shared graph. The final graph is the union of all partitions. | |
| 133 | + | |
| 134 | +### In CI | |
| 135 | + | |
| 136 | +```yaml | |
| 137 | +# .github/workflows/ingest.yml | |
| 138 | +jobs: | |
| 139 | + ingest: | |
| 140 | + strategy: | |
| 141 | + matrix: | |
| 142 | + partition: [0, 1, 2, 3] | |
| 143 | + steps: | |
| 144 | + - run: navegador cluster worker start --partition ${{ matrix.partition }} --of 4 --run-once | |
| 145 | +``` | |
| 146 | + | |
| 147 | +`--run-once` processes the current queue and exits rather than running as a daemon. | |
| 148 | + | |
| 149 | +--- | |
| 150 | + | |
| 151 | +## Sessions | |
| 152 | + | |
| 153 | +Sessions let multiple agents coordinate on the same task without interfering with each other. | |
| 154 | + | |
| 155 | +```bash | |
| 156 | +# start a session (returns a session ID) | |
| 157 | +SESSION=$(navegador cluster session start --name "feature/auth-refactor") | |
| 158 | +echo "Session: $SESSION" | |
| 159 | + | |
| 160 | +# run commands scoped to the session | |
| 161 | +navegador --session $SESSION ingest ./src/auth | |
| 162 | +navegador --session $SESSION explain AuthService | |
| 163 | + | |
| 164 | +# end the session | |
| 165 | +navegador cluster session end $SESSION | |
| 166 | +``` | |
| 167 | + | |
| 168 | +Sessions create a namespaced view of the graph. Writes within a session are visible to other session members but isolated from the main graph until committed. | |
| 169 | + | |
| 170 | +```bash | |
| 171 | +# commit session changes to the main graph | |
| 172 | +navegador cluster session commit $SESSION | |
| 173 | + | |
| 174 | +# discard session changes | |
| 175 | +navegador cluster session discard $SESSION | |
| 176 | +``` | |
| 177 | + | |
| 178 | +--- | |
| 179 | + | |
| 180 | +## Locking | |
| 181 | + | |
| 182 | +For writes that must not overlap (e.g., `--clear` ingest), navegador acquires a distributed lock: | |
| 183 | + | |
| 184 | +```bash | |
| 185 | +navegador ingest ./src --clear | |
| 186 | +# automatically acquires the graph write lock; other writers block until it releases | |
| 187 | +``` | |
| 188 | + | |
| 189 | +You can also acquire locks explicitly: | |
| 190 | + | |
| 191 | +```bash | |
| 192 | +# acquire a named lock | |
| 193 | +LOCK=$(navegador cluster lock acquire "ingest-lock" --ttl 300) | |
| 194 | + | |
| 195 | +# ... run your operations ... | |
| 196 | + | |
| 197 | +# release the lock | |
| 198 | +navegador cluster lock release $LOCK | |
| 199 | +``` | |
| 200 | + | |
| 201 | +Locks have a TTL (seconds) and release automatically if the holder crashes. | |
| 202 | + | |
| 203 | +--- | |
| 204 | + | |
| 205 | +## Messaging | |
| 206 | + | |
| 207 | +Workers and agents can exchange messages via the cluster bus: | |
| 208 | + | |
| 209 | +```bash | |
| 210 | +# publish a message to a channel | |
| 211 | +navegador cluster publish "ingest.complete" '{"repo": "myorg/myrepo", "nodes": 12450}' | |
| 212 | + | |
| 213 | +# subscribe to a channel (blocks; prints messages as they arrive) | |
| 214 | +navegador cluster subscribe "ingest.complete" | |
| 215 | +``` | |
| 216 | + | |
| 217 | +Useful for triggering downstream steps (e.g., notify agents that a fresh ingest is ready) without polling. | |
| 218 | + | |
| 219 | +--- | |
| 220 | + | |
| 221 | +## Observability | |
| 222 | + | |
| 223 | +### Cluster metrics | |
| 224 | + | |
| 225 | +```bash | |
| 226 | +navegador cluster metrics | |
| 227 | +``` | |
| 228 | + | |
| 229 | +Output: | |
| 230 | + | |
| 231 | +``` | |
| 232 | +Graph | |
| 233 | + Nodes: 47,231 | |
| 234 | + Edges: 189,043 | |
| 235 | + Last ingest: 2026-03-23T14:22:11Z (worker-2) | |
| 236 | + | |
| 237 | +Workers (3 online) | |
| 238 | + worker-1 idle last seen 4s ago | |
| 239 | + worker-2 idle last seen 2s ago | |
| 240 | + ci-runner-7 processing task: ingest ./src/payments | |
| 241 | + | |
| 242 | +Queue | |
| 243 | + Pending: 0 | |
| 244 | + Active: 1 | |
| 245 | + Completed: 847 (last 24h) | |
| 246 | + Failed: 2 (last 24h) | |
| 247 | +``` | |
| 248 | + | |
| 249 | +### Logs | |
| 250 | + | |
| 251 | +Workers emit structured JSON logs. Stream them: | |
| 252 | + | |
| 253 | +```bash | |
| 254 | +navegador cluster logs --follow | |
| 255 | +navegador cluster logs --worker worker-2 --since 1h | |
| 256 | +``` | |
| 257 | + | |
| 258 | +### Health check | |
| 259 | + | |
| 260 | +```bash | |
| 261 | +navegador cluster health | |
| 262 | +# exits 0 if healthy, 1 if degraded, 2 if unavailable | |
| 263 | +``` | |
| 264 | + | |
| 265 | +Suitable for use in load balancer health checks and PagerDuty integrations. | |
| 266 | + | |
| 267 | +--- | |
| 268 | + | |
| 269 | +## Configuration reference | |
| 270 | + | |
| 271 | +All cluster settings can be set in `navegador.toml` or as environment variables: | |
| 272 | + | |
| 273 | +| Setting | Env var | Default | Description | | |
| 274 | +|---|---|---|---| | |
| 275 | +| `redis_url` | `NAVEGADOR_REDIS_URL` | `redis://localhost:6379` | Redis connection URL | | |
| 276 | +| `graph_name` | `NAVEGADOR_GRAPH_NAME` | `navegador` | FalkorDB graph name | | |
| 277 | +| `node_id` | `NAVEGADOR_NODE_ID` | auto | Unique identifier for this worker | | |
| 278 | +| `lock_ttl` | `NAVEGADOR_LOCK_TTL` | `300` | Default lock TTL in seconds | | |
| 279 | +| `worker_poll_interval` | `NAVEGADOR_POLL_INTERVAL` | `2` | Queue poll interval in seconds | | |
| 280 | +| `snapshot_dir` | `NAVEGADOR_SNAPSHOT_DIR` | `.navegador/snapshots` | Local snapshot directory | |
| --- a/docs/guide/cluster.md | |
| +++ b/docs/guide/cluster.md | |
| @@ -0,0 +1,280 @@ | |
| --- a/docs/guide/cluster.md | |
| +++ b/docs/guide/cluster.md | |
| @@ -0,0 +1,280 @@ | |
| 1 | # Cluster Mode |
| 2 | |
| 3 | Cluster mode lets multiple machines share a single navegador graph over Redis. Use it when your team runs large ingestion jobs, want shared context across agents and CI, or need partitioned work processing. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## Prerequisites |
| 8 | |
| 9 | - Redis 7+ (or a managed Redis service — Upstash, Redis Cloud, etc.) |
| 10 | - `pip install "navegador[redis]"` |
| 11 | |
| 12 | --- |
| 13 | |
| 14 | ## Setup |
| 15 | |
| 16 | ### 1. Initialize cluster mode |
| 17 | |
| 18 | Point navegador at your Redis instance and run init: |
| 19 | |
| 20 | ```bash |
| 21 | navegador init --cluster --redis redis://your-redis-host:6379 |
| 22 | ``` |
| 23 | |
| 24 | This writes cluster config to `navegador.toml`: |
| 25 | |
| 26 | ```toml |
| 27 | [cluster] |
| 28 | enabled = true |
| 29 | redis_url = "redis://your-redis-host:6379" |
| 30 | graph_name = "navegador" |
| 31 | node_id = "worker-1" # auto-generated; override with --node-id |
| 32 | ``` |
| 33 | |
| 34 | ### 2. Verify connectivity |
| 35 | |
| 36 | ```bash |
| 37 | navegador cluster status |
| 38 | ``` |
| 39 | |
| 40 | Output: |
| 41 | |
| 42 | ``` |
| 43 | Cluster: connected |
| 44 | Redis: redis://your-redis-host:6379 |
| 45 | Graph: navegador (47,231 nodes, 189,043 edges) |
| 46 | Workers: 3 online (worker-1, worker-2, ci-runner-7) |
| 47 | Queue: 0 tasks pending |
| 48 | ``` |
| 49 | |
| 50 | --- |
| 51 | |
| 52 | ## Shared graph |
| 53 | |
| 54 | All cluster members read from and write to the same FalkorDB graph stored in Redis. Any ingestion or annotation from any node is immediately visible to all other nodes. |
| 55 | |
| 56 | ```bash |
| 57 | # on any machine in the cluster |
| 58 | navegador ingest ./src |
| 59 | |
| 60 | # on any other machine — sees the result immediately |
| 61 | navegador explain AuthService |
| 62 | ``` |
| 63 | |
| 64 | ### Local snapshots |
| 65 | |
| 66 | To work offline or reduce Redis round-trips, snapshot the graph to a local SQLite file: |
| 67 | |
| 68 | ```bash |
| 69 | # pull a snapshot from the shared graph |
| 70 | navegador cluster snapshot --pull .navegador/local.db |
| 71 | |
| 72 | # use the snapshot for queries |
| 73 | navegador --db .navegador/local.db explain AuthService |
| 74 | |
| 75 | # push local changes back to the shared graph |
| 76 | navegador cluster snapshot --push .navegador/local.db |
| 77 | ``` |
| 78 | |
| 79 | Snapshots are point-in-time copies. They do not auto-sync. Use `--pull` to refresh and `--push` to merge back. |
| 80 | |
| 81 | --- |
| 82 | |
| 83 | ## Task queue |
| 84 | |
| 85 | The cluster task queue distributes ingestion and analysis jobs across workers. Instead of running `navegador ingest` directly, submit a task: |
| 86 | |
| 87 | ```bash |
| 88 | # submit an ingestion task |
| 89 | navegador cluster enqueue ingest ./src --clear |
| 90 | |
| 91 | # submit an analysis task |
| 92 | navegador cluster enqueue analyze impact validate_token |
| 93 | |
| 94 | # list pending and active tasks |
| 95 | navegador cluster queue |
| 96 | ``` |
| 97 | |
| 98 | Workers pick up tasks from the queue automatically. See [work partitioning](#work-partitioning) for multi-worker ingestion. |
| 99 | |
| 100 | ### Starting a worker |
| 101 | |
| 102 | ```bash |
| 103 | navegador cluster worker start |
| 104 | ``` |
| 105 | |
| 106 | The worker polls the queue and processes tasks. Run one worker per machine. |
| 107 | |
| 108 | ```bash |
| 109 | # run in background |
| 110 | navegador cluster worker start --daemon |
| 111 | |
| 112 | # stop the worker |
| 113 | navegador cluster worker stop |
| 114 | ``` |
| 115 | |
| 116 | --- |
| 117 | |
| 118 | ## Work partitioning |
| 119 | |
| 120 | For large monorepos, split ingestion across multiple workers: |
| 121 | |
| 122 | ```bash |
| 123 | # partition a directory across N workers |
| 124 | navegador cluster partition ./src --workers 4 |
| 125 | |
| 126 | # each worker then runs its assigned slice |
| 127 | navegador cluster worker start --partition 0 --of 4 # worker 0 |
| 128 | navegador cluster worker start --partition 1 --of 4 # worker 1 |
| 129 | # ... |
| 130 | ``` |
| 131 | |
| 132 | Partitioning splits the file list across workers by file count. All workers write to the same shared graph. The final graph is the union of all partitions. |
| 133 | |
| 134 | ### In CI |
| 135 | |
| 136 | ```yaml |
| 137 | # .github/workflows/ingest.yml |
| 138 | jobs: |
| 139 | ingest: |
| 140 | strategy: |
| 141 | matrix: |
| 142 | partition: [0, 1, 2, 3] |
| 143 | steps: |
| 144 | - run: navegador cluster worker start --partition ${{ matrix.partition }} --of 4 --run-once |
| 145 | ``` |
| 146 | |
| 147 | `--run-once` processes the current queue and exits rather than running as a daemon. |
| 148 | |
| 149 | --- |
| 150 | |
| 151 | ## Sessions |
| 152 | |
| 153 | Sessions let multiple agents coordinate on the same task without interfering with each other. |
| 154 | |
| 155 | ```bash |
| 156 | # start a session (returns a session ID) |
| 157 | SESSION=$(navegador cluster session start --name "feature/auth-refactor") |
| 158 | echo "Session: $SESSION" |
| 159 | |
| 160 | # run commands scoped to the session |
| 161 | navegador --session $SESSION ingest ./src/auth |
| 162 | navegador --session $SESSION explain AuthService |
| 163 | |
| 164 | # end the session |
| 165 | navegador cluster session end $SESSION |
| 166 | ``` |
| 167 | |
| 168 | Sessions create a namespaced view of the graph. Writes within a session are visible to other session members but isolated from the main graph until committed. |
| 169 | |
| 170 | ```bash |
| 171 | # commit session changes to the main graph |
| 172 | navegador cluster session commit $SESSION |
| 173 | |
| 174 | # discard session changes |
| 175 | navegador cluster session discard $SESSION |
| 176 | ``` |
| 177 | |
| 178 | --- |
| 179 | |
| 180 | ## Locking |
| 181 | |
| 182 | For writes that must not overlap (e.g., `--clear` ingest), navegador acquires a distributed lock: |
| 183 | |
| 184 | ```bash |
| 185 | navegador ingest ./src --clear |
| 186 | # automatically acquires the graph write lock; other writers block until it releases |
| 187 | ``` |
| 188 | |
| 189 | You can also acquire locks explicitly: |
| 190 | |
| 191 | ```bash |
| 192 | # acquire a named lock |
| 193 | LOCK=$(navegador cluster lock acquire "ingest-lock" --ttl 300) |
| 194 | |
| 195 | # ... run your operations ... |
| 196 | |
| 197 | # release the lock |
| 198 | navegador cluster lock release $LOCK |
| 199 | ``` |
| 200 | |
| 201 | Locks have a TTL (seconds) and release automatically if the holder crashes. |
| 202 | |
| 203 | --- |
| 204 | |
| 205 | ## Messaging |
| 206 | |
| 207 | Workers and agents can exchange messages via the cluster bus: |
| 208 | |
| 209 | ```bash |
| 210 | # publish a message to a channel |
| 211 | navegador cluster publish "ingest.complete" '{"repo": "myorg/myrepo", "nodes": 12450}' |
| 212 | |
| 213 | # subscribe to a channel (blocks; prints messages as they arrive) |
| 214 | navegador cluster subscribe "ingest.complete" |
| 215 | ``` |
| 216 | |
| 217 | Useful for triggering downstream steps (e.g., notify agents that a fresh ingest is ready) without polling. |
| 218 | |
| 219 | --- |
| 220 | |
| 221 | ## Observability |
| 222 | |
| 223 | ### Cluster metrics |
| 224 | |
| 225 | ```bash |
| 226 | navegador cluster metrics |
| 227 | ``` |
| 228 | |
| 229 | Output: |
| 230 | |
| 231 | ``` |
| 232 | Graph |
| 233 | Nodes: 47,231 |
| 234 | Edges: 189,043 |
| 235 | Last ingest: 2026-03-23T14:22:11Z (worker-2) |
| 236 | |
| 237 | Workers (3 online) |
| 238 | worker-1 idle last seen 4s ago |
| 239 | worker-2 idle last seen 2s ago |
| 240 | ci-runner-7 processing task: ingest ./src/payments |
| 241 | |
| 242 | Queue |
| 243 | Pending: 0 |
| 244 | Active: 1 |
| 245 | Completed: 847 (last 24h) |
| 246 | Failed: 2 (last 24h) |
| 247 | ``` |
| 248 | |
| 249 | ### Logs |
| 250 | |
| 251 | Workers emit structured JSON logs. Stream them: |
| 252 | |
| 253 | ```bash |
| 254 | navegador cluster logs --follow |
| 255 | navegador cluster logs --worker worker-2 --since 1h |
| 256 | ``` |
| 257 | |
| 258 | ### Health check |
| 259 | |
| 260 | ```bash |
| 261 | navegador cluster health |
| 262 | # exits 0 if healthy, 1 if degraded, 2 if unavailable |
| 263 | ``` |
| 264 | |
| 265 | Suitable for use in load balancer health checks and PagerDuty integrations. |
| 266 | |
| 267 | --- |
| 268 | |
| 269 | ## Configuration reference |
| 270 | |
| 271 | All cluster settings can be set in `navegador.toml` or as environment variables: |
| 272 | |
| 273 | | Setting | Env var | Default | Description | |
| 274 | |---|---|---|---| |
| 275 | | `redis_url` | `NAVEGADOR_REDIS_URL` | `redis://localhost:6379` | Redis connection URL | |
| 276 | | `graph_name` | `NAVEGADOR_GRAPH_NAME` | `navegador` | FalkorDB graph name | |
| 277 | | `node_id` | `NAVEGADOR_NODE_ID` | auto | Unique identifier for this worker | |
| 278 | | `lock_ttl` | `NAVEGADOR_LOCK_TTL` | `300` | Default lock TTL in seconds | |
| 279 | | `worker_poll_interval` | `NAVEGADOR_POLL_INTERVAL` | `2` | Queue poll interval in seconds | |
| 280 | | `snapshot_dir` | `NAVEGADOR_SNAPSHOT_DIR` | `.navegador/snapshots` | Local snapshot directory | |
| --- docs/guide/context-loading.md | ||
| +++ docs/guide/context-loading.md | ||
| @@ -146,5 +146,168 @@ | ||
| 146 | 146 | navegador decorated pytest.mark.parametrize |
| 147 | 147 | navegador decorated --format json login_required |
| 148 | 148 | ``` |
| 149 | 149 | |
| 150 | 150 | Returns function/class nodes with their file paths, line numbers, and the full decorator expression. |
| 151 | + | |
| 152 | +--- | |
| 153 | + | |
| 154 | +## impact — blast radius analysis | |
| 155 | + | |
| 156 | +Return the set of code nodes that could be affected if a given node changes, traversing CALLS, IMPORTS, and INHERITS edges transitively. | |
| 157 | + | |
| 158 | +```bash | |
| 159 | +navegador impact validate_token | |
| 160 | +navegador impact validate_token --depth 3 | |
| 161 | +navegador impact validate_token --format json | |
| 162 | +``` | |
| 163 | + | |
| 164 | +Useful before a refactor to understand the blast radius. | |
| 165 | + | |
| 166 | +--- | |
| 167 | + | |
| 168 | +## trace — execution flow | |
| 169 | + | |
| 170 | +Trace the execution path through the call graph from a starting function: | |
| 171 | + | |
| 172 | +```bash | |
| 173 | +navegador trace process_payment | |
| 174 | +navegador trace process_payment --depth 4 --format json | |
| 175 | +``` | |
| 176 | + | |
| 177 | +Output shows the call chain as a tree, with each node annotated by file and line. | |
| 178 | + | |
| 179 | +--- | |
| 180 | + | |
| 181 | +## diff — graph diff between refs | |
| 182 | + | |
| 183 | +Show what changed in the graph between two Git refs: | |
| 184 | + | |
| 185 | +```bash | |
| 186 | +navegador diff HEAD~1 HEAD | |
| 187 | +navegador diff main feature-branch | |
| 188 | +``` | |
| 189 | + | |
| 190 | +Reports added, removed, and changed nodes and edges. | |
| 191 | + | |
| 192 | +--- | |
| 193 | + | |
| 194 | +## churn — code churn analysis | |
| 195 | + | |
| 196 | +Identify files and functions that change most frequently, based on Git history: | |
| 197 | + | |
| 198 | +```bash | |
| 199 | +navegador churn | |
| 200 | +navegador churn --days 30 | |
| 201 | +navegador churn --format json | |
| 202 | +``` | |
| 203 | + | |
| 204 | +High-churn nodes are often candidates for stabilization or better test coverage. | |
| 205 | + | |
| 206 | +--- | |
| 207 | + | |
| 208 | +## deadcode — find unreachable code | |
| 209 | + | |
| 210 | +Find functions and classes with no callers and no references from outside their defining file: | |
| 211 | + | |
| 212 | +```bash | |
| 213 | +navegador deadcode | |
| 214 | +navegador deadcode --format json | |
| 215 | +``` | |
| 216 | + | |
| 217 | +--- | |
| 218 | + | |
| 219 | +## cycles — dependency cycle detection | |
| 220 | + | |
| 221 | +Detect cycles in the IMPORTS and CALLS graphs: | |
| 222 | + | |
| 223 | +```bash | |
| 224 | +navegador cycles | |
| 225 | +navegador cycles --format json | |
| 226 | +``` | |
| 227 | + | |
| 228 | +Reports each cycle as an ordered list of node names. | |
| 229 | + | |
| 230 | +--- | |
| 231 | + | |
| 232 | +## testmap — test-to-source mapping | |
| 233 | + | |
| 234 | +Map test functions to the source functions they exercise (based on naming conventions and import analysis): | |
| 235 | + | |
| 236 | +```bash | |
| 237 | +navegador testmap | |
| 238 | +navegador testmap src/auth/service.py | |
| 239 | +navegador testmap --format json | |
| 240 | +``` | |
| 241 | + | |
| 242 | +Creates `TESTS` edges between test functions and their targets. | |
| 243 | + | |
| 244 | +--- | |
| 245 | + | |
| 246 | +## semantic-search — vector similarity search | |
| 247 | + | |
| 248 | +Search using natural language against embeddings of docstrings and code. Requires `pip install "navegador[llm]"`. | |
| 249 | + | |
| 250 | +```bash | |
| 251 | +navegador semantic-search "functions that validate user input" | |
| 252 | +navegador semantic-search "payment retry logic" --limit 10 | |
| 253 | +``` | |
| 254 | + | |
| 255 | +--- | |
| 256 | + | |
| 257 | +## ask — NLP query interface | |
| 258 | + | |
| 259 | +Ask a natural language question about the codebase. Requires `pip install "navegador[llm]"`. | |
| 260 | + | |
| 261 | +```bash | |
| 262 | +navegador ask "What handles authentication in this codebase?" | |
| 263 | +navegador ask "Which functions touch the database?" | |
| 264 | +``` | |
| 265 | + | |
| 266 | +The answer is grounded in graph queries — not hallucinated from code text. | |
| 267 | + | |
| 268 | +--- | |
| 269 | + | |
| 270 | +## rename — coordinated rename | |
| 271 | + | |
| 272 | +Rename a function or class across the graph and get a list of all files that reference the old name: | |
| 273 | + | |
| 274 | +```bash | |
| 275 | +navegador rename validate_token validate_access_token | |
| 276 | +``` | |
| 277 | + | |
| 278 | +Output is a structured change plan. The command does not modify source files — it produces the list of locations to update. | |
| 279 | + | |
| 280 | +--- | |
| 281 | + | |
| 282 | +## codeowners — ownership queries | |
| 283 | + | |
| 284 | +Query CODEOWNERS assignments and domain ownership: | |
| 285 | + | |
| 286 | +```bash | |
| 287 | +navegador codeowners src/auth/service.py | |
| 288 | +navegador codeowners AuthService | |
| 289 | +``` | |
| 290 | + | |
| 291 | +Returns owning teams and people from CODEOWNERS file and from `Person` nodes annotated to the matching code nodes. | |
| 292 | + | |
| 293 | +--- | |
| 294 | + | |
| 295 | +## communities — module cluster detection | |
| 296 | + | |
| 297 | +Detect communities of highly-coupled modules using graph clustering: | |
| 298 | + | |
| 299 | +```bash | |
| 300 | +navegador communities | |
| 301 | +navegador communities --format json | |
| 302 | +``` | |
| 303 | + | |
| 304 | +--- | |
| 305 | + | |
| 306 | +## explore — interactive graph explorer | |
| 307 | + | |
| 308 | +Open an interactive graph explorer in the terminal: | |
| 309 | + | |
| 310 | +```bash | |
| 311 | +navegador explore | |
| 312 | +navegador explore --start AuthService | |
| 313 | +``` | |
| 151 | 314 | |
| 152 | 315 | ADDED docs/guide/framework-enrichment.md |
| --- docs/guide/context-loading.md | |
| +++ docs/guide/context-loading.md | |
| @@ -146,5 +146,168 @@ | |
| 146 | navegador decorated pytest.mark.parametrize |
| 147 | navegador decorated --format json login_required |
| 148 | ``` |
| 149 | |
| 150 | Returns function/class nodes with their file paths, line numbers, and the full decorator expression. |
| 151 | |
| 152 | DDED docs/guide/framework-enrichment.md |
| --- docs/guide/context-loading.md | |
| +++ docs/guide/context-loading.md | |
| @@ -146,5 +146,168 @@ | |
| 146 | navegador decorated pytest.mark.parametrize |
| 147 | navegador decorated --format json login_required |
| 148 | ``` |
| 149 | |
| 150 | Returns function/class nodes with their file paths, line numbers, and the full decorator expression. |
| 151 | |
| 152 | --- |
| 153 | |
| 154 | ## impact — blast radius analysis |
| 155 | |
| 156 | Return the set of code nodes that could be affected if a given node changes, traversing CALLS, IMPORTS, and INHERITS edges transitively. |
| 157 | |
| 158 | ```bash |
| 159 | navegador impact validate_token |
| 160 | navegador impact validate_token --depth 3 |
| 161 | navegador impact validate_token --format json |
| 162 | ``` |
| 163 | |
| 164 | Useful before a refactor to understand the blast radius. |
| 165 | |
| 166 | --- |
| 167 | |
| 168 | ## trace — execution flow |
| 169 | |
| 170 | Trace the execution path through the call graph from a starting function: |
| 171 | |
| 172 | ```bash |
| 173 | navegador trace process_payment |
| 174 | navegador trace process_payment --depth 4 --format json |
| 175 | ``` |
| 176 | |
| 177 | Output shows the call chain as a tree, with each node annotated by file and line. |
| 178 | |
| 179 | --- |
| 180 | |
| 181 | ## diff — graph diff between refs |
| 182 | |
| 183 | Show what changed in the graph between two Git refs: |
| 184 | |
| 185 | ```bash |
| 186 | navegador diff HEAD~1 HEAD |
| 187 | navegador diff main feature-branch |
| 188 | ``` |
| 189 | |
| 190 | Reports added, removed, and changed nodes and edges. |
| 191 | |
| 192 | --- |
| 193 | |
| 194 | ## churn — code churn analysis |
| 195 | |
| 196 | Identify files and functions that change most frequently, based on Git history: |
| 197 | |
| 198 | ```bash |
| 199 | navegador churn |
| 200 | navegador churn --days 30 |
| 201 | navegador churn --format json |
| 202 | ``` |
| 203 | |
| 204 | High-churn nodes are often candidates for stabilization or better test coverage. |
| 205 | |
| 206 | --- |
| 207 | |
| 208 | ## deadcode — find unreachable code |
| 209 | |
| 210 | Find functions and classes with no callers and no references from outside their defining file: |
| 211 | |
| 212 | ```bash |
| 213 | navegador deadcode |
| 214 | navegador deadcode --format json |
| 215 | ``` |
| 216 | |
| 217 | --- |
| 218 | |
| 219 | ## cycles — dependency cycle detection |
| 220 | |
| 221 | Detect cycles in the IMPORTS and CALLS graphs: |
| 222 | |
| 223 | ```bash |
| 224 | navegador cycles |
| 225 | navegador cycles --format json |
| 226 | ``` |
| 227 | |
| 228 | Reports each cycle as an ordered list of node names. |
| 229 | |
| 230 | --- |
| 231 | |
| 232 | ## testmap — test-to-source mapping |
| 233 | |
| 234 | Map test functions to the source functions they exercise (based on naming conventions and import analysis): |
| 235 | |
| 236 | ```bash |
| 237 | navegador testmap |
| 238 | navegador testmap src/auth/service.py |
| 239 | navegador testmap --format json |
| 240 | ``` |
| 241 | |
| 242 | Creates `TESTS` edges between test functions and their targets. |
| 243 | |
| 244 | --- |
| 245 | |
| 246 | ## semantic-search — vector similarity search |
| 247 | |
| 248 | Search using natural language against embeddings of docstrings and code. Requires `pip install "navegador[llm]"`. |
| 249 | |
| 250 | ```bash |
| 251 | navegador semantic-search "functions that validate user input" |
| 252 | navegador semantic-search "payment retry logic" --limit 10 |
| 253 | ``` |
| 254 | |
| 255 | --- |
| 256 | |
| 257 | ## ask — NLP query interface |
| 258 | |
| 259 | Ask a natural language question about the codebase. Requires `pip install "navegador[llm]"`. |
| 260 | |
| 261 | ```bash |
| 262 | navegador ask "What handles authentication in this codebase?" |
| 263 | navegador ask "Which functions touch the database?" |
| 264 | ``` |
| 265 | |
| 266 | The answer is grounded in graph queries — not hallucinated from code text. |
| 267 | |
| 268 | --- |
| 269 | |
| 270 | ## rename — coordinated rename |
| 271 | |
| 272 | Rename a function or class across the graph and get a list of all files that reference the old name: |
| 273 | |
| 274 | ```bash |
| 275 | navegador rename validate_token validate_access_token |
| 276 | ``` |
| 277 | |
| 278 | Output is a structured change plan. The command does not modify source files — it produces the list of locations to update. |
| 279 | |
| 280 | --- |
| 281 | |
| 282 | ## codeowners — ownership queries |
| 283 | |
| 284 | Query CODEOWNERS assignments and domain ownership: |
| 285 | |
| 286 | ```bash |
| 287 | navegador codeowners src/auth/service.py |
| 288 | navegador codeowners AuthService |
| 289 | ``` |
| 290 | |
| 291 | Returns owning teams and people from CODEOWNERS file and from `Person` nodes annotated to the matching code nodes. |
| 292 | |
| 293 | --- |
| 294 | |
| 295 | ## communities — module cluster detection |
| 296 | |
| 297 | Detect communities of highly-coupled modules using graph clustering: |
| 298 | |
| 299 | ```bash |
| 300 | navegador communities |
| 301 | navegador communities --format json |
| 302 | ``` |
| 303 | |
| 304 | --- |
| 305 | |
| 306 | ## explore — interactive graph explorer |
| 307 | |
| 308 | Open an interactive graph explorer in the terminal: |
| 309 | |
| 310 | ```bash |
| 311 | navegador explore |
| 312 | navegador explore --start AuthService |
| 313 | ``` |
| 314 | |
| 315 | DDED docs/guide/framework-enrichment.md |
| --- a/docs/guide/framework-enrichment.md | ||
| +++ b/docs/guide/framework-enrichment.md | ||
| @@ -0,0 +1,51 @@ | ||
| 1 | +# Framework Enrichment | |
| 2 | + | |
| 3 | +## What enrichment does | |
| 4 | + | |
| 5 | +After ingestion, navegador's graph contains generic structural nodes: `Function`, `Class`, `File`, `Import`. Enrichment promotes those generic nodes to **semantic types** that reflect how the code is actually used. | |
| 6 | + | |
| 7 | +For example, a Django view function becomes a `View` node. A pytest function becomes a `Test` node. A Flask route decorator triggers creation of a `Route` node with the URL pattern extracted. | |
| 8 | + | |
| 9 | +This lets you ask questions that wouldn't be possible from structure alone: | |
| 10 | + | |
| 11 | +```bash | |
| 12 | +# without enrichment: grep for "def test_" | |
| 13 | +# with enrichment: query the graph by semantic type | |
| 14 | +navegador query "MATCH (t:Tting nodes and edges, applies framework-specific pattern matching (decorator names, base class names, naming conventions), and: | |
| 15 | + | |
| 16 | +1. Adds semantic labels to matched nodes (e.g., adds `View` label to Django view functions) | |
| 17 | +2. Creates typed edges where the framework implies relationships (e.g., `HANDLES` from a `Route` to its handler function) | |
| 18 | +3. Extracts framework-specific properties (e.g., HTTP method and URL pattern from route decorators) | |
| 19 | + | |
| 20 | +Enrichment is **non-destructive** — it never removes or modifies existing nodes, only adds labels and edges. | |
| 21 | + | |
| 22 | +--- | |
| 23 | + | |
| 24 | +## Supported frameworks | |
| 25 | + | |
| 26 | +| Framework | Language | Detected patterns | Semantic types added | | |
| 27 | +|---|---|---|---| | |
| 28 | +| Django | Python | `View` subclasses, `urlpatterns`, `@login_required` | `View`, `Route`, `Model`, `Form`, `Middleware` oute`, `MethodView` | `Route`, `View`, `Blueprint` | | |
| 29 | +| FastAPI | Python | `@router.get/post/put/delete/patch`, `APIRouter` | `Route`, `Schema`, `Dependency` | | |
| 30 | +| pytest | Python | `def test_*`, `@pytest.mark.*`, `conftest.py` | `Test`, `Fixture`, `TestSuite` | | |
| 31 | +| SQLAlchemy | Python | `Base` subclasses, `Column`, `relationship()` | `Model`, `Column`, `Relation` | | |
| 32 | +| Next.js | TypeScript | `pages/`, `app/`, `getServerSideProps` | `Page`, `Route`, `ServerComponent` | | |
| 33 | +| Express | JavaScript | `app.get/post/put/delete`, `Router` | `Route`, `Middleware` | | |
| 34 | +| NestJS | TypeScript | `@Controller`, `@Injectable`, `@Module` | `Controller`, `Service`, `Module` | | |
| 35 | +| **Terraform** | HCL | `main.tf`, `variables.tf`, `outputs.tf` | Cross-file module resolution, provider grouping | | |
| 36 | +| **Chef** | Ruby | `metadata.rb`, `Berksfile` | `chef_recipe`, `chef_resource`, `chef_cookbook`, `chef_include` | | |
| 37 | + | |
| 38 | +!!! note | |
| 39 | + Framework detection is automatic when `--framework auto` is used (the default). Navegador inspects imports and decorator patterns to identify which frameworks are present. | |
| 40 | + | |
| 41 | +--- | |
| 42 | + | |
| 43 | +## Usage | |
| 44 | + | |
| 45 | +### Auto-detect and enrich all frameworks | |
| 46 | + | |
| 47 | +```bash | |
| 48 | +navegador enrich ./src | |
| 49 | +``` | |
| 50 | + | |
| 51 | +This runs after ingestion and enriches everything it can detect |
| --- a/docs/guide/framework-enrichment.md | |
| +++ b/docs/guide/framework-enrichment.md | |
| @@ -0,0 +1,51 @@ | |
| --- a/docs/guide/framework-enrichment.md | |
| +++ b/docs/guide/framework-enrichment.md | |
| @@ -0,0 +1,51 @@ | |
| 1 | # Framework Enrichment |
| 2 | |
| 3 | ## What enrichment does |
| 4 | |
| 5 | After ingestion, navegador's graph contains generic structural nodes: `Function`, `Class`, `File`, `Import`. Enrichment promotes those generic nodes to **semantic types** that reflect how the code is actually used. |
| 6 | |
| 7 | For example, a Django view function becomes a `View` node. A pytest function becomes a `Test` node. A Flask route decorator triggers creation of a `Route` node with the URL pattern extracted. |
| 8 | |
| 9 | This lets you ask questions that wouldn't be possible from structure alone: |
| 10 | |
| 11 | ```bash |
| 12 | # without enrichment: grep for "def test_" |
| 13 | # with enrichment: query the graph by semantic type |
| 14 | navegador query "MATCH (t:Tting nodes and edges, applies framework-specific pattern matching (decorator names, base class names, naming conventions), and: |
| 15 | |
| 16 | 1. Adds semantic labels to matched nodes (e.g., adds `View` label to Django view functions) |
| 17 | 2. Creates typed edges where the framework implies relationships (e.g., `HANDLES` from a `Route` to its handler function) |
| 18 | 3. Extracts framework-specific properties (e.g., HTTP method and URL pattern from route decorators) |
| 19 | |
| 20 | Enrichment is **non-destructive** — it never removes or modifies existing nodes, only adds labels and edges. |
| 21 | |
| 22 | --- |
| 23 | |
| 24 | ## Supported frameworks |
| 25 | |
| 26 | | Framework | Language | Detected patterns | Semantic types added | |
| 27 | |---|---|---|---| |
| 28 | | Django | Python | `View` subclasses, `urlpatterns`, `@login_required` | `View`, `Route`, `Model`, `Form`, `Middleware` oute`, `MethodView` | `Route`, `View`, `Blueprint` | |
| 29 | | FastAPI | Python | `@router.get/post/put/delete/patch`, `APIRouter` | `Route`, `Schema`, `Dependency` | |
| 30 | | pytest | Python | `def test_*`, `@pytest.mark.*`, `conftest.py` | `Test`, `Fixture`, `TestSuite` | |
| 31 | | SQLAlchemy | Python | `Base` subclasses, `Column`, `relationship()` | `Model`, `Column`, `Relation` | |
| 32 | | Next.js | TypeScript | `pages/`, `app/`, `getServerSideProps` | `Page`, `Route`, `ServerComponent` | |
| 33 | | Express | JavaScript | `app.get/post/put/delete`, `Router` | `Route`, `Middleware` | |
| 34 | | NestJS | TypeScript | `@Controller`, `@Injectable`, `@Module` | `Controller`, `Service`, `Module` | |
| 35 | | **Terraform** | HCL | `main.tf`, `variables.tf`, `outputs.tf` | Cross-file module resolution, provider grouping | |
| 36 | | **Chef** | Ruby | `metadata.rb`, `Berksfile` | `chef_recipe`, `chef_resource`, `chef_cookbook`, `chef_include` | |
| 37 | |
| 38 | !!! note |
| 39 | Framework detection is automatic when `--framework auto` is used (the default). Navegador inspects imports and decorator patterns to identify which frameworks are present. |
| 40 | |
| 41 | --- |
| 42 | |
| 43 | ## Usage |
| 44 | |
| 45 | ### Auto-detect and enrich all frameworks |
| 46 | |
| 47 | ```bash |
| 48 | navegador enrich ./src |
| 49 | ``` |
| 50 | |
| 51 | This runs after ingestion and enriches everything it can detect |
+62
-9
| --- docs/guide/ingestion.md | ||
| +++ docs/guide/ingestion.md | ||
| @@ -12,18 +12,31 @@ | ||
| 12 | 12 | |
| 13 | 13 | ### What gets extracted |
| 14 | 14 | |
| 15 | 15 | Navegador walks all source files in the repo and uses tree-sitter to extract structure. Supported languages: |
| 16 | 16 | |
| 17 | -| Extension(s) | Language | | |
| 18 | -|---|---| | |
| 19 | -| `.py` | Python | | |
| 20 | -| `.ts`, `.tsx` | TypeScript | | |
| 21 | -| `.js`, `.jsx` | JavaScript | | |
| 22 | -| `.go` | Go | | |
| 23 | -| `.rs` | Rust | | |
| 24 | -| `.java` | Java | | |
| 17 | +| Extension(s) | Language | Extra | | |
| 18 | +|---|---|---| | |
| 19 | +| `.py` | Python | — | | |
| 20 | +| `.ts`, `.tsx` | TypeScript | — | | |
| 21 | +| `.js`, `.jsx` | JavaScript | — | | |
| 22 | +| `.go` | Go | — | | |
| 23 | +| `.rs` | Rust | — | | |
| 24 | +| `.java` | Java | — | | |
| 25 | +| `.kt`, `.kts` | Kotlin | `[languages]` | | |
| 26 | +| `.cs` | C# | `[languages]` | | |
| 27 | +| `.php` | PHP | `[languages]` | | |
| 28 | +| `.rb` | Ruby | `[languages]` | | |
| 29 | +| `.swift` | Swift | `[languages]` | | |
| 30 | +| `.c`, `.h` | C | `[languages]` | | |
| 31 | +| `.cpp`, `.cc`, `.cxx`, `.hpp` | C++ | `[languages]` | | |
| 32 | + | |
| 33 | +Install extended language support: | |
| 34 | + | |
| 35 | +```bash | |
| 36 | +pip install "navegador[languages]" | |
| 37 | +``` | |
| 25 | 38 | |
| 26 | 39 | The following directories are always skipped: `.git`, `.venv`, `venv`, `node_modules`, `__pycache__`, `dist`, `build`, `.next`, `target` (Rust/Java builds), `vendor` (Go modules), `.gradle`. |
| 27 | 40 | |
| 28 | 41 | ### What gets extracted |
| 29 | 42 | |
| @@ -41,16 +54,56 @@ | ||
| 41 | 54 | ### Options |
| 42 | 55 | |
| 43 | 56 | | Flag | Effect | |
| 44 | 57 | |---|---| |
| 45 | 58 | | `--clear` | Wipe the graph before ingesting (full rebuild) | |
| 59 | +| `--incremental` | Only reprocess files whose content hash has changed | | |
| 60 | +| `--watch` | Keep running and re-ingest on file changes | | |
| 61 | +| `--redact` | Strip secrets (tokens, passwords, keys) from string literals | | |
| 62 | +| `--monorepo` | Traverse workspace sub-packages (Turborepo, Nx, Yarn, pnpm, Cargo, Go) | | |
| 46 | 63 | | `--json` | Output a JSON summary of nodes and edges created | |
| 47 | 64 | | `--db <path>` | Use a specific database file | |
| 48 | 65 | |
| 49 | 66 | ### Re-ingesting |
| 50 | 67 | |
| 51 | -Re-run `navegador ingest` anytime to pick up changes. Nodes are upserted by identity (file path + name), so repeated ingestion is idempotent for unchanged nodes. Use `--clear` when you need a clean slate (e.g., after a large rename refactor). | |
| 68 | +Re-run `navegador ingest` anytime to pick up changes. Nodes are upserted by identity (file path + name), so repeated ingestion is idempotent for unchanged nodes. Use `--incremental` for large repos to skip unchanged files. Use `--clear` when you need a clean slate (e.g., after a large rename refactor). | |
| 69 | + | |
| 70 | +### Incremental ingestion | |
| 71 | + | |
| 72 | +`--incremental` uses SHA-256 content hashing to skip files that haven't changed since the last ingest. The hash is stored on each `File` node. On large repos this can reduce ingest time by 90%+ after the initial run. | |
| 73 | + | |
| 74 | +```bash | |
| 75 | +navegador ingest ./repo --incremental | |
| 76 | +``` | |
| 77 | + | |
| 78 | +### Watch mode | |
| 79 | + | |
| 80 | +`--watch` starts a file-system watcher and automatically re-ingests any file that changes: | |
| 81 | + | |
| 82 | +```bash | |
| 83 | +navegador ingest ./repo --watch | |
| 84 | +``` | |
| 85 | + | |
| 86 | +Press `Ctrl-C` to stop. Watch mode uses `--incremental` automatically. | |
| 87 | + | |
| 88 | +### Sensitive content redaction | |
| 89 | + | |
| 90 | +`--redact` scans string literals for patterns that look like API keys, tokens, and passwords, and replaces their values with `[REDACTED]` in the graph. Source files are never modified. | |
| 91 | + | |
| 92 | +```bash | |
| 93 | +navegador ingest ./repo --redact | |
| 94 | +``` | |
| 95 | + | |
| 96 | +### Monorepo support | |
| 97 | + | |
| 98 | +`--monorepo` detects the workspace type and traverses all sub-packages: | |
| 99 | + | |
| 100 | +```bash | |
| 101 | +navegador ingest ./monorepo --monorepo | |
| 102 | +``` | |
| 103 | + | |
| 104 | +Supported workspace formats: Turborepo, Nx, Yarn workspaces, pnpm workspaces, Cargo workspaces, Go workspaces. | |
| 52 | 105 | |
| 53 | 106 | --- |
| 54 | 107 | |
| 55 | 108 | ## Knowledge curation |
| 56 | 109 | |
| 57 | 110 | |
| 58 | 111 | ADDED docs/guide/intelligence.md |
| --- docs/guide/ingestion.md | |
| +++ docs/guide/ingestion.md | |
| @@ -12,18 +12,31 @@ | |
| 12 | |
| 13 | ### What gets extracted |
| 14 | |
| 15 | Navegador walks all source files in the repo and uses tree-sitter to extract structure. Supported languages: |
| 16 | |
| 17 | | Extension(s) | Language | |
| 18 | |---|---| |
| 19 | | `.py` | Python | |
| 20 | | `.ts`, `.tsx` | TypeScript | |
| 21 | | `.js`, `.jsx` | JavaScript | |
| 22 | | `.go` | Go | |
| 23 | | `.rs` | Rust | |
| 24 | | `.java` | Java | |
| 25 | |
| 26 | The following directories are always skipped: `.git`, `.venv`, `venv`, `node_modules`, `__pycache__`, `dist`, `build`, `.next`, `target` (Rust/Java builds), `vendor` (Go modules), `.gradle`. |
| 27 | |
| 28 | ### What gets extracted |
| 29 | |
| @@ -41,16 +54,56 @@ | |
| 41 | ### Options |
| 42 | |
| 43 | | Flag | Effect | |
| 44 | |---|---| |
| 45 | | `--clear` | Wipe the graph before ingesting (full rebuild) | |
| 46 | | `--json` | Output a JSON summary of nodes and edges created | |
| 47 | | `--db <path>` | Use a specific database file | |
| 48 | |
| 49 | ### Re-ingesting |
| 50 | |
| 51 | Re-run `navegador ingest` anytime to pick up changes. Nodes are upserted by identity (file path + name), so repeated ingestion is idempotent for unchanged nodes. Use `--clear` when you need a clean slate (e.g., after a large rename refactor). |
| 52 | |
| 53 | --- |
| 54 | |
| 55 | ## Knowledge curation |
| 56 | |
| 57 | |
| 58 | DDED docs/guide/intelligence.md |
| --- docs/guide/ingestion.md | |
| +++ docs/guide/ingestion.md | |
| @@ -12,18 +12,31 @@ | |
| 12 | |
| 13 | ### What gets extracted |
| 14 | |
| 15 | Navegador walks all source files in the repo and uses tree-sitter to extract structure. Supported languages: |
| 16 | |
| 17 | | Extension(s) | Language | Extra | |
| 18 | |---|---|---| |
| 19 | | `.py` | Python | — | |
| 20 | | `.ts`, `.tsx` | TypeScript | — | |
| 21 | | `.js`, `.jsx` | JavaScript | — | |
| 22 | | `.go` | Go | — | |
| 23 | | `.rs` | Rust | — | |
| 24 | | `.java` | Java | — | |
| 25 | | `.kt`, `.kts` | Kotlin | `[languages]` | |
| 26 | | `.cs` | C# | `[languages]` | |
| 27 | | `.php` | PHP | `[languages]` | |
| 28 | | `.rb` | Ruby | `[languages]` | |
| 29 | | `.swift` | Swift | `[languages]` | |
| 30 | | `.c`, `.h` | C | `[languages]` | |
| 31 | | `.cpp`, `.cc`, `.cxx`, `.hpp` | C++ | `[languages]` | |
| 32 | |
| 33 | Install extended language support: |
| 34 | |
| 35 | ```bash |
| 36 | pip install "navegador[languages]" |
| 37 | ``` |
| 38 | |
| 39 | The following directories are always skipped: `.git`, `.venv`, `venv`, `node_modules`, `__pycache__`, `dist`, `build`, `.next`, `target` (Rust/Java builds), `vendor` (Go modules), `.gradle`. |
| 40 | |
| 41 | ### What gets extracted |
| 42 | |
| @@ -41,16 +54,56 @@ | |
| 54 | ### Options |
| 55 | |
| 56 | | Flag | Effect | |
| 57 | |---|---| |
| 58 | | `--clear` | Wipe the graph before ingesting (full rebuild) | |
| 59 | | `--incremental` | Only reprocess files whose content hash has changed | |
| 60 | | `--watch` | Keep running and re-ingest on file changes | |
| 61 | | `--redact` | Strip secrets (tokens, passwords, keys) from string literals | |
| 62 | | `--monorepo` | Traverse workspace sub-packages (Turborepo, Nx, Yarn, pnpm, Cargo, Go) | |
| 63 | | `--json` | Output a JSON summary of nodes and edges created | |
| 64 | | `--db <path>` | Use a specific database file | |
| 65 | |
| 66 | ### Re-ingesting |
| 67 | |
| 68 | Re-run `navegador ingest` anytime to pick up changes. Nodes are upserted by identity (file path + name), so repeated ingestion is idempotent for unchanged nodes. Use `--incremental` for large repos to skip unchanged files. Use `--clear` when you need a clean slate (e.g., after a large rename refactor). |
| 69 | |
| 70 | ### Incremental ingestion |
| 71 | |
| 72 | `--incremental` uses SHA-256 content hashing to skip files that haven't changed since the last ingest. The hash is stored on each `File` node. On large repos this can reduce ingest time by 90%+ after the initial run. |
| 73 | |
| 74 | ```bash |
| 75 | navegador ingest ./repo --incremental |
| 76 | ``` |
| 77 | |
| 78 | ### Watch mode |
| 79 | |
| 80 | `--watch` starts a file-system watcher and automatically re-ingests any file that changes: |
| 81 | |
| 82 | ```bash |
| 83 | navegador ingest ./repo --watch |
| 84 | ``` |
| 85 | |
| 86 | Press `Ctrl-C` to stop. Watch mode uses `--incremental` automatically. |
| 87 | |
| 88 | ### Sensitive content redaction |
| 89 | |
| 90 | `--redact` scans string literals for patterns that look like API keys, tokens, and passwords, and replaces their values with `[REDACTED]` in the graph. Source files are never modified. |
| 91 | |
| 92 | ```bash |
| 93 | navegador ingest ./repo --redact |
| 94 | ``` |
| 95 | |
| 96 | ### Monorepo support |
| 97 | |
| 98 | `--monorepo` detects the workspace type and traverses all sub-packages: |
| 99 | |
| 100 | ```bash |
| 101 | navegador ingest ./monorepo --monorepo |
| 102 | ``` |
| 103 | |
| 104 | Supported workspace formats: Turborepo, Nx, Yarn workspaces, pnpm workspaces, Cargo workspaces, Go workspaces. |
| 105 | |
| 106 | --- |
| 107 | |
| 108 | ## Knowledge curation |
| 109 | |
| 110 | |
| 111 | DDED docs/guide/intelligence.md |
+267
| --- a/docs/guide/intelligence.md | ||
| +++ b/docs/guide/intelligence.md | ||
| @@ -0,0 +1,267 @@ | ||
| 1 | +# Intelligence Layer | |
| 2 | + | |
| 3 | +The intelligence layer adds capabilities that go beyond structural graph queries: semantic similarity search, community detection, natural language queries, and documentation generation. These features require an LLM provider or embedding model. | |
| 4 | + | |
| 5 | +--- | |
| 6 | + | |
| 7 | +## Setup | |
| 8 | + | |
| 9 | +Install the intelligence extras: | |
| 10 | + | |
| 11 | +```bash | |
| 12 | +pip install "navegador[intelligence]" | |
| 13 | +``` | |
| 14 | + | |
| 15 | +Configure your LLM provider: | |
| 16 | + | |
| 17 | +=== "OpenAI" | |
| 18 | + | |
| 19 | + ```bash | |
| 20 | + export NAVEGADOR_LLM_PROVIDER=openai | |
| 21 | + export OPENAI_API_KEY=sk-... | |
| 22 | + ``` | |
| 23 | + | |
| 24 | +=== "Anthropic" | |
| 25 | + | |
| 26 | + ```bash | |
| 27 | + export NAVEGADOR_LLM_PROVIDER=anthropic | |
| 28 | + export ANTHROPIC_API_KEY=sk-ant-... | |
| 29 | + ``` | |
| 30 | + | |
| 31 | +=== "Local (Ollama)" | |
| 32 | + | |
| 33 | + ```bash | |
| 34 | + export NAVEGADOR_LLM_PROVIDER=ollama | |
| 35 | + export NAVEGADOR_LLM_MODEL=llama3.2 | |
| 36 | + # no API key required; Ollama must be running on localhost:11434 | |
| 37 | + ``` | |
| 38 | + | |
| 39 | +Or set via `navegador.toml`: | |
| 40 | + | |
| 41 | +```toml | |
| 42 | +[intelligence] | |
| 43 | +provider = "openai" | |
| 44 | +model = "text-embedding-3-small" # embedding model | |
| 45 | +llm_model = "gpt-4o-mini" # generation model | |
| 46 | +``` | |
| 47 | + | |
| 48 | +--- | |
| 49 | + | |
| 50 | +## Semantic search | |
| 51 | + | |
| 52 | +Standard `navegador search` matches on names and text. Semantic search matches on **meaning**: a query for "billing" finds code about "invoices", "subscriptions", and "charges" even when those words don't appear in the query. | |
| 53 | + | |
| 54 | +```bash | |
| 55 | +navegador search "handle payment failure" --semantic | |
| 56 | +navegador search "retry with backoff" --semantic --limit 10 | |
| 57 | +navegador search "authentication middleware" --semantic --all | |
| 58 | +``` | |
| 59 | + | |
| 60 | +!!! note | |
| 61 | + First run after ingestion builds the embedding index. This may take a minute on large codebases. The index is cached in the database and updated incrementally on re-ingest. | |
| 62 | + | |
| 63 | +### Combining semantic and text search | |
| 64 | + | |
| 65 | +```bash | |
| 66 | +# semantic search scoped to a domain | |
| 67 | +navegador search "billing failure" --semantic --domain Payments | |
| 68 | + | |
| 69 | +# hybrid: semantic ranking with text pre-filter | |
| 70 | +navegador search "rate limit" --semantic --all | |
| 71 | +``` | |
| 72 | + | |
| 73 | +### Python API | |
| 74 | + | |
| 75 | +```python | |
| 76 | +from navegador.graph import GraphStore | |
| 77 | +from navegador.intelligence import SemanticSearch | |
| 78 | + | |
| 79 | +store = GraphStore.sqlite(".navegador/navegador.db") | |
| 80 | +search = SemanticSearch(store) | |
| 81 | + | |
| 82 | +results = search.query("handle payment failure", limit=10) | |
| 83 | +for node in results: | |
| 84 | + print(f"[{node.label}] {node.name} score={node.score:.3f}") | |
| 85 | +``` | |
| 86 | + | |
| 87 | +--- | |
| 88 | + | |
| 89 | +## Community detection | |
| 90 | + | |
| 91 | +Community detection groups nodes in the graph into clusters based on structural connectivity. Clusters typically correspond to logical modules or subsystems, even when the code doesn't explicitly organize itself that way. | |
| 92 | + | |
| 93 | +```bash | |
| 94 | +navegador intelligence communities | |
| 95 | +navegador intelligence communities --algorithm louvain | |
| 96 | +navegador intelligence communities --format json | |
| 97 | +``` | |
| 98 | + | |
| 99 | +### Algorithms | |
| 100 | + | |
| 101 | +| Algorithm | Best for | | |
| 102 | +|---|---| | |
| 103 | +| `louvain` (default) | Large codebases; fast and produces stable clusters | | |
| 104 | +| `leiden` | Higher modularity; slower | | |
| 105 | +| `label-propagation` | Very large graphs | | |
| 106 | + | |
| 107 | +### Output | |
| 108 | + | |
| 109 | +``` | |
| 110 | +Detected 6 communities: | |
| 111 | + | |
| 112 | + Community 1 (47 nodes) — suggested name: Auth | |
| 113 | + Core: AuthService, validate_token, JWTManager | |
| 114 | + Files: src/auth/ (12 files) | |
| 115 | + | |
| 116 | + Community 2 (38 nodes) — suggested name: Payments | |
| 117 | + Core: PaymentProcessor, charge_card, StripeClient | |
| 118 | + Files: src/payments/ (9 files) | |
| 119 | + | |
| 120 | + Community 3 (22 nodes) — suggested name: Orders | |
| 121 | + Core: OrderService, create_order, OrderRepository | |
| 122 | + Files: src/orders/ (6 files) | |
| 123 | + ... | |
| 124 | +``` | |
| 125 | + | |
| 126 | +Suggested names are generated by the LLM by inspecting the core nodes of each cluster. | |
| 127 | + | |
| 128 | +### Persisting communities | |
| 129 | + | |
| 130 | +```bash | |
| 131 | +navegador intelligence communities --save | |
| 132 | +``` | |
| 133 | + | |
| 134 | +This writes `Community` nodes and `MEMBER_OF` edges into the graph, making communities queryable: | |
| 135 | + | |
| 136 | +```bash | |
| 137 | +navegador query "MATCH (f:Function)-[:MEMBER_OF]->(c:Community) WHERE c.name = 'Payments' RETURN f.name, f.file" | |
| 138 | +``` | |
| 139 | + | |
| 140 | +--- | |
| 141 | + | |
| 142 | +## Natural language queries | |
| 143 | + | |
| 144 | +`navegador ask` lets you query the graph in plain English. The LLM translates your question into Cypher, executes it, and returns a natural language answer with citations. | |
| 145 | + | |
| 146 | +```bash | |
| 147 | +navegador ask "Which functions call process_payment?" | |
| 148 | +navegador ask "What rules govern the Payments domain?" | |
| 149 | +navegador ask "Show me all classes that inherit from BaseProcessor" | |
| 150 | +navegador ask "What decisions have been made about the database?" | |
| 151 | +``` | |
| 152 | + | |
| 153 | +### How it works | |
| 154 | + | |
| 155 | +1. Your question is embedded and matched against graph schema context | |
| 156 | +2. The LLM generates a Cypher query based on the question and schema | |
| 157 | +3. Navegador executes the query against the graph | |
| 158 | +4. The LLM formats the results as a natural language answer with source references | |
| 159 | + | |
| 160 | +### Safety | |
| 161 | + | |
| 162 | +Generated queries are run in read-only mode. Write operations (`CREATE`, `MERGE`, `DELETE`, `SET`) in generated queries are blocked. | |
| 163 | + | |
| 164 | +```bash | |
| 165 | +# show the generated Cypher without executing | |
| 166 | +navegador ask "Which functions have no tests?" --show-query | |
| 167 | + | |
| 168 | +# execute and show both Cypher and answer | |
| 169 | +navegador ask "Which functions have no tests?" --verbose | |
| 170 | +``` | |
| 171 | + | |
| 172 | +--- | |
| 173 | + | |
| 174 | +## Documentation generation | |
| 175 | + | |
| 176 | +`navegador docgen` generates or improves docstrings for undocumented functions and classes, using graph context to produce accurate, context-aware descriptions. | |
| 177 | + | |
| 178 | +```bash | |
| 179 | +# generate docs for all undocumented functions in a file | |
| 180 | +navegador docgen src/payments/processor.py | |
| 181 | + | |
| 182 | +# generate docs for a specific function | |
| 183 | +navegador docgen --target process_payment | |
| 184 | + | |
| 185 | +# dry run: show what would be generated without writing | |
| 186 | +navegador docgen src/payments/processor.py --dry-run | |
| 187 | + | |
| 188 | +# write directly to source files | |
| 189 | +navegador docgen src/payments/processor.py --write | |
| 190 | +``` | |
| 191 | + | |
| 192 | +### What it includes | |
| 193 | + | |
| 194 | +The generated docstring draws on: | |
| 195 | + | |
| 196 | +- The function's call graph (what it calls and what calls it) | |
| 197 | +- Related concepts and rules from the knowledge layer | |
| 198 | +- The class or module context | |
| 199 | +- Existing docstrings in the same file as style examples | |
| 200 | + | |
| 201 | +### Output | |
| 202 | + | |
| 203 | +```python | |
| 204 | +def process_payment(order_id: str, amount: Decimal, idempotency_key: str) -> PaymentResult: | |
| 205 | + """Process a payment for an order. | |
| 206 | + | |
| 207 | + Charges the card on file for the given order using the Stripe payment | |
| 208 | + processor. Requires an idempotency key to prevent double-charging on | |
| 209 | + client retries (see: RequireIdempotencyKey rule, UseStripeForPayments | |
| 210 | + decision). | |
| 211 | + | |
| 212 | + Args: | |
| 213 | + order_id: The unique identifier of the order to charge. | |
| 214 | + amount: The amount to charge in the order's currency. | |
| 215 | + idempotency_key: Client-supplied key for idempotent retries. | |
| 216 | + | |
| 217 | + Returns: | |
| 218 | + PaymentResult with charge_id and status. | |
| 219 | + | |
| 220 | + Raises: | |
| 221 | + PaymentError: If the charge fails or the card is declined. | |
| 222 | + DuplicatePaymentError: If a payment with this idempotency_key | |
| 223 | + already exists with a different amount. | |
| 224 | + """ | |
| 225 | +``` | |
| 226 | + | |
| 227 | +### Batch generation | |
| 228 | + | |
| 229 | +```bash | |
| 230 | +# generate docs for all undocumented functions in the repo | |
| 231 | +navegador docgen ./src --write --format google | |
| 232 | + | |
| 233 | +# formats: google (default), numpy, sphinx | |
| 234 | +``` | |
| 235 | + | |
| 236 | +!!! warning | |
| 237 | + `--write` modifies source files in place. Review changes with `--dry-run` first and commit before running with `--write` so you can diff and revert. | |
| 238 | + | |
| 239 | +--- | |
| 240 | + | |
| 241 | +## Python API | |
| 242 | + | |
| 243 | +```python | |
| 244 | +from navegador.graph import GraphStore | |
| 245 | +from navegador.intelligence import SemanticSearch, CommunityDetector, NaturalLanguageQuery, DocGenerator | |
| 246 | + | |
| 247 | +store = GraphStore.sqlite(".navegador/navegador.db") | |
| 248 | + | |
| 249 | +# semantic search | |
| 250 | +search = SemanticSearch(store) | |
| 251 | +results = search.query("retry logic", limit=5) | |
| 252 | + | |
| 253 | +# community detection | |
| 254 | +detector = CommunityDetector(store) | |
| 255 | +communities = detector.detect(algorithm="louvain", save=True) | |
| 256 | + | |
| 257 | +# natural language query | |
| 258 | +nlq = NaturalLanguageQuery(store) | |
| 259 | +answer = nlq.ask("What functions call process_payment?") | |
| 260 | +print(answer.text) | |
| 261 | +print(answer.cypher) # generated Cypher | |
| 262 | + | |
| 263 | +# doc generation | |
| 264 | +docgen = DocGenerator(store) | |
| 265 | +draft = docgen.generate_for_function("process_payment") | |
| 266 | +print(draft.docstring) | |
| 267 | +``` |
| --- a/docs/guide/intelligence.md | |
| +++ b/docs/guide/intelligence.md | |
| @@ -0,0 +1,267 @@ | |
| --- a/docs/guide/intelligence.md | |
| +++ b/docs/guide/intelligence.md | |
| @@ -0,0 +1,267 @@ | |
| 1 | # Intelligence Layer |
| 2 | |
| 3 | The intelligence layer adds capabilities that go beyond structural graph queries: semantic similarity search, community detection, natural language queries, and documentation generation. These features require an LLM provider or embedding model. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## Setup |
| 8 | |
| 9 | Install the intelligence extras: |
| 10 | |
| 11 | ```bash |
| 12 | pip install "navegador[intelligence]" |
| 13 | ``` |
| 14 | |
| 15 | Configure your LLM provider: |
| 16 | |
| 17 | === "OpenAI" |
| 18 | |
| 19 | ```bash |
| 20 | export NAVEGADOR_LLM_PROVIDER=openai |
| 21 | export OPENAI_API_KEY=sk-... |
| 22 | ``` |
| 23 | |
| 24 | === "Anthropic" |
| 25 | |
| 26 | ```bash |
| 27 | export NAVEGADOR_LLM_PROVIDER=anthropic |
| 28 | export ANTHROPIC_API_KEY=sk-ant-... |
| 29 | ``` |
| 30 | |
| 31 | === "Local (Ollama)" |
| 32 | |
| 33 | ```bash |
| 34 | export NAVEGADOR_LLM_PROVIDER=ollama |
| 35 | export NAVEGADOR_LLM_MODEL=llama3.2 |
| 36 | # no API key required; Ollama must be running on localhost:11434 |
| 37 | ``` |
| 38 | |
| 39 | Or set via `navegador.toml`: |
| 40 | |
| 41 | ```toml |
| 42 | [intelligence] |
| 43 | provider = "openai" |
| 44 | model = "text-embedding-3-small" # embedding model |
| 45 | llm_model = "gpt-4o-mini" # generation model |
| 46 | ``` |
| 47 | |
| 48 | --- |
| 49 | |
| 50 | ## Semantic search |
| 51 | |
| 52 | Standard `navegador search` matches on names and text. Semantic search matches on **meaning**: a query for "billing" finds code about "invoices", "subscriptions", and "charges" even when those words don't appear in the query. |
| 53 | |
| 54 | ```bash |
| 55 | navegador search "handle payment failure" --semantic |
| 56 | navegador search "retry with backoff" --semantic --limit 10 |
| 57 | navegador search "authentication middleware" --semantic --all |
| 58 | ``` |
| 59 | |
| 60 | !!! note |
| 61 | First run after ingestion builds the embedding index. This may take a minute on large codebases. The index is cached in the database and updated incrementally on re-ingest. |
| 62 | |
| 63 | ### Combining semantic and text search |
| 64 | |
| 65 | ```bash |
| 66 | # semantic search scoped to a domain |
| 67 | navegador search "billing failure" --semantic --domain Payments |
| 68 | |
| 69 | # hybrid: semantic ranking with text pre-filter |
| 70 | navegador search "rate limit" --semantic --all |
| 71 | ``` |
| 72 | |
| 73 | ### Python API |
| 74 | |
| 75 | ```python |
| 76 | from navegador.graph import GraphStore |
| 77 | from navegador.intelligence import SemanticSearch |
| 78 | |
| 79 | store = GraphStore.sqlite(".navegador/navegador.db") |
| 80 | search = SemanticSearch(store) |
| 81 | |
| 82 | results = search.query("handle payment failure", limit=10) |
| 83 | for node in results: |
| 84 | print(f"[{node.label}] {node.name} score={node.score:.3f}") |
| 85 | ``` |
| 86 | |
| 87 | --- |
| 88 | |
| 89 | ## Community detection |
| 90 | |
| 91 | Community detection groups nodes in the graph into clusters based on structural connectivity. Clusters typically correspond to logical modules or subsystems, even when the code doesn't explicitly organize itself that way. |
| 92 | |
| 93 | ```bash |
| 94 | navegador intelligence communities |
| 95 | navegador intelligence communities --algorithm louvain |
| 96 | navegador intelligence communities --format json |
| 97 | ``` |
| 98 | |
| 99 | ### Algorithms |
| 100 | |
| 101 | | Algorithm | Best for | |
| 102 | |---|---| |
| 103 | | `louvain` (default) | Large codebases; fast and produces stable clusters | |
| 104 | | `leiden` | Higher modularity; slower | |
| 105 | | `label-propagation` | Very large graphs | |
| 106 | |
| 107 | ### Output |
| 108 | |
| 109 | ``` |
| 110 | Detected 6 communities: |
| 111 | |
| 112 | Community 1 (47 nodes) — suggested name: Auth |
| 113 | Core: AuthService, validate_token, JWTManager |
| 114 | Files: src/auth/ (12 files) |
| 115 | |
| 116 | Community 2 (38 nodes) — suggested name: Payments |
| 117 | Core: PaymentProcessor, charge_card, StripeClient |
| 118 | Files: src/payments/ (9 files) |
| 119 | |
| 120 | Community 3 (22 nodes) — suggested name: Orders |
| 121 | Core: OrderService, create_order, OrderRepository |
| 122 | Files: src/orders/ (6 files) |
| 123 | ... |
| 124 | ``` |
| 125 | |
| 126 | Suggested names are generated by the LLM by inspecting the core nodes of each cluster. |
| 127 | |
| 128 | ### Persisting communities |
| 129 | |
| 130 | ```bash |
| 131 | navegador intelligence communities --save |
| 132 | ``` |
| 133 | |
| 134 | This writes `Community` nodes and `MEMBER_OF` edges into the graph, making communities queryable: |
| 135 | |
| 136 | ```bash |
| 137 | navegador query "MATCH (f:Function)-[:MEMBER_OF]->(c:Community) WHERE c.name = 'Payments' RETURN f.name, f.file" |
| 138 | ``` |
| 139 | |
| 140 | --- |
| 141 | |
| 142 | ## Natural language queries |
| 143 | |
| 144 | `navegador ask` lets you query the graph in plain English. The LLM translates your question into Cypher, executes it, and returns a natural language answer with citations. |
| 145 | |
| 146 | ```bash |
| 147 | navegador ask "Which functions call process_payment?" |
| 148 | navegador ask "What rules govern the Payments domain?" |
| 149 | navegador ask "Show me all classes that inherit from BaseProcessor" |
| 150 | navegador ask "What decisions have been made about the database?" |
| 151 | ``` |
| 152 | |
| 153 | ### How it works |
| 154 | |
| 155 | 1. Your question is embedded and matched against graph schema context |
| 156 | 2. The LLM generates a Cypher query based on the question and schema |
| 157 | 3. Navegador executes the query against the graph |
| 158 | 4. The LLM formats the results as a natural language answer with source references |
| 159 | |
| 160 | ### Safety |
| 161 | |
| 162 | Generated queries are run in read-only mode. Write operations (`CREATE`, `MERGE`, `DELETE`, `SET`) in generated queries are blocked. |
| 163 | |
| 164 | ```bash |
| 165 | # show the generated Cypher without executing |
| 166 | navegador ask "Which functions have no tests?" --show-query |
| 167 | |
| 168 | # execute and show both Cypher and answer |
| 169 | navegador ask "Which functions have no tests?" --verbose |
| 170 | ``` |
| 171 | |
| 172 | --- |
| 173 | |
| 174 | ## Documentation generation |
| 175 | |
| 176 | `navegador docgen` generates or improves docstrings for undocumented functions and classes, using graph context to produce accurate, context-aware descriptions. |
| 177 | |
| 178 | ```bash |
| 179 | # generate docs for all undocumented functions in a file |
| 180 | navegador docgen src/payments/processor.py |
| 181 | |
| 182 | # generate docs for a specific function |
| 183 | navegador docgen --target process_payment |
| 184 | |
| 185 | # dry run: show what would be generated without writing |
| 186 | navegador docgen src/payments/processor.py --dry-run |
| 187 | |
| 188 | # write directly to source files |
| 189 | navegador docgen src/payments/processor.py --write |
| 190 | ``` |
| 191 | |
| 192 | ### What it includes |
| 193 | |
| 194 | The generated docstring draws on: |
| 195 | |
| 196 | - The function's call graph (what it calls and what calls it) |
| 197 | - Related concepts and rules from the knowledge layer |
| 198 | - The class or module context |
| 199 | - Existing docstrings in the same file as style examples |
| 200 | |
| 201 | ### Output |
| 202 | |
| 203 | ```python |
| 204 | def process_payment(order_id: str, amount: Decimal, idempotency_key: str) -> PaymentResult: |
| 205 | """Process a payment for an order. |
| 206 | |
| 207 | Charges the card on file for the given order using the Stripe payment |
| 208 | processor. Requires an idempotency key to prevent double-charging on |
| 209 | client retries (see: RequireIdempotencyKey rule, UseStripeForPayments |
| 210 | decision). |
| 211 | |
| 212 | Args: |
| 213 | order_id: The unique identifier of the order to charge. |
| 214 | amount: The amount to charge in the order's currency. |
| 215 | idempotency_key: Client-supplied key for idempotent retries. |
| 216 | |
| 217 | Returns: |
| 218 | PaymentResult with charge_id and status. |
| 219 | |
| 220 | Raises: |
| 221 | PaymentError: If the charge fails or the card is declined. |
| 222 | DuplicatePaymentError: If a payment with this idempotency_key |
| 223 | already exists with a different amount. |
| 224 | """ |
| 225 | ``` |
| 226 | |
| 227 | ### Batch generation |
| 228 | |
| 229 | ```bash |
| 230 | # generate docs for all undocumented functions in the repo |
| 231 | navegador docgen ./src --write --format google |
| 232 | |
| 233 | # formats: google (default), numpy, sphinx |
| 234 | ``` |
| 235 | |
| 236 | !!! warning |
| 237 | `--write` modifies source files in place. Review changes with `--dry-run` first and commit before running with `--write` so you can diff and revert. |
| 238 | |
| 239 | --- |
| 240 | |
| 241 | ## Python API |
| 242 | |
| 243 | ```python |
| 244 | from navegador.graph import GraphStore |
| 245 | from navegador.intelligence import SemanticSearch, CommunityDetector, NaturalLanguageQuery, DocGenerator |
| 246 | |
| 247 | store = GraphStore.sqlite(".navegador/navegador.db") |
| 248 | |
| 249 | # semantic search |
| 250 | search = SemanticSearch(store) |
| 251 | results = search.query("retry logic", limit=5) |
| 252 | |
| 253 | # community detection |
| 254 | detector = CommunityDetector(store) |
| 255 | communities = detector.detect(algorithm="louvain", save=True) |
| 256 | |
| 257 | # natural language query |
| 258 | nlq = NaturalLanguageQuery(store) |
| 259 | answer = nlq.ask("What functions call process_payment?") |
| 260 | print(answer.text) |
| 261 | print(answer.cypher) # generated Cypher |
| 262 | |
| 263 | # doc generation |
| 264 | docgen = DocGenerator(store) |
| 265 | draft = docgen.generate_for_function("process_payment") |
| 266 | print(draft.docstring) |
| 267 | ``` |
+70
-1
| --- docs/guide/mcp-integration.md | ||
| +++ docs/guide/mcp-integration.md | ||
| @@ -90,11 +90,11 @@ | ||
| 90 | 90 | |
| 91 | 91 | --- |
| 92 | 92 | |
| 93 | 93 | ## Available MCP tools |
| 94 | 94 | |
| 95 | -All tools accept and return JSON. | |
| 95 | +All tools accept and return JSON. There are 11 tools in total. | |
| 96 | 96 | |
| 97 | 97 | | Tool | Equivalent CLI | Description | |
| 98 | 98 | |---|---|---| |
| 99 | 99 | | `ingest` | `navegador ingest` | Ingest a repo into the graph | |
| 100 | 100 | | `context` | `navegador context` | File-level context bundle | |
| @@ -101,10 +101,14 @@ | ||
| 101 | 101 | | `function` | `navegador function` | Function with call graph | |
| 102 | 102 | | `class` | `navegador class` | Class with hierarchy | |
| 103 | 103 | | `explain` | `navegador explain` | Universal node lookup | |
| 104 | 104 | | `search` | `navegador search` | Text search across graph | |
| 105 | 105 | | `query` | `navegador query` | Raw Cypher passthrough | |
| 106 | +| `get_rationale` | `navegador explain --rationale` | Decisions and rules governing a node | | |
| 107 | +| `find_owners` | `navegador codeowners` | People and domains that own a node | | |
| 108 | +| `search_knowledge` | `navegador search --knowledge` | Search knowledge layer only | | |
| 109 | +| `blast_radius` | `navegador impact` | Transitive impact set for a node | | |
| 106 | 110 | |
| 107 | 111 | ### Tool input schemas |
| 108 | 112 | |
| 109 | 113 | **ingest** |
| 110 | 114 | ```json |
| @@ -139,14 +143,79 @@ | ||
| 139 | 143 | **query** |
| 140 | 144 | ```json |
| 141 | 145 | { "cypher": "MATCH (f:Function) RETURN f.name LIMIT 10" } |
| 142 | 146 | ``` |
| 143 | 147 | |
| 148 | +**get_rationale** | |
| 149 | +```json | |
| 150 | +{ "name": "process_payment", "file": "src/payments/processor.py" } | |
| 151 | +``` | |
| 152 | + | |
| 153 | +**find_owners** | |
| 154 | +```json | |
| 155 | +{ "name": "AuthService", "file": "src/auth/service.py" } | |
| 156 | +``` | |
| 157 | + | |
| 158 | +**search_knowledge** | |
| 159 | +```json | |
| 160 | +{ "query": "idempotency", "limit": 10 } | |
| 161 | +``` | |
| 162 | + | |
| 163 | +**blast_radius** | |
| 164 | +```json | |
| 165 | +{ "name": "validate_token", "depth": 3 } | |
| 166 | +``` | |
| 167 | + | |
| 168 | +--- | |
| 169 | + | |
| 170 | +## Read-only mode | |
| 171 | + | |
| 172 | +Start the MCP server in read-only mode to prevent agents from modifying the graph. This is recommended for shared or production environments. | |
| 173 | + | |
| 174 | +```bash | |
| 175 | +navegador mcp --read-only | |
| 176 | +``` | |
| 177 | + | |
| 178 | +In read-only mode, the `ingest` tool is disabled and the `query` tool only accepts `MATCH`/`RETURN` queries. Write keywords (`CREATE`, `MERGE`, `SET`, `DELETE`) are rejected. | |
| 179 | + | |
| 180 | +Set in agent config: | |
| 181 | + | |
| 182 | +```json | |
| 183 | +{ | |
| 184 | + "mcpServers": { | |
| 185 | + "navegador": { | |
| 186 | + "command": "navegador", | |
| 187 | + "args": ["mcp", "--read-only"], | |
| 188 | + "env": { | |
| 189 | + "NAVEGADOR_DB": ".navegador/navegador.db" | |
| 190 | + } | |
| 191 | + } | |
| 192 | + } | |
| 193 | +} | |
| 194 | +``` | |
| 195 | + | |
| 196 | +Or set `read_only = true` in `.navegador/config.toml` under `[mcp]`. | |
| 197 | + | |
| 198 | +--- | |
| 199 | + | |
| 200 | +## Editor integration | |
| 201 | + | |
| 202 | +Instead of configuring MCP manually, use the editor setup command: | |
| 203 | + | |
| 204 | +```bash | |
| 205 | +navegador editor setup claude-code | |
| 206 | +navegador editor setup cursor | |
| 207 | +navegador editor setup codex | |
| 208 | +navegador editor setup windsurf | |
| 209 | +``` | |
| 210 | + | |
| 211 | +This writes the correct MCP config file for the selected editor and prompts for the database path. | |
| 212 | + | |
| 144 | 213 | --- |
| 145 | 214 | |
| 146 | 215 | ## When MCP makes sense |
| 147 | 216 | |
| 148 | 217 | - You are in an interactive Claude or Cursor session and want to call `explain`, `search`, or `function` without dropping to a terminal |
| 149 | 218 | - You want navegador tools auto-discovered by the agent without writing custom tool definitions |
| 150 | 219 | - You are building an agent workflow that dynamically queries the graph mid-task |
| 151 | 220 | |
| 152 | 221 | For automated background tasks (re-ingest on file save, sync on pull), use the CLI via [agent hooks](agent-hooks.md) instead. |
| 153 | 222 | |
| 154 | 223 | ADDED docs/guide/sdk.md |
| --- docs/guide/mcp-integration.md | |
| +++ docs/guide/mcp-integration.md | |
| @@ -90,11 +90,11 @@ | |
| 90 | |
| 91 | --- |
| 92 | |
| 93 | ## Available MCP tools |
| 94 | |
| 95 | All tools accept and return JSON. |
| 96 | |
| 97 | | Tool | Equivalent CLI | Description | |
| 98 | |---|---|---| |
| 99 | | `ingest` | `navegador ingest` | Ingest a repo into the graph | |
| 100 | | `context` | `navegador context` | File-level context bundle | |
| @@ -101,10 +101,14 @@ | |
| 101 | | `function` | `navegador function` | Function with call graph | |
| 102 | | `class` | `navegador class` | Class with hierarchy | |
| 103 | | `explain` | `navegador explain` | Universal node lookup | |
| 104 | | `search` | `navegador search` | Text search across graph | |
| 105 | | `query` | `navegador query` | Raw Cypher passthrough | |
| 106 | |
| 107 | ### Tool input schemas |
| 108 | |
| 109 | **ingest** |
| 110 | ```json |
| @@ -139,14 +143,79 @@ | |
| 139 | **query** |
| 140 | ```json |
| 141 | { "cypher": "MATCH (f:Function) RETURN f.name LIMIT 10" } |
| 142 | ``` |
| 143 | |
| 144 | --- |
| 145 | |
| 146 | ## When MCP makes sense |
| 147 | |
| 148 | - You are in an interactive Claude or Cursor session and want to call `explain`, `search`, or `function` without dropping to a terminal |
| 149 | - You want navegador tools auto-discovered by the agent without writing custom tool definitions |
| 150 | - You are building an agent workflow that dynamically queries the graph mid-task |
| 151 | |
| 152 | For automated background tasks (re-ingest on file save, sync on pull), use the CLI via [agent hooks](agent-hooks.md) instead. |
| 153 | |
| 154 | DDED docs/guide/sdk.md |
| --- docs/guide/mcp-integration.md | |
| +++ docs/guide/mcp-integration.md | |
| @@ -90,11 +90,11 @@ | |
| 90 | |
| 91 | --- |
| 92 | |
| 93 | ## Available MCP tools |
| 94 | |
| 95 | All tools accept and return JSON. There are 11 tools in total. |
| 96 | |
| 97 | | Tool | Equivalent CLI | Description | |
| 98 | |---|---|---| |
| 99 | | `ingest` | `navegador ingest` | Ingest a repo into the graph | |
| 100 | | `context` | `navegador context` | File-level context bundle | |
| @@ -101,10 +101,14 @@ | |
| 101 | | `function` | `navegador function` | Function with call graph | |
| 102 | | `class` | `navegador class` | Class with hierarchy | |
| 103 | | `explain` | `navegador explain` | Universal node lookup | |
| 104 | | `search` | `navegador search` | Text search across graph | |
| 105 | | `query` | `navegador query` | Raw Cypher passthrough | |
| 106 | | `get_rationale` | `navegador explain --rationale` | Decisions and rules governing a node | |
| 107 | | `find_owners` | `navegador codeowners` | People and domains that own a node | |
| 108 | | `search_knowledge` | `navegador search --knowledge` | Search knowledge layer only | |
| 109 | | `blast_radius` | `navegador impact` | Transitive impact set for a node | |
| 110 | |
| 111 | ### Tool input schemas |
| 112 | |
| 113 | **ingest** |
| 114 | ```json |
| @@ -139,14 +143,79 @@ | |
| 143 | **query** |
| 144 | ```json |
| 145 | { "cypher": "MATCH (f:Function) RETURN f.name LIMIT 10" } |
| 146 | ``` |
| 147 | |
| 148 | **get_rationale** |
| 149 | ```json |
| 150 | { "name": "process_payment", "file": "src/payments/processor.py" } |
| 151 | ``` |
| 152 | |
| 153 | **find_owners** |
| 154 | ```json |
| 155 | { "name": "AuthService", "file": "src/auth/service.py" } |
| 156 | ``` |
| 157 | |
| 158 | **search_knowledge** |
| 159 | ```json |
| 160 | { "query": "idempotency", "limit": 10 } |
| 161 | ``` |
| 162 | |
| 163 | **blast_radius** |
| 164 | ```json |
| 165 | { "name": "validate_token", "depth": 3 } |
| 166 | ``` |
| 167 | |
| 168 | --- |
| 169 | |
| 170 | ## Read-only mode |
| 171 | |
| 172 | Start the MCP server in read-only mode to prevent agents from modifying the graph. This is recommended for shared or production environments. |
| 173 | |
| 174 | ```bash |
| 175 | navegador mcp --read-only |
| 176 | ``` |
| 177 | |
| 178 | In read-only mode, the `ingest` tool is disabled and the `query` tool only accepts `MATCH`/`RETURN` queries. Write keywords (`CREATE`, `MERGE`, `SET`, `DELETE`) are rejected. |
| 179 | |
| 180 | Set in agent config: |
| 181 | |
| 182 | ```json |
| 183 | { |
| 184 | "mcpServers": { |
| 185 | "navegador": { |
| 186 | "command": "navegador", |
| 187 | "args": ["mcp", "--read-only"], |
| 188 | "env": { |
| 189 | "NAVEGADOR_DB": ".navegador/navegador.db" |
| 190 | } |
| 191 | } |
| 192 | } |
| 193 | } |
| 194 | ``` |
| 195 | |
| 196 | Or set `read_only = true` in `.navegador/config.toml` under `[mcp]`. |
| 197 | |
| 198 | --- |
| 199 | |
| 200 | ## Editor integration |
| 201 | |
| 202 | Instead of configuring MCP manually, use the editor setup command: |
| 203 | |
| 204 | ```bash |
| 205 | navegador editor setup claude-code |
| 206 | navegador editor setup cursor |
| 207 | navegador editor setup codex |
| 208 | navegador editor setup windsurf |
| 209 | ``` |
| 210 | |
| 211 | This writes the correct MCP config file for the selected editor and prompts for the database path. |
| 212 | |
| 213 | --- |
| 214 | |
| 215 | ## When MCP makes sense |
| 216 | |
| 217 | - You are in an interactive Claude or Cursor session and want to call `explain`, `search`, or `function` without dropping to a terminal |
| 218 | - You want navegador tools auto-discovered by the agent without writing custom tool definitions |
| 219 | - You are building an agent workflow that dynamically queries the graph mid-task |
| 220 | |
| 221 | For automated background tasks (re-ingest on file save, sync on pull), use the CLI via [agent hooks](agent-hooks.md) instead. |
| 222 | |
| 223 | DDED docs/guide/sdk.md |
+254
| --- a/docs/guide/sdk.md | ||
| +++ b/docs/guide/sdk.md | ||
| @@ -0,0 +1,254 @@ | ||
| 1 | +# Python SDK | |
| 2 | + | |
| 3 | +The navegador Python SDK lets you drive ingestion, query the graph, and load context from your own scripts and tools — without going through the CLI. | |
| 4 | + | |
| 5 | +--- | |
| 6 | + | |
| 7 | +## Installation | |
| 8 | + | |
| 9 | +```bash | |
| 10 | +pip install navegador | |
| 11 | +``` | |
| 12 | + | |
| 13 | +For Redis (production/team) support: | |
| 14 | + | |
| 15 | +```bash | |
| 16 | +pip install "navegador[redis]" | |
| 17 | +``` | |
| 18 | + | |
| 19 | +--- | |
| 20 | + | |
| 21 | +## Connecting to the graph | |
| 22 | + | |
| 23 | +=== "SQLite (local)" | |
| 24 | + | |
| 25 | + ```python | |
| 26 | + from navegador.graph import GraphStore | |
| 27 | + | |
| 28 | + store = GraphStore.sqlite(".navegador/navegador.db") | |
| 29 | + ``` | |
| 30 | + | |
| 31 | +=== "Redis (production)" | |
| 32 | + | |
| 33 | + ```python | |
| 34 | + from navegador.graph import GraphStore | |
| 35 | + | |
| 36 | + store = GraphStore.redis("redis://localhost:6379") | |
| 37 | + ``` | |
| 38 | + | |
| 39 | +Both backends implement the same interface. All examples below work with either. | |
| 40 | + | |
| 41 | +Use the context manager to ensure the connection is closed: | |
| 42 | + | |
| 43 | +```python | |
| 44 | +with GraphStore.sqlite(".navegador/navegador.db") as store: | |
| 45 | + results = store.query("MATCH (n) RETURN count(n) AS total") | |
| 46 | + print(results[0]["total"]) | |
| 47 | +``` | |
| 48 | + | |
| 49 | +--- | |
| 50 | + | |
| 51 | +## Ingestion | |
| 52 | + | |
| 53 | +### Ingest a repo | |
| 54 | + | |
| 55 | +```python | |
| 56 | +from navegador.graph import GraphStore | |
| 57 | +from navegador.ingest import RepoIngester | |
| 58 | + | |
| 59 | +store = GraphStore.sqlite(".navegador/navegador.db") | |
| 60 | +ingester = RepoIngester(store) | |
| 61 | + | |
| 62 | +result = ingester.ingest("./src") | |
| 63 | +print(f"{result.nodes_created} nodes, {result.edges_created} edges in {result.duration_seconds:.2f}s") | |
| 64 | +``` | |
| 65 | + | |
| 66 | +### Incremental ingest (single file) | |
| 67 | + | |
| 68 | +```python | |
| 69 | +result = ingester.ingest_file("./src/auth/service.py") | |
| 70 | +``` | |
| 71 | + | |
| 72 | +### Wipe and rebuild | |
| 73 | + | |
| 74 | +```python | |
| 75 | +result = ingester.ingest("./src", clear=True) | |
| 76 | +``` | |
| 77 | + | |
| 78 | +### Add knowledge programmatically | |
| 79 | + | |
| 80 | +```python | |
| 81 | +from navegador.ingest import KnowledgeIngester | |
| 82 | + | |
| 83 | +ki = KnowledgeIngester(store) | |
| 84 | + | |
| 85 | +ki.add_domain("Payments", description="Payment processing and billing") | |
| 86 | +ki.add_concept("Idempotency", domain="Payments", | |
| 87 | + description="Operations safe to retry without side effects") | |
| 88 | +ki.add_rule("RequireIdempotencyKey", | |
| 89 | + domain="Payments", severity="critical", | |
| 90 | + rationale="Card networks retry on timeout") | |
| 91 | +ki.annotate("process_payment", node_type="Function", | |
| 92 | + concept="Idempotency", rule="RequireIdempotencyKey") | |
| 93 | +``` | |
| 94 | + | |
| 95 | +--- | |
| 96 | + | |
| 97 | +## Loading context | |
| 98 | + | |
| 99 | +`ContextLoader` builds structured context bundles from the graph. Each method corresponds to a CLI command. | |
| 100 | + | |
| 101 | +```python | |
| 102 | +from navegador.graph import GraphStore | |
| 103 | +from navegador.context import ContextLoader | |
| 104 | + | |
| 105 | +store = GraphStore.sqlite(".navegador/navegador.db") | |
| 106 | +loader = ContextLoader(store) | |
| 107 | +``` | |
| 108 | + | |
| 109 | +### File context | |
| 110 | + | |
| 111 | +```python | |
| 112 | +bundle = loader.load_file("src/auth/service.py") | |
| 113 | +print(bundle.to_markdown()) | |
| 114 | +``` | |
| 115 | + | |
| 116 | +### Function with call graph | |
| 117 | + | |
| 118 | +```python | |
| 119 | +# depth controls how many hops of callers/callees to include | |
| 120 | +bundle = loader.load_function("validate_token", depth=2) | |
| 121 | +print(bundle.to_json()) | |
| 122 | +``` | |
| 123 | + | |
| 124 | +### Class hierarchy | |
| 125 | + | |
| 126 | +```python | |
| 127 | +bundle = loader.load_class("PaymentProcessor", file="src/payments/processor.py") | |
| 128 | +data = bundle.to_dict() | |
| 129 | +``` | |
| 130 | + | |
| 131 | +### Universal explain | |
| 132 | + | |
| 133 | +```python | |
| 134 | +# works for any node type: function, class, file, concept, rule, decision | |
| 135 | +bundle = loader.explain("AuthService") | |
| 136 | +bundle = loader.explain("PaymentsMustBeIdempotent") | |
| 137 | +``` | |
| 138 | + | |
| 139 | +### Concept and domain | |
| 140 | + | |
| 141 | +```python | |
| 142 | +bundle = loader.load_concept("Idempotency") | |
| 143 | +bundle = loader.load_domain("Payments") | |
| 144 | +``` | |
| 145 | + | |
| 146 | +--- | |
| 147 | + | |
| 148 | +## Search | |
| 149 | + | |
| 150 | +```python | |
| 151 | +# search function and class names (default) | |
| 152 | +nodes = loader.search("rate limit") | |
| 153 | + | |
| 154 | +# search all layers including knowledge and docs | |
| 155 | +nodes = loader.search("rate limit", all_layers=True, limit=50) | |
| 156 | + | |
| 157 | +# search docstrings and wiki content only | |
| 158 | +nodes = loader.search_by_docstring("retry logic") | |
| 159 | + | |
| 160 | +# find all functions using a specific decorator | |
| 161 | +nodes = loader.decorated_by("login_required") | |
| 162 | + | |
| 163 | +for node in nodes: | |
| 164 | + print(f"{node.label}: {node.name} ({node.properties.get('file', '')})") | |
| 165 | +``` | |
| 166 | + | |
| 167 | +--- | |
| 168 | + | |
| 169 | +## Knowledge queries | |
| 170 | + | |
| 171 | +```python | |
| 172 | +# everything in the Payments domain | |
| 173 | +bundle = loader.load_domain("Payments") | |
| 174 | +for node in bundle.nodes: | |
| 175 | + print(f" [{node.label}] {node.name}") | |
| 176 | + | |
| 177 | +# all code annotated with a concept | |
| 178 | +bundle = loader.load_concept("Idempotency") | |
| 179 | +for node in bundle.nodes: | |
| 180 | + if node.layer == "code": | |
| 181 | + print(f" {node.name} {node.properties.get('file', '')}") | |
| 182 | +``` | |
| 183 | + | |
| 184 | +--- | |
| 185 | + | |
| 186 | +## Exporting output | |
| 187 | + | |
| 188 | +Every `ContextBundle` supports three output formats: | |
| 189 | + | |
| 190 | +```python | |
| 191 | +bundle = loader.load_function("process_payment") | |
| 192 | + | |
| 193 | +# JSON string — for agents, APIs, CI | |
| 194 | +json_str = bundle.to_json() | |
| 195 | + | |
| 196 | +# Markdown — readable by humans and LLMs | |
| 197 | +md_str = bundle.to_markdown() | |
| 198 | + | |
| 199 | +# Python dict — for further processing | |
| 200 | +data = bundle.to_dict() | |
| 201 | +print(data["root"]["name"]) | |
| 202 | +print(len(data["nodes"])) | |
| 203 | +``` | |
| 204 | + | |
| 205 | +--- | |
| 206 | + | |
| 207 | +## Raw Cypher queries | |
| 208 | + | |
| 209 | +Drop to raw Cypher for anything the built-in methods don't cover: | |
| 210 | + | |
| 211 | +```python | |
| 212 | +results = store.query( | |
| 213 | + "MATCH (f:Function)-[:CALLS]->(g:Function) " | |
| 214 | + "WHERE f.file = $file " | |
| 215 | + "RETURN f.name, g.name", | |
| 216 | + params={"file": "src/payments/processor.py"} | |
| 217 | +) | |
| 218 | +for row in results: | |
| 219 | + print(f"{row['f.name']} -> {row['g.name']}") | |
| 220 | +``` | |
| 221 | + | |
| 222 | +!!! warning | |
| 223 | + `store.query()` executes writes as well as reads. Stick to `MATCH` / `RETURN` for inspection queries. | |
| 224 | + | |
| 225 | +--- | |
| 226 | + | |
| 227 | +## Wiki ingestion | |
| 228 | + | |
| 229 | +```python | |
| 230 | +import os | |
| 231 | +from navegador.ingest import WikiIngester | |
| 232 | + | |
| 233 | +ingester = WikiIngester(store) | |
| 234 | + | |
| 235 | +# from GitHub API | |
| 236 | +result = ingester.ingest_repo("myorg/myrepo", token=os.environ["GITHUB_TOKEN"]) | |
| 237 | + | |
| 238 | +# from a locally cloned wiki directory | |
| 239 | +result = ingester.ingest_dir("./myrepo.wiki") | |
| 240 | +``` | |
| 241 | + | |
| 242 | +--- | |
| 243 | + | |
| 244 | +## Error handling | |
| 245 | + | |
| 246 | +All ingesters return an `IngestionResult` dataclass. Check `errors` for per-file failures without crashing the whole run: | |
| 247 | + | |
| 248 | +```python | |
| 249 | +result = ingester.ingest("./src") | |
| 250 | +if result.errors: | |
| 251 | + for err in result.errors: | |
| 252 | + print(f"Warning: {err}") | |
| 253 | +print(f"Processed {result.files_processed} files, {result.nodes_created} nodes created") | |
| 254 | +``` |
| --- a/docs/guide/sdk.md | |
| +++ b/docs/guide/sdk.md | |
| @@ -0,0 +1,254 @@ | |
| --- a/docs/guide/sdk.md | |
| +++ b/docs/guide/sdk.md | |
| @@ -0,0 +1,254 @@ | |
| 1 | # Python SDK |
| 2 | |
| 3 | The navegador Python SDK lets you drive ingestion, query the graph, and load context from your own scripts and tools — without going through the CLI. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## Installation |
| 8 | |
| 9 | ```bash |
| 10 | pip install navegador |
| 11 | ``` |
| 12 | |
| 13 | For Redis (production/team) support: |
| 14 | |
| 15 | ```bash |
| 16 | pip install "navegador[redis]" |
| 17 | ``` |
| 18 | |
| 19 | --- |
| 20 | |
| 21 | ## Connecting to the graph |
| 22 | |
| 23 | === "SQLite (local)" |
| 24 | |
| 25 | ```python |
| 26 | from navegador.graph import GraphStore |
| 27 | |
| 28 | store = GraphStore.sqlite(".navegador/navegador.db") |
| 29 | ``` |
| 30 | |
| 31 | === "Redis (production)" |
| 32 | |
| 33 | ```python |
| 34 | from navegador.graph import GraphStore |
| 35 | |
| 36 | store = GraphStore.redis("redis://localhost:6379") |
| 37 | ``` |
| 38 | |
| 39 | Both backends implement the same interface. All examples below work with either. |
| 40 | |
| 41 | Use the context manager to ensure the connection is closed: |
| 42 | |
| 43 | ```python |
| 44 | with GraphStore.sqlite(".navegador/navegador.db") as store: |
| 45 | results = store.query("MATCH (n) RETURN count(n) AS total") |
| 46 | print(results[0]["total"]) |
| 47 | ``` |
| 48 | |
| 49 | --- |
| 50 | |
| 51 | ## Ingestion |
| 52 | |
| 53 | ### Ingest a repo |
| 54 | |
| 55 | ```python |
| 56 | from navegador.graph import GraphStore |
| 57 | from navegador.ingest import RepoIngester |
| 58 | |
| 59 | store = GraphStore.sqlite(".navegador/navegador.db") |
| 60 | ingester = RepoIngester(store) |
| 61 | |
| 62 | result = ingester.ingest("./src") |
| 63 | print(f"{result.nodes_created} nodes, {result.edges_created} edges in {result.duration_seconds:.2f}s") |
| 64 | ``` |
| 65 | |
| 66 | ### Incremental ingest (single file) |
| 67 | |
| 68 | ```python |
| 69 | result = ingester.ingest_file("./src/auth/service.py") |
| 70 | ``` |
| 71 | |
| 72 | ### Wipe and rebuild |
| 73 | |
| 74 | ```python |
| 75 | result = ingester.ingest("./src", clear=True) |
| 76 | ``` |
| 77 | |
| 78 | ### Add knowledge programmatically |
| 79 | |
| 80 | ```python |
| 81 | from navegador.ingest import KnowledgeIngester |
| 82 | |
| 83 | ki = KnowledgeIngester(store) |
| 84 | |
| 85 | ki.add_domain("Payments", description="Payment processing and billing") |
| 86 | ki.add_concept("Idempotency", domain="Payments", |
| 87 | description="Operations safe to retry without side effects") |
| 88 | ki.add_rule("RequireIdempotencyKey", |
| 89 | domain="Payments", severity="critical", |
| 90 | rationale="Card networks retry on timeout") |
| 91 | ki.annotate("process_payment", node_type="Function", |
| 92 | concept="Idempotency", rule="RequireIdempotencyKey") |
| 93 | ``` |
| 94 | |
| 95 | --- |
| 96 | |
| 97 | ## Loading context |
| 98 | |
| 99 | `ContextLoader` builds structured context bundles from the graph. Each method corresponds to a CLI command. |
| 100 | |
| 101 | ```python |
| 102 | from navegador.graph import GraphStore |
| 103 | from navegador.context import ContextLoader |
| 104 | |
| 105 | store = GraphStore.sqlite(".navegador/navegador.db") |
| 106 | loader = ContextLoader(store) |
| 107 | ``` |
| 108 | |
| 109 | ### File context |
| 110 | |
| 111 | ```python |
| 112 | bundle = loader.load_file("src/auth/service.py") |
| 113 | print(bundle.to_markdown()) |
| 114 | ``` |
| 115 | |
| 116 | ### Function with call graph |
| 117 | |
| 118 | ```python |
| 119 | # depth controls how many hops of callers/callees to include |
| 120 | bundle = loader.load_function("validate_token", depth=2) |
| 121 | print(bundle.to_json()) |
| 122 | ``` |
| 123 | |
| 124 | ### Class hierarchy |
| 125 | |
| 126 | ```python |
| 127 | bundle = loader.load_class("PaymentProcessor", file="src/payments/processor.py") |
| 128 | data = bundle.to_dict() |
| 129 | ``` |
| 130 | |
| 131 | ### Universal explain |
| 132 | |
| 133 | ```python |
| 134 | # works for any node type: function, class, file, concept, rule, decision |
| 135 | bundle = loader.explain("AuthService") |
| 136 | bundle = loader.explain("PaymentsMustBeIdempotent") |
| 137 | ``` |
| 138 | |
| 139 | ### Concept and domain |
| 140 | |
| 141 | ```python |
| 142 | bundle = loader.load_concept("Idempotency") |
| 143 | bundle = loader.load_domain("Payments") |
| 144 | ``` |
| 145 | |
| 146 | --- |
| 147 | |
| 148 | ## Search |
| 149 | |
| 150 | ```python |
| 151 | # search function and class names (default) |
| 152 | nodes = loader.search("rate limit") |
| 153 | |
| 154 | # search all layers including knowledge and docs |
| 155 | nodes = loader.search("rate limit", all_layers=True, limit=50) |
| 156 | |
| 157 | # search docstrings and wiki content only |
| 158 | nodes = loader.search_by_docstring("retry logic") |
| 159 | |
| 160 | # find all functions using a specific decorator |
| 161 | nodes = loader.decorated_by("login_required") |
| 162 | |
| 163 | for node in nodes: |
| 164 | print(f"{node.label}: {node.name} ({node.properties.get('file', '')})") |
| 165 | ``` |
| 166 | |
| 167 | --- |
| 168 | |
| 169 | ## Knowledge queries |
| 170 | |
| 171 | ```python |
| 172 | # everything in the Payments domain |
| 173 | bundle = loader.load_domain("Payments") |
| 174 | for node in bundle.nodes: |
| 175 | print(f" [{node.label}] {node.name}") |
| 176 | |
| 177 | # all code annotated with a concept |
| 178 | bundle = loader.load_concept("Idempotency") |
| 179 | for node in bundle.nodes: |
| 180 | if node.layer == "code": |
| 181 | print(f" {node.name} {node.properties.get('file', '')}") |
| 182 | ``` |
| 183 | |
| 184 | --- |
| 185 | |
| 186 | ## Exporting output |
| 187 | |
| 188 | Every `ContextBundle` supports three output formats: |
| 189 | |
| 190 | ```python |
| 191 | bundle = loader.load_function("process_payment") |
| 192 | |
| 193 | # JSON string — for agents, APIs, CI |
| 194 | json_str = bundle.to_json() |
| 195 | |
| 196 | # Markdown — readable by humans and LLMs |
| 197 | md_str = bundle.to_markdown() |
| 198 | |
| 199 | # Python dict — for further processing |
| 200 | data = bundle.to_dict() |
| 201 | print(data["root"]["name"]) |
| 202 | print(len(data["nodes"])) |
| 203 | ``` |
| 204 | |
| 205 | --- |
| 206 | |
| 207 | ## Raw Cypher queries |
| 208 | |
| 209 | Drop to raw Cypher for anything the built-in methods don't cover: |
| 210 | |
| 211 | ```python |
| 212 | results = store.query( |
| 213 | "MATCH (f:Function)-[:CALLS]->(g:Function) " |
| 214 | "WHERE f.file = $file " |
| 215 | "RETURN f.name, g.name", |
| 216 | params={"file": "src/payments/processor.py"} |
| 217 | ) |
| 218 | for row in results: |
| 219 | print(f"{row['f.name']} -> {row['g.name']}") |
| 220 | ``` |
| 221 | |
| 222 | !!! warning |
| 223 | `store.query()` executes writes as well as reads. Stick to `MATCH` / `RETURN` for inspection queries. |
| 224 | |
| 225 | --- |
| 226 | |
| 227 | ## Wiki ingestion |
| 228 | |
| 229 | ```python |
| 230 | import os |
| 231 | from navegador.ingest import WikiIngester |
| 232 | |
| 233 | ingester = WikiIngester(store) |
| 234 | |
| 235 | # from GitHub API |
| 236 | result = ingester.ingest_repo("myorg/myrepo", token=os.environ["GITHUB_TOKEN"]) |
| 237 | |
| 238 | # from a locally cloned wiki directory |
| 239 | result = ingester.ingest_dir("./myrepo.wiki") |
| 240 | ``` |
| 241 | |
| 242 | --- |
| 243 | |
| 244 | ## Error handling |
| 245 | |
| 246 | All ingesters return an `IngestionResult` dataclass. Check `errors` for per-file failures without crashing the whole run: |
| 247 | |
| 248 | ```python |
| 249 | result = ingester.ingest("./src") |
| 250 | if result.errors: |
| 251 | for err in result.errors: |
| 252 | print(f"Warning: {err}") |
| 253 | print(f"Processed {result.files_processed} files, {result.nodes_created} nodes created") |
| 254 | ``` |
+59
-2
| --- docs/index.md | ||
| +++ docs/index.md | ||
| @@ -3,10 +3,12 @@ | ||
| 3 | 3 | **The project knowledge graph for AI coding agents.** |
| 4 | 4 | |
| 5 | 5 | Navegador builds and maintains a queryable graph of your software project — combining static code analysis with human-curated business knowledge — so that AI coding agents always have precise, structured context instead of raw file dumps. |
| 6 | 6 | |
| 7 | 7 | > *navegador* — Spanish for *navigator / sailor*. It helps agents navigate your code. |
| 8 | +> | |
| 9 | +> **Current version: 0.7.0** | |
| 8 | 10 | |
| 9 | 11 | --- |
| 10 | 12 | |
| 11 | 13 | ## Two layers, one graph |
| 12 | 14 | |
| @@ -22,11 +24,11 @@ | ||
| 22 | 24 | │ Variable · Import · Decorator · (call graphs, hierarchies) │ |
| 23 | 25 | └─────────────────────────────────────────────────────────────────┘ |
| 24 | 26 | stored in FalkorDB (SQLite local / Redis prod) |
| 25 | 27 | ``` |
| 26 | 28 | |
| 27 | -The **code layer** is populated automatically by ingesting source trees. Python and TypeScript are supported out of the box via tree-sitter. The **knowledge layer** is populated by manual curation (`navegador add`), GitHub wiki ingestion, and [Planopticon](guide/planopticon.md) output (meeting and video knowledge graphs). | |
| 29 | +The **code layer** is populated automatically by ingesting source trees. 13 languages are supported via tree-sitter (Python, TypeScript, JavaScript, Go, Rust, Java, Kotlin, C#, PHP, Ruby, Swift, C, C++). The **knowledge layer** is populated by manual curation (`navegador add`), GitHub wiki ingestion, and [Planopticon](guide/planopticon.md) output (meeting and video knowledge graphs). | |
| 28 | 30 | |
| 29 | 31 | --- |
| 30 | 32 | |
| 31 | 33 | ## Quick start |
| 32 | 34 | |
| @@ -34,10 +36,21 @@ | ||
| 34 | 36 | pip install navegador # Python 3.12+ required |
| 35 | 37 | navegador ingest ./my-repo # parse + index the codebase |
| 36 | 38 | navegador explain AuthService # what is this thing? |
| 37 | 39 | navegador search "rate limit" --all # search code + knowledge together |
| 38 | 40 | ``` |
| 41 | + | |
| 42 | +Or use the Python SDK: | |
| 43 | + | |
| 44 | +```python | |
| 45 | +from navegador import Navegador | |
| 46 | + | |
| 47 | +nav = Navegador(".navegador/navegador.db") | |
| 48 | +nav.ingest("./my-repo") | |
| 49 | +bundle = nav.explain("AuthService") | |
| 50 | +print(bundle.to_markdown()) | |
| 51 | +``` | |
| 39 | 52 | |
| 40 | 53 | --- |
| 41 | 54 | |
| 42 | 55 | ## What goes in the graph |
| 43 | 56 | |
| @@ -52,10 +65,11 @@ | ||
| 52 | 65 | | Knowledge | Decision | `navegador add decision` | |
| 53 | 66 | | Knowledge | Person | `navegador add person` | |
| 54 | 67 | | Knowledge | WikiPage | `navegador wiki ingest` | |
| 55 | 68 | | Knowledge | (any) | `navegador planopticon ingest` | |
| 56 | 69 | | Cross-layer | ANNOTATES, GOVERNS, IMPLEMENTS | `navegador annotate` | |
| 70 | +| Analysis | TESTS, COUPLED_WITH edges | `navegador testmap`, `navegador cycles` | | |
| 57 | 71 | |
| 58 | 72 | --- |
| 59 | 73 | |
| 60 | 74 | ## Agent integration |
| 61 | 75 | |
| @@ -87,11 +101,11 @@ | ||
| 87 | 101 | } |
| 88 | 102 | } |
| 89 | 103 | } |
| 90 | 104 | ``` |
| 91 | 105 | |
| 92 | - See [MCP Integration](guide/mcp-integration.md) for the full tool list and per-agent config snippets. | |
| 106 | + 11 tools available. See [MCP Integration](guide/mcp-integration.md) for the full tool list and per-agent config snippets. Use `--read-only` mode to restrict agents to query-only access. | |
| 93 | 107 | |
| 94 | 108 | === "Bootstrap" |
| 95 | 109 | |
| 96 | 110 | One command to install navegador, ingest a repo, and wire the agent hook for your preferred AI coding assistant. |
| 97 | 111 | |
| @@ -99,10 +113,53 @@ | ||
| 99 | 113 | ./bootstrap.sh --repo owner/repo --wiki --agent claude |
| 100 | 114 | ``` |
| 101 | 115 | |
| 102 | 116 | Supports `--agent claude`, `--agent gemini`, and `--agent openai`. See [Agent Hooks](guide/agent-hooks.md) for what the hook does and how to configure it manually. |
| 103 | 117 | |
| 118 | +=== "Editor integration" | |
| 119 | + | |
| 120 | + Wire navegador into your editor with one command: | |
| 121 | + | |
| 122 | + ```bash | |
| 123 | + navegador editor setup claude-code | |
| 124 | + navegador editor setup cursor | |
| 125 | + navegador editor setup codex | |
| 126 | + navegador editor setup windsurf | |
| 127 | + ``` | |
| 128 | + | |
| 129 | +=== "CI/CD" | |
| 130 | + | |
| 131 | + Run navegador in CI pipelines for automated context checks: | |
| 132 | + | |
| 133 | + ```bash | |
| 134 | + navegador ci ingest | |
| 135 | + navegador ci stats | |
| 136 | + navegador ci check | |
| 137 | + ``` | |
| 138 | + | |
| 139 | +--- | |
| 140 | + | |
| 141 | +## What's new in 0.7.0 | |
| 142 | + | |
| 143 | +| Feature | Command / API | | |
| 144 | +|---|---| | |
| 145 | +| **13 languages** (added Kotlin, C#, PHP, Ruby, Swift, C, C++) | `pip install "navegador[languages]"` | | |
| 146 | +| **Python SDK** | `from navegador import Navegador` | | |
| 147 | +| **Incremental ingestion** | `navegador ingest --incremental`, `--watch` | | |
| 148 | +| **Schema migrations** | `navegador migrate` | | |
| 149 | +| **Export / import** | `navegador export`, `navegador import` (JSONL) | | |
| 150 | +| **Editor integrations** | `navegador editor setup <editor>` | | |
| 151 | +| **Analysis commands** | `navegador diff`, `navegador churn`, `navegador impact`, `navegador trace`, `navegador deadcode`, `navegador cycles`, `navegador testmap` | | |
| 152 | +| **Multi-repo** | `navegador repo add/list/ingest-all/search` | | |
| 153 | +| **Semantic search** | `navegador semantic-search`, `navegador ask` | | |
| 154 | +| **Framework enrichment** | Django, FastAPI, React, Rails, Spring Boot, Laravel, and more | | |
| 155 | +| **Monorepo support** | Turborepo, Nx, Yarn, pnpm, Cargo, Go workspaces | | |
| 156 | +| **Cluster mode** | Shared Redis graph, pub/sub, task queue, sessions | | |
| 157 | +| **11 MCP tools** (was 7) | `get_rationale`, `find_owners`, `search_knowledge`, `blast_radius` added | | |
| 158 | +| **Sensitive content redaction** | `navegador ingest --redact` | | |
| 159 | +| **Shell completions** | `navegador completions bash/zsh/fish` | | |
| 160 | + | |
| 104 | 161 | --- |
| 105 | 162 | |
| 106 | 163 | ## License |
| 107 | 164 | |
| 108 | 165 | Navegador is open source under the [MIT License](https://github.com/ConflictHQ/navegador/blob/main/LICENSE). Copyright 2026 CONFLICT LLC. |
| 109 | 166 |
| --- docs/index.md | |
| +++ docs/index.md | |
| @@ -3,10 +3,12 @@ | |
| 3 | **The project knowledge graph for AI coding agents.** |
| 4 | |
| 5 | Navegador builds and maintains a queryable graph of your software project — combining static code analysis with human-curated business knowledge — so that AI coding agents always have precise, structured context instead of raw file dumps. |
| 6 | |
| 7 | > *navegador* — Spanish for *navigator / sailor*. It helps agents navigate your code. |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## Two layers, one graph |
| 12 | |
| @@ -22,11 +24,11 @@ | |
| 22 | │ Variable · Import · Decorator · (call graphs, hierarchies) │ |
| 23 | └─────────────────────────────────────────────────────────────────┘ |
| 24 | stored in FalkorDB (SQLite local / Redis prod) |
| 25 | ``` |
| 26 | |
| 27 | The **code layer** is populated automatically by ingesting source trees. Python and TypeScript are supported out of the box via tree-sitter. The **knowledge layer** is populated by manual curation (`navegador add`), GitHub wiki ingestion, and [Planopticon](guide/planopticon.md) output (meeting and video knowledge graphs). |
| 28 | |
| 29 | --- |
| 30 | |
| 31 | ## Quick start |
| 32 | |
| @@ -34,10 +36,21 @@ | |
| 34 | pip install navegador # Python 3.12+ required |
| 35 | navegador ingest ./my-repo # parse + index the codebase |
| 36 | navegador explain AuthService # what is this thing? |
| 37 | navegador search "rate limit" --all # search code + knowledge together |
| 38 | ``` |
| 39 | |
| 40 | --- |
| 41 | |
| 42 | ## What goes in the graph |
| 43 | |
| @@ -52,10 +65,11 @@ | |
| 52 | | Knowledge | Decision | `navegador add decision` | |
| 53 | | Knowledge | Person | `navegador add person` | |
| 54 | | Knowledge | WikiPage | `navegador wiki ingest` | |
| 55 | | Knowledge | (any) | `navegador planopticon ingest` | |
| 56 | | Cross-layer | ANNOTATES, GOVERNS, IMPLEMENTS | `navegador annotate` | |
| 57 | |
| 58 | --- |
| 59 | |
| 60 | ## Agent integration |
| 61 | |
| @@ -87,11 +101,11 @@ | |
| 87 | } |
| 88 | } |
| 89 | } |
| 90 | ``` |
| 91 | |
| 92 | See [MCP Integration](guide/mcp-integration.md) for the full tool list and per-agent config snippets. |
| 93 | |
| 94 | === "Bootstrap" |
| 95 | |
| 96 | One command to install navegador, ingest a repo, and wire the agent hook for your preferred AI coding assistant. |
| 97 | |
| @@ -99,10 +113,53 @@ | |
| 99 | ./bootstrap.sh --repo owner/repo --wiki --agent claude |
| 100 | ``` |
| 101 | |
| 102 | Supports `--agent claude`, `--agent gemini`, and `--agent openai`. See [Agent Hooks](guide/agent-hooks.md) for what the hook does and how to configure it manually. |
| 103 | |
| 104 | --- |
| 105 | |
| 106 | ## License |
| 107 | |
| 108 | Navegador is open source under the [MIT License](https://github.com/ConflictHQ/navegador/blob/main/LICENSE). Copyright 2026 CONFLICT LLC. |
| 109 |
| --- docs/index.md | |
| +++ docs/index.md | |
| @@ -3,10 +3,12 @@ | |
| 3 | **The project knowledge graph for AI coding agents.** |
| 4 | |
| 5 | Navegador builds and maintains a queryable graph of your software project — combining static code analysis with human-curated business knowledge — so that AI coding agents always have precise, structured context instead of raw file dumps. |
| 6 | |
| 7 | > *navegador* — Spanish for *navigator / sailor*. It helps agents navigate your code. |
| 8 | > |
| 9 | > **Current version: 0.7.0** |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## Two layers, one graph |
| 14 | |
| @@ -22,11 +24,11 @@ | |
| 24 | │ Variable · Import · Decorator · (call graphs, hierarchies) │ |
| 25 | └─────────────────────────────────────────────────────────────────┘ |
| 26 | stored in FalkorDB (SQLite local / Redis prod) |
| 27 | ``` |
| 28 | |
| 29 | The **code layer** is populated automatically by ingesting source trees. 13 languages are supported via tree-sitter (Python, TypeScript, JavaScript, Go, Rust, Java, Kotlin, C#, PHP, Ruby, Swift, C, C++). The **knowledge layer** is populated by manual curation (`navegador add`), GitHub wiki ingestion, and [Planopticon](guide/planopticon.md) output (meeting and video knowledge graphs). |
| 30 | |
| 31 | --- |
| 32 | |
| 33 | ## Quick start |
| 34 | |
| @@ -34,10 +36,21 @@ | |
| 36 | pip install navegador # Python 3.12+ required |
| 37 | navegador ingest ./my-repo # parse + index the codebase |
| 38 | navegador explain AuthService # what is this thing? |
| 39 | navegador search "rate limit" --all # search code + knowledge together |
| 40 | ``` |
| 41 | |
| 42 | Or use the Python SDK: |
| 43 | |
| 44 | ```python |
| 45 | from navegador import Navegador |
| 46 | |
| 47 | nav = Navegador(".navegador/navegador.db") |
| 48 | nav.ingest("./my-repo") |
| 49 | bundle = nav.explain("AuthService") |
| 50 | print(bundle.to_markdown()) |
| 51 | ``` |
| 52 | |
| 53 | --- |
| 54 | |
| 55 | ## What goes in the graph |
| 56 | |
| @@ -52,10 +65,11 @@ | |
| 65 | | Knowledge | Decision | `navegador add decision` | |
| 66 | | Knowledge | Person | `navegador add person` | |
| 67 | | Knowledge | WikiPage | `navegador wiki ingest` | |
| 68 | | Knowledge | (any) | `navegador planopticon ingest` | |
| 69 | | Cross-layer | ANNOTATES, GOVERNS, IMPLEMENTS | `navegador annotate` | |
| 70 | | Analysis | TESTS, COUPLED_WITH edges | `navegador testmap`, `navegador cycles` | |
| 71 | |
| 72 | --- |
| 73 | |
| 74 | ## Agent integration |
| 75 | |
| @@ -87,11 +101,11 @@ | |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | ``` |
| 105 | |
| 106 | 11 tools available. See [MCP Integration](guide/mcp-integration.md) for the full tool list and per-agent config snippets. Use `--read-only` mode to restrict agents to query-only access. |
| 107 | |
| 108 | === "Bootstrap" |
| 109 | |
| 110 | One command to install navegador, ingest a repo, and wire the agent hook for your preferred AI coding assistant. |
| 111 | |
| @@ -99,10 +113,53 @@ | |
| 113 | ./bootstrap.sh --repo owner/repo --wiki --agent claude |
| 114 | ``` |
| 115 | |
| 116 | Supports `--agent claude`, `--agent gemini`, and `--agent openai`. See [Agent Hooks](guide/agent-hooks.md) for what the hook does and how to configure it manually. |
| 117 | |
| 118 | === "Editor integration" |
| 119 | |
| 120 | Wire navegador into your editor with one command: |
| 121 | |
| 122 | ```bash |
| 123 | navegador editor setup claude-code |
| 124 | navegador editor setup cursor |
| 125 | navegador editor setup codex |
| 126 | navegador editor setup windsurf |
| 127 | ``` |
| 128 | |
| 129 | === "CI/CD" |
| 130 | |
| 131 | Run navegador in CI pipelines for automated context checks: |
| 132 | |
| 133 | ```bash |
| 134 | navegador ci ingest |
| 135 | navegador ci stats |
| 136 | navegador ci check |
| 137 | ``` |
| 138 | |
| 139 | --- |
| 140 | |
| 141 | ## What's new in 0.7.0 |
| 142 | |
| 143 | | Feature | Command / API | |
| 144 | |---|---| |
| 145 | | **13 languages** (added Kotlin, C#, PHP, Ruby, Swift, C, C++) | `pip install "navegador[languages]"` | |
| 146 | | **Python SDK** | `from navegador import Navegador` | |
| 147 | | **Incremental ingestion** | `navegador ingest --incremental`, `--watch` | |
| 148 | | **Schema migrations** | `navegador migrate` | |
| 149 | | **Export / import** | `navegador export`, `navegador import` (JSONL) | |
| 150 | | **Editor integrations** | `navegador editor setup <editor>` | |
| 151 | | **Analysis commands** | `navegador diff`, `navegador churn`, `navegador impact`, `navegador trace`, `navegador deadcode`, `navegador cycles`, `navegador testmap` | |
| 152 | | **Multi-repo** | `navegador repo add/list/ingest-all/search` | |
| 153 | | **Semantic search** | `navegador semantic-search`, `navegador ask` | |
| 154 | | **Framework enrichment** | Django, FastAPI, React, Rails, Spring Boot, Laravel, and more | |
| 155 | | **Monorepo support** | Turborepo, Nx, Yarn, pnpm, Cargo, Go workspaces | |
| 156 | | **Cluster mode** | Shared Redis graph, pub/sub, task queue, sessions | |
| 157 | | **11 MCP tools** (was 7) | `get_rationale`, `find_owners`, `search_knowledge`, `blast_radius` added | |
| 158 | | **Sensitive content redaction** | `navegador ingest --redact` | |
| 159 | | **Shell completions** | `navegador completions bash/zsh/fish` | |
| 160 | |
| 161 | --- |
| 162 | |
| 163 | ## License |
| 164 | |
| 165 | Navegador is open source under the [MIT License](https://github.com/ConflictHQ/navegador/blob/main/LICENSE). Copyright 2026 CONFLICT LLC. |
| 166 |
+8
| --- mkdocs.yml | ||
| +++ mkdocs.yml | ||
| @@ -79,19 +79,27 @@ | ||
| 79 | 79 | - Quick Start: getting-started/quickstart.md |
| 80 | 80 | - Configuration: getting-started/configuration.md |
| 81 | 81 | - Guide: |
| 82 | 82 | - Ingesting a Repo: guide/ingestion.md |
| 83 | 83 | - Loading Context: guide/context-loading.md |
| 84 | + - Framework Enrichment: guide/framework-enrichment.md | |
| 85 | + - Structural Analysis: guide/analysis.md | |
| 86 | + - Intelligence Layer: guide/intelligence.md | |
| 87 | + - Python SDK: guide/sdk.md | |
| 84 | 88 | - Graph Queries: guide/graph-queries.md |
| 85 | 89 | - MCP Integration: guide/mcp-integration.md |
| 90 | + - CI/CD: guide/ci-cd.md | |
| 91 | + - Cluster Mode: guide/cluster.md | |
| 86 | 92 | - Agent Hooks: guide/agent-hooks.md |
| 87 | 93 | - Planopticon: guide/planopticon.md |
| 88 | 94 | - Architecture: |
| 89 | 95 | - Overview: architecture/overview.md |
| 90 | 96 | - Graph Schema: architecture/graph-schema.md |
| 91 | 97 | - API Reference: |
| 98 | + - Python SDK: api/sdk.md | |
| 92 | 99 | - Ingestion: api/ingestion.md |
| 100 | + - Analysis: api/analysis.md | |
| 93 | 101 | - Graph: api/graph.md |
| 94 | 102 | - MCP Server: api/mcp.md |
| 95 | 103 | |
| 96 | 104 | extra: |
| 97 | 105 | social: |
| 98 | 106 |
| --- mkdocs.yml | |
| +++ mkdocs.yml | |
| @@ -79,19 +79,27 @@ | |
| 79 | - Quick Start: getting-started/quickstart.md |
| 80 | - Configuration: getting-started/configuration.md |
| 81 | - Guide: |
| 82 | - Ingesting a Repo: guide/ingestion.md |
| 83 | - Loading Context: guide/context-loading.md |
| 84 | - Graph Queries: guide/graph-queries.md |
| 85 | - MCP Integration: guide/mcp-integration.md |
| 86 | - Agent Hooks: guide/agent-hooks.md |
| 87 | - Planopticon: guide/planopticon.md |
| 88 | - Architecture: |
| 89 | - Overview: architecture/overview.md |
| 90 | - Graph Schema: architecture/graph-schema.md |
| 91 | - API Reference: |
| 92 | - Ingestion: api/ingestion.md |
| 93 | - Graph: api/graph.md |
| 94 | - MCP Server: api/mcp.md |
| 95 | |
| 96 | extra: |
| 97 | social: |
| 98 |
| --- mkdocs.yml | |
| +++ mkdocs.yml | |
| @@ -79,19 +79,27 @@ | |
| 79 | - Quick Start: getting-started/quickstart.md |
| 80 | - Configuration: getting-started/configuration.md |
| 81 | - Guide: |
| 82 | - Ingesting a Repo: guide/ingestion.md |
| 83 | - Loading Context: guide/context-loading.md |
| 84 | - Framework Enrichment: guide/framework-enrichment.md |
| 85 | - Structural Analysis: guide/analysis.md |
| 86 | - Intelligence Layer: guide/intelligence.md |
| 87 | - Python SDK: guide/sdk.md |
| 88 | - Graph Queries: guide/graph-queries.md |
| 89 | - MCP Integration: guide/mcp-integration.md |
| 90 | - CI/CD: guide/ci-cd.md |
| 91 | - Cluster Mode: guide/cluster.md |
| 92 | - Agent Hooks: guide/agent-hooks.md |
| 93 | - Planopticon: guide/planopticon.md |
| 94 | - Architecture: |
| 95 | - Overview: architecture/overview.md |
| 96 | - Graph Schema: architecture/graph-schema.md |
| 97 | - API Reference: |
| 98 | - Python SDK: api/sdk.md |
| 99 | - Ingestion: api/ingestion.md |
| 100 | - Analysis: api/analysis.md |
| 101 | - Graph: api/graph.md |
| 102 | - MCP Server: api/mcp.md |
| 103 | |
| 104 | extra: |
| 105 | social: |
| 106 |