Navegador

init: scaffold navegador — AST + knowledge graph context engine - Package structure: ingestion/, graph/, context/, mcp/, cli/ - tree-sitter AST parsers for Python and TypeScript - FalkorDB property graph (falkordblite/SQLite local, Redis production) - ContextLoader: file, function, class context bundles (JSON + markdown) - MCP server with 7 tools for AI agent integration - CLI: ingest, context, search, stats, mcp commands - MkDocs docs site wired to navegador.dev - CI, publish, and docs GitHub Actions workflows - MIT license, pre-commit (ruff), CONTRIBUTING, SECURITY

anonymous 2026-03-22 21:02 trunk
Commit 5e4b8e49561fed9672820667bfbe1a50349406711b84e5df318f29c4ae6854f6
+15
--- a/.env.example
+++ b/.env.example
@@ -0,0 +1,15 @@
1
+# Navegador — environment configuration
2
+
3
+# Graph database
4
+# SQLite (local, zero-infra default)
5
+NAVEGADOR_DB_PATH=.navegador/graph.db
6
+
7
+# Redis-backed FalkorDB (production)
8
+# NAVEGADOR_REDIS_URL=redis://localhost:6379
9
+
10
+# MCP server
11
+NAVEGADOR_MCP_HOST=127.0.0.1
12
+NAVEGADOR_MCP_PORT=8765
13
+
14
+# Logging
15
+NAVEGADOR_LOG_LEVEL=INFO
--- a/.env.example
+++ b/.env.example
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/.env.example
+++ b/.env.example
@@ -0,0 +1,15 @@
1 # Navegador — environment configuration
2
3 # Graph database
4 # SQLite (local, zero-infra default)
5 NAVEGADOR_DB_PATH=.navegador/graph.db
6
7 # Redis-backed FalkorDB (production)
8 # NAVEGADOR_REDIS_URL=redis://localhost:6379
9
10 # MCP server
11 NAVEGADOR_MCP_HOST=127.0.0.1
12 NAVEGADOR_MCP_PORT=8765
13
14 # Logging
15 NAVEGADOR_LOG_LEVEL=INFO
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,39 @@
1
+# Contributing to Navegador
2
+
3
+Thanks for your interest in contributing!
4
+
5
+## Development setup
6
+
7
+```bash
8
+git clone https://github.com/ConflictHQ/navegador
9
+cd navegador
10
+python -m venv .venv && source .venv/bin/activate
11
+pip install -e ".[dev]"
12
+pre-commit install
13
+```
14
+
15
+## Running tests
16
+
17
+```bash
18
+pytest tests/ -v
19
+```
20
+
21
+## Code style
22
+
23
+We use `ruff` for linting and formatting. Pre-commit hooks run automatically on commit.
24
+
25
+```bash
26
+ruff check navegador/
27
+ruff format navegador/
28
+```
29
+
30
+## Pull requests
31
+
32
+1. Fork the repo and create a branch from `main`
33
+2. Add tests for new behaviour
34
+3. Ensure CI passes
35
+4. Open a PR with a clear description of the change
36
+
37
+## Commit messages
38
+
39
+Use the imperative mood: `add X`, `fix Y`, `update Z`. Keep the first line under 72 characters.
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,39 @@
1 # Contributing to Navegador
2
3 Thanks for your interest in contributing!
4
5 ## Development setup
6
7 ```bash
8 git clone https://github.com/ConflictHQ/navegador
9 cd navegador
10 python -m venv .venv && source .venv/bin/activate
11 pip install -e ".[dev]"
12 pre-commit install
13 ```
14
15 ## Running tests
16
17 ```bash
18 pytest tests/ -v
19 ```
20
21 ## Code style
22
23 We use `ruff` for linting and formatting. Pre-commit hooks run automatically on commit.
24
25 ```bash
26 ruff check navegador/
27 ruff format navegador/
28 ```
29
30 ## Pull requests
31
32 1. Fork the repo and create a branch from `main`
33 2. Add tests for new behaviour
34 3. Ensure CI passes
35 4. Open a PR with a clear description of the change
36
37 ## Commit messages
38
39 Use the imperative mood: `add X`, `fix Y`, `update Z`. Keep the first line under 72 characters.
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,17 @@
1
+## Summary
2
+
3
+<!-- What does this PR do? -->
4
+
5
+## Changes
6
+
7
+-
8
+
9
+## Testing
10
+
11
+<!-- How was this tested? -->
12
+
13
+## Checklist
14
+
15
+- [ ] Tests added / updated
16
+- [ ] Docs updated (if needed)
17
+- [ ] `ruff check` passes
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,17 @@
1 ## Summary
2
3 <!-- What does this PR do? -->
4
5 ## Changes
6
7 -
8
9 ## Testing
10
11 <!-- How was this tested? -->
12
13 ## Checklist
14
15 - [ ] Tests added / updated
16 - [ ] Docs updated (if needed)
17 - [ ] `ruff check` passes
--- a/.github/SECURITY.md
+++ b/.github/SECURITY.md
@@ -0,0 +1,18 @@
1
+# Security Policy
2
+
3
+## Supported Versions
4
+
5
+| Version | Supported |
6
+| ------- | --------- |
7
+| 0.x | ✅ |
8
+
9
+## Reporting a Vulnerability
10
+
11
+Please **do not** open a public GitHub issue for security vulnerabilities.
12
+
13
+Report vulnerabilities by emailing **[email protected]** with:
14
+- A description of the vulnerability
15
+- Steps to reproduce
16
+- Potential impact
17
+
18
+You will receive a response within 48 hours. We will work with you on a fix and coordinate disclosure.
--- a/.github/SECURITY.md
+++ b/.github/SECURITY.md
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/.github/SECURITY.md
+++ b/.github/SECURITY.md
@@ -0,0 +1,18 @@
1 # Security Policy
2
3 ## Supported Versions
4
5 | Version | Supported |
6 | ------- | --------- |
7 | 0.x | ✅ |
8
9 ## Reporting a Vulnerability
10
11 Please **do not** open a public GitHub issue for security vulnerabilities.
12
13 Report vulnerabilities by emailing **[email protected]** with:
14 - A description of the vulnerability
15 - Steps to reproduce
16 - Potential impact
17
18 You will receive a response within 48 hours. We will work with you on a fix and coordinate disclosure.
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,15 @@
1
+name: CI
2
+
3
+on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ matrix:
14
+ os: [ubuntu-latest, macos-latest]
15
+ python-version: ["3.10", "3.11",N@CV,9T@4L,2:2"G@4W,Y@7L,2a@Eb,2uWjkR;
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,15 @@
1 name: CI
2
3 on:
4 push:
5 branches: [main]
6 pull_request:
7 branches: [main]
8
9 jobs:
10 test:
11 runs-on: ${{ matrix.os }}
12 strategy:
13 matrix:
14 os: [ubuntu-latest, macos-latest]
15 python-version: ["3.10", "3.11",N@CV,9T@4L,2:2"G@4W,Y@7L,2a@Eb,2uWjkR;
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,30 @@
1
+name: Deploy Docs
2
+
3
+on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "docs/**"
8
+ - "mkdocs.yml"
9
+
10
+permissions:
11
+ contents: write
12
+ pages: write
13
+ id-token: write
14
+
15
+jobs:
16
+ deploy:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+
25
+ - name: Install dependencies
26
+ run: |
27
+ pip install mkdocs-material mkdocstrings[python] pymdown-extensions
28
+
29
+ - name: Build and deploy
30
+ run: mkdocs gh-deploy --force
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,30 @@
1 name: Deploy Docs
2
3 on:
4 push:
5 branches: [main]
6 paths:
7 - "docs/**"
8 - "mkdocs.yml"
9
10 permissions:
11 contents: write
12 pages: write
13 id-token: write
14
15 jobs:
16 deploy:
17 runs-on: ubuntu-latest
18 steps:
19 - uses: actions/checkout@v4
20
21 - uses: actions/setup-python@v5
22 with:
23 python-version: "3.12"
24
25 - name: Install dependencies
26 run: |
27 pip install mkdocs-material mkdocstrings[python] pymdown-extensions
28
29 - name: Build and deploy
30 run: mkdocs gh-deploy --force
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,26 @@
1
+name: Publish to PyPI
2
+
3
+on:
4
+ release:
5
+ types: [published]
6
+
7
+jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: "3.12"
19
+
20
+ - name: Build package
21
+ run: |
22
+ pip install build
23
+ python -m build
24
+
25
+ - name: Publish to PyPI
26
+ uses: pypa/gh-action-pypi-publish@release/v1
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,26 @@
1 name: Publish to PyPI
2
3 on:
4 release:
5 types: [published]
6
7 jobs:
8 publish:
9 runs-on: ubuntu-latest
10 environment: pypi
11 permissions:
12 id-token: write
13 steps:
14 - uses: actions/checkout@v4
15
16 - uses: actions/setup-python@v5
17 with:
18 python-version: "3.12"
19
20 - name: Build package
21 run: |
22 pip install build
23 python -m build
24
25 - name: Publish to PyPI
26 uses: pypa/gh-action-pypi-publish@release/v1
+73
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,73 @@
1
+# Byte-compiled / optimized / DLL files
2
+__pycache__/
3
+*.py[cod]
4
+*$py.class
5
+
6
+# C extensions
7
+*.so
8
+
9
+# Distribution / packaging
10
+dist/
11
+build/
12
+*.egg-info/
13
+
14
+# Unit test / coverage reports
15
+htmlcov/
16
+.tox/
17
+.coverage
18
+.coverage.*
19
+.cache
20
+.pytest_cache/
21
+.mypy_cache/
22
+coverage.xml
23
+*.cover
24
+
25
+# Virtual environments
26
+.venv/
27
+venv/
28
+env/
29
+ENV/
30
+
31
+# IDE and editor files
32
+.idea/
33
+.vscode/
34
+.cursor/
35
+*.swp
36
+*.swo
37
+
38
+# AI tools
39
+.claude/
40
+.gemini/
41
+.codex/
42
+.aider/
43
+.continue/
44
+.copilot/
45
+AGENTS.md
46
+GEMINI.md
47
+
48
+# API keys and secrets
49
+.env
50
+.env.*
51
+!.env.example
52
+*service_account*.json
53
+*credentials*.json
54
+
55
+# Log files
56
+*.log
57
+
58
+# OS specific
59
+.DS_Store
60
+Thumbs.db
61
+
62
+# MkDocs build output
63
+site/
64
+
65
+# Navegador runtime
66
+*.db
67
+*.db-shm
68
+*.db-wal
69
+.navegador/
70
+navegador-graph/
71
+
72
+# Ruff cache
73
+.ruff_cache/
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,73 @@
1 # Byte-compiled / optimized / DLL files
2 __pycache__/
3 *.py[cod]
4 *$py.class
5
6 # C extensions
7 *.so
8
9 # Distribution / packaging
10 dist/
11 build/
12 *.egg-info/
13
14 # Unit test / coverage reports
15 htmlcov/
16 .tox/
17 .coverage
18 .coverage.*
19 .cache
20 .pytest_cache/
21 .mypy_cache/
22 coverage.xml
23 *.cover
24
25 # Virtual environments
26 .venv/
27 venv/
28 env/
29 ENV/
30
31 # IDE and editor files
32 .idea/
33 .vscode/
34 .cursor/
35 *.swp
36 *.swo
37
38 # AI tools
39 .claude/
40 .gemini/
41 .codex/
42 .aider/
43 .continue/
44 .copilot/
45 AGENTS.md
46 GEMINI.md
47
48 # API keys and secrets
49 .env
50 .env.*
51 !.env.example
52 *service_account*.json
53 *credentials*.json
54
55 # Log files
56 *.log
57
58 # OS specific
59 .DS_Store
60 Thumbs.db
61
62 # MkDocs build output
63 site/
64
65 # Navegador runtime
66 *.db
67 *.db-shm
68 *.db-wal
69 .navegador/
70 navegador-graph/
71
72 # Ruff cache
73 .ruff_cache/
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,18 @@
1
+repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.9.7
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
8
+ - repo: https://github.com/pre-commit/pre-commit-hooks
9
+ rev: v5.0.0
10
+ hooks:
11
+ - id: trailing-whitespace
12
+ - id: end-of-file-fixer
13
+ - id: check-yaml
14
+ args: [--unsafe]
15
+ - id: check-added-large-files
16
+ args: [--maxkb=500]
17
+ - id: check-merge-conflict
18
+ - id: detect-private-key
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,18 @@
1 repos:
2 - repo: https://github.com/astral-sh/ruff-pre-commit
3 rev: v0.9.7
4 hooks:
5 - id: ruff
6 args: [--fix]
7 - id: ruff-format
8 - repo: https://github.com/pre-commit/pre-commit-hooks
9 rev: v5.0.0
10 hooks:
11 - id: trailing-whitespace
12 - id: end-of-file-fixer
13 - id: check-yaml
14 args: [--unsafe]
15 - id: check-added-large-files
16 args: [--maxkb=500]
17 - id: check-merge-conflict
18 - id: detect-private-key
+12
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
1
+# Changelog
2
+
3
+## 0.1.0 — 2026-03-22
4
+
5
+Initial release scaffold.
6
+
7
+- AST ingestion pipeline (Python + TypeScript via tree-sitter)
8
+- Property graph storage via FalkorDB-lite (SQLite) or Redis
9
+- Context bundles: file, function, class context loading
10
+- MCP server with 7 tools for AI agent integration
11
+- CLI: `ingest`, `context`, `search`, `stats`, `mcp`
12
+- MkDocs documentation site (navegador.dev)
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
1 # Changelog
2
3 ## 0.1.0 — 2026-03-22
4
5 Initial release scaffold.
6
7 - AST ingestion pipeline (Python + TypeScript via tree-sitter)
8 - Property graph storage via FalkorDB-lite (SQLite) or Redis
9 - Context bundles: file, function, class context loading
10 - MCP server with 7 tools for AI agent integration
11 - CLI: `ingest`, `context`, `search`, `stats`, `mcp`
12 - MkDocs documentation site (navegador.dev)
+50
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -0,0 +1,50 @@
1
+# Navegador — Claude Context
2
+
3
+## What it is
4
+
5
+AST + knowledge graph context engine for AI coding agents. Parses codebases into a FalkorDB property graph. Agents query via MCP or Python API.
6
+
7
+## Stack
8
+
9
+- **Python 3.10+**, standalone (no Django dependency)
10
+- **tree-sitter** for multi-language AST parsing (`tree-sitter-pythone AST parsing (13 languages)
11
+- **FalkorDB** graph DB with **falkordblite** (SQLite via redislite) for local use
12
+- **MCP** (`mcp` Python SD
13
+- **Click + Rich** for CLI
14
+- **Pydantic** for data models
15
+- **Ruff** for linting/formatting
16
+
17
+## Package layout
18
+
19
+```
20
+navegador/
21
+ cli/ — Click commands (ingest, context, search, stats, mcp)
22
+ graph/ — GraphStore + schema + Cypher query templates
23
+ ingestion/ — RepoIngester + language parsers (python.py+ optimization
24
+ context/ — ContextLoader + ContextBundle (JSON/markdown)
25
+ mcp/ — MCP server with 11 tools + security hardening
26
+ enrichment/ — FrameworkEnricher base + 8 framework enrichers
27
+ analysis/ — impact, flow tracing, dead code, cycles, test mapping
28
+ intelligence/ — semantic search, community detection, NLP, doc generation
29
+ cluster/ — Redis pub/sub, task queue, locking, sessions, messaging
30
+ sdk.py — Python SDK (Navegador class)
31
+ llm.py — LLM provider abstraction (Anthropic, OpenAI, Ollama)
32
+ vcs.py — VCS abstraction (Git, Fossil)
33
+ diff.py — Git diff → graph impact mapping
34
+ churn.py — Behavioural coupling from git history
35
+ monorepo.py — Workspace detection + ingestion
36
+ security.py — Sensitive content detection + redaction
37
+ explorer/ — HTTP server + browser-based graph visualization
38
+```
39
+
40
+## FalkorDB connection
41
+
42
+```python
43
+# SQLite (local, zero-infra) — uses falkordblite
44
+from redislite import FalkorDB # falkordblite provides this
45
+db = FalkorDB("path/to/graph.db")
46
+graph = db.select_graph("navegador")
47
+
48
+# Redis (production)
49
+import falkordb
50
+client = falkordb.Fa
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -0,0 +1,50 @@
1 # Navegador — Claude Context
2
3 ## What it is
4
5 AST + knowledge graph context engine for AI coding agents. Parses codebases into a FalkorDB property graph. Agents query via MCP or Python API.
6
7 ## Stack
8
9 - **Python 3.10+**, standalone (no Django dependency)
10 - **tree-sitter** for multi-language AST parsing (`tree-sitter-pythone AST parsing (13 languages)
11 - **FalkorDB** graph DB with **falkordblite** (SQLite via redislite) for local use
12 - **MCP** (`mcp` Python SD
13 - **Click + Rich** for CLI
14 - **Pydantic** for data models
15 - **Ruff** for linting/formatting
16
17 ## Package layout
18
19 ```
20 navegador/
21 cli/ — Click commands (ingest, context, search, stats, mcp)
22 graph/ — GraphStore + schema + Cypher query templates
23 ingestion/ — RepoIngester + language parsers (python.py+ optimization
24 context/ — ContextLoader + ContextBundle (JSON/markdown)
25 mcp/ — MCP server with 11 tools + security hardening
26 enrichment/ — FrameworkEnricher base + 8 framework enrichers
27 analysis/ — impact, flow tracing, dead code, cycles, test mapping
28 intelligence/ — semantic search, community detection, NLP, doc generation
29 cluster/ — Redis pub/sub, task queue, locking, sessions, messaging
30 sdk.py — Python SDK (Navegador class)
31 llm.py — LLM provider abstraction (Anthropic, OpenAI, Ollama)
32 vcs.py — VCS abstraction (Git, Fossil)
33 diff.py — Git diff → graph impact mapping
34 churn.py — Behavioural coupling from git history
35 monorepo.py — Workspace detection + ingestion
36 security.py — Sensitive content detection + redaction
37 explorer/ — HTTP server + browser-based graph visualization
38 ```
39
40 ## FalkorDB connection
41
42 ```python
43 # SQLite (local, zero-infra) — uses falkordblite
44 from redislite import FalkorDB # falkordblite provides this
45 db = FalkorDB("path/to/graph.db")
46 graph = db.select_graph("navegador")
47
48 # Redis (production)
49 import falkordb
50 client = falkordb.Fa
+21
--- a/LICENSE
+++ b/LICENSE
@@ -0,0 +1,21 @@
1
+MIT License
2
+
3
+Copy. All rights reserved.
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in all
13
+copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+SOFTWARE.
--- a/LICENSE
+++ b/LICENSE
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/LICENSE
+++ b/LICENSE
@@ -0,0 +1,21 @@
1 MIT License
2
3 Copy. All rights reserved.
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in all
13 copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 SOFTWARE.
+23
--- a/README.md
+++ b/README.md
@@ -0,0 +1,23 @@
1
+# Navegador
2
+
3
+**AST + knowledge graph context engine for AI coding agents.**
4
+
5
+Navegador parses your codebase into a property graph and makes it queryable. AI coding agents can ask "what calls this function?", "what does this file depend on?", or "show me everythianswersWhy
6
+
7
+AI coding agents load context by reading raw files. They don't know what calls what, what depends on what, or which 5 functions out of 500 are actually relevant. Navegador builds a structured map — then exposes it via MCP so any agent can navigate your code with precisionfor a symbol
8
+Graph schema
9
+
10
+**N
11
+
12
+---
13
+
14
+## Storage
15
+
16
+Navegador uses **FalkorDB** (property graph, Cypher queries).— in one queryable graph.**
17
+
18
+Navegador parses your source code int# Navegador
19
+
20
+**Your cod# SQLite (default)
21
+
22
+# Redis LLCf 500 are actually relevant. Na)
23
+, Rust, Java | Planned
--- a/README.md
+++ b/README.md
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/README.md
+++ b/README.md
@@ -0,0 +1,23 @@
1 # Navegador
2
3 **AST + knowledge graph context engine for AI coding agents.**
4
5 Navegador parses your codebase into a property graph and makes it queryable. AI coding agents can ask "what calls this function?", "what does this file depend on?", or "show me everythianswersWhy
6
7 AI coding agents load context by reading raw files. They don't know what calls what, what depends on what, or which 5 functions out of 500 are actually relevant. Navegador builds a structured map — then exposes it via MCP so any agent can navigate your code with precisionfor a symbol
8 Graph schema
9
10 **N
11
12 ---
13
14 ## Storage
15
16 Navegador uses **FalkorDB** (property graph, Cypher queries).— in one queryable graph.**
17
18 Navegador parses your source code int# Navegador
19
20 **Your cod# SQLite (default)
21
22 # Redis LLCf 500 are actually relevant. Na)
23 , Rust, Java | Planned
+1
--- a/docs/CNAME
+++ b/docs/CNAME
@@ -0,0 +1 @@
1
+navegador.dev
--- a/docs/CNAME
+++ b/docs/CNAME
@@ -0,0 +1 @@
 
--- a/docs/CNAME
+++ b/docs/CNAME
@@ -0,0 +1 @@
1 navegador.dev
--- a/docs/api/graph.md
+++ b/docs/api/graph.md
@@ -0,0 +1,3 @@
1
+# graph
2
+
3
+Documentation coming soon.
--- a/docs/api/graph.md
+++ b/docs/api/graph.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/api/graph.md
+++ b/docs/api/graph.md
@@ -0,0 +1,3 @@
1 # graph
2
3 Documentation coming soon.
--- a/docs/api/ingestion.md
+++ b/docs/api/ingestion.md
@@ -0,0 +1,3 @@
1
+# ingestion
2
+
3
+Documentation coming soon.
--- a/docs/api/ingestion.md
+++ b/docs/api/ingestion.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/api/ingestion.md
+++ b/docs/api/ingestion.md
@@ -0,0 +1,3 @@
1 # ingestion
2
3 Documentation coming soon.
--- a/docs/api/mcp.md
+++ b/docs/api/mcp.md
@@ -0,0 +1,3 @@
1
+# mcp
2
+
3
+Documentation coming soon.
--- a/docs/api/mcp.md
+++ b/docs/api/mcp.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/api/mcp.md
+++ b/docs/api/mcp.md
@@ -0,0 +1,3 @@
1 # mcp
2
3 Documentation coming soon.
--- a/docs/architecture/graph-schema.md
+++ b/docs/architecture/graph-schema.md
@@ -0,0 +1,3 @@
1
+# graph-schema
2
+
3
+Documentation coming soon.
--- a/docs/architecture/graph-schema.md
+++ b/docs/architecture/graph-schema.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/architecture/graph-schema.md
+++ b/docs/architecture/graph-schema.md
@@ -0,0 +1,3 @@
1 # graph-schema
2
3 Documentation coming soon.
--- a/docs/architecture/overview.md
+++ b/docs/architecture/overview.md
@@ -0,0 +1,3 @@
1
+# overview
2
+
3
+Documentation coming soon.
--- a/docs/architecture/overview.md
+++ b/docs/architecture/overview.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/architecture/overview.md
+++ b/docs/architecture/overview.md
@@ -0,0 +1,3 @@
1 # overview
2
3 Documentation coming soon.
--- a/docs/assets/css/custom.css
+++ b/docs/assets/css/custom.css
@@ -0,0 +1,16 @@
1
+/* Navegador — MkDocs Material custom theme */
2
+
3
+:root {
4
+ --md-primary-fg-color: #DC394C;
5
+ --md-primary-fg-color--light: #e8677a;
6
+ --md-primary-fg-color--dark: #8B3138;
7
+ --md-accent-fg-color: #DC394C;
8
+}
9
+
10
+[data-md-color-scheme="slate"] {
11
+ --md-primary-fg-color: #DC394C;
12
+ --md-primary-fg-color--light: #e8677a;
13
+ --md-primary-fg-color--dark: #8B3138;
14
+ --md-default-bg-color: #1a1c1b;
15
+ --md-code-bg-color: #222423;
16
+}
--- a/docs/assets/css/custom.css
+++ b/docs/assets/css/custom.css
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/docs/assets/css/custom.css
+++ b/docs/assets/css/custom.css
@@ -0,0 +1,16 @@
1 /* Navegador — MkDocs Material custom theme */
2
3 :root {
4 --md-primary-fg-color: #DC394C;
5 --md-primary-fg-color--light: #e8677a;
6 --md-primary-fg-color--dark: #8B3138;
7 --md-accent-fg-color: #DC394C;
8 }
9
10 [data-md-color-scheme="slate"] {
11 --md-primary-fg-color: #DC394C;
12 --md-primary-fg-color--light: #e8677a;
13 --md-primary-fg-color--dark: #8B3138;
14 --md-default-bg-color: #1a1c1b;
15 --md-code-bg-color: #222423;
16 }
--- a/docs/getting-started/configuration.md
+++ b/docs/getting-started/configuration.md
@@ -0,0 +1,3 @@
1
+# configuration
2
+
3
+Documentation coming soon.
--- a/docs/getting-started/configuration.md
+++ b/docs/getting-started/configuration.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/getting-started/configuration.md
+++ b/docs/getting-started/configuration.md
@@ -0,0 +1,3 @@
1 # configuration
2
3 Documentation coming soon.
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -0,0 +1,3 @@
1
+# installation
2
+
3
+Documentation coming soon.
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/getting-started/installation.md
+++ b/docs/getting-started/installation.md
@@ -0,0 +1,3 @@
1 # installation
2
3 Documentation coming soon.
--- a/docs/getting-started/quickstart.md
+++ b/docs/getting-started/quickstart.md
@@ -0,0 +1,3 @@
1
+# quickstart
2
+
3
+Documentation coming soon.
--- a/docs/getting-started/quickstart.md
+++ b/docs/getting-started/quickstart.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/getting-started/quickstart.md
+++ b/docs/getting-started/quickstart.md
@@ -0,0 +1,3 @@
1 # quickstart
2
3 Documentation coming soon.
--- a/docs/guide/context-loading.md
+++ b/docs/guide/context-loading.md
@@ -0,0 +1,3 @@
1
+# context-loading
2
+
3
+Documentation coming soon.
--- a/docs/guide/context-loading.md
+++ b/docs/guide/context-loading.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/guide/context-loading.md
+++ b/docs/guide/context-loading.md
@@ -0,0 +1,3 @@
1 # context-loading
2
3 Documentation coming soon.
--- a/docs/guide/graph-queries.md
+++ b/docs/guide/graph-queries.md
@@ -0,0 +1,3 @@
1
+# graph-queries
2
+
3
+Documentation coming soon.
--- a/docs/guide/graph-queries.md
+++ b/docs/guide/graph-queries.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/guide/graph-queries.md
+++ b/docs/guide/graph-queries.md
@@ -0,0 +1,3 @@
1 # graph-queries
2
3 Documentation coming soon.
--- a/docs/guide/ingestion.md
+++ b/docs/guide/ingestion.md
@@ -0,0 +1,3 @@
1
+# ingestion
2
+
3
+Documentation coming soon.
--- a/docs/guide/ingestion.md
+++ b/docs/guide/ingestion.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/guide/ingestion.md
+++ b/docs/guide/ingestion.md
@@ -0,0 +1,3 @@
1 # ingestion
2
3 Documentation coming soon.
--- a/docs/guide/mcp-integration.md
+++ b/docs/guide/mcp-integration.md
@@ -0,0 +1,3 @@
1
+# mcp-integration
2
+
3
+Documentation coming soon.
--- a/docs/guide/mcp-integration.md
+++ b/docs/guide/mcp-integration.md
@@ -0,0 +1,3 @@
 
 
 
--- a/docs/guide/mcp-integration.md
+++ b/docs/guide/mcp-integration.md
@@ -0,0 +1,3 @@
1 # mcp-integration
2
3 Documentation coming soon.
--- a/docs/index.md
+++ b/docs/index.md
@@ -0,0 +1,46 @@
1
+# Navegador
2
+
3
+**AST + knowledge graph context engine for AI coding agents.**
4
+
5
+Navegador parses your codebase into a property graph — functions, classes, files, imports, call relationships — and makes that structure queryable by AI coding agents via MCP or a Python API.
6
+
7
+> *navegador* — Spanish for *navigator / sailor*. It helps agents navigate your code.
8
+
9
+---
10
+
11
+## Why
12
+
13
+AI coding agents (Claude, Cursor, Copilot) load context by reading raw files. They don't know what calls what, what depends on what, or how to find the relevant 5 functions out of 500. Navegador gives agents a structured map.
14
+
15
+```
16
+agent: "find everything that depends on auth1/sessions.py"
17
+
18
+→ MATCH (f:File {path: "auth1/sessions.py"})<-[:IMPORTS|CALLS*1..3]-(n)
19
+→ returns: 4 files, 12 functions, full source + docstrings
20
+```
21
+
22
+---
23
+
24
+## Features
25
+
26
+- **Multi-language AST parsing** — Python and TypeScript/JavaScript via tree-sitter
27
+- **Property graph storage** — FalkorDB-lite (SQLite, zero infra) or Redis FalkorDB for production
28
+- **MCP server** — native integration with Claude, Cursor, and any MCP-compatible agent
29
+- **Context bundles** — structured JSON or markdown export of a file/function/class and its relationships
30
+- **CLI** — `navegador ingest ./myrepo`, `navegador context src/auth.py`
31
+
32
+---
33
+
34
+## Quick start
35
+
36
+```bash
37
+pip install navegador
38
+navegador ingest ./myrepo
39
+navegador context src/auth.py
40
+```
41
+
42
+---
43
+
44
+## License
45
+
46
+MIT — [CONFLICT LLC](https://github.com/ConflictHQ)
--- a/docs/index.md
+++ b/docs/index.md
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/docs/index.md
+++ b/docs/index.md
@@ -0,0 +1,46 @@
1 # Navegador
2
3 **AST + knowledge graph context engine for AI coding agents.**
4
5 Navegador parses your codebase into a property graph — functions, classes, files, imports, call relationships — and makes that structure queryable by AI coding agents via MCP or a Python API.
6
7 > *navegador* — Spanish for *navigator / sailor*. It helps agents navigate your code.
8
9 ---
10
11 ## Why
12
13 AI coding agents (Claude, Cursor, Copilot) load context by reading raw files. They don't know what calls what, what depends on what, or how to find the relevant 5 functions out of 500. Navegador gives agents a structured map.
14
15 ```
16 agent: "find everything that depends on auth1/sessions.py"
17
18 → MATCH (f:File {path: "auth1/sessions.py"})<-[:IMPORTS|CALLS*1..3]-(n)
19 → returns: 4 files, 12 functions, full source + docstrings
20 ```
21
22 ---
23
24 ## Features
25
26 - **Multi-language AST parsing** — Python and TypeScript/JavaScript via tree-sitter
27 - **Property graph storage** — FalkorDB-lite (SQLite, zero infra) or Redis FalkorDB for production
28 - **MCP server** — native integration with Claude, Cursor, and any MCP-compatible agent
29 - **Context bundles** — structured JSON or markdown export of a file/function/class and its relationships
30 - **CLI** — `navegador ingest ./myrepo`, `navegador context src/auth.py`
31
32 ---
33
34 ## Quick start
35
36 ```bash
37 pip install navegador
38 navegador ingest ./myrepo
39 navegador context src/auth.py
40 ```
41
42 ---
43
44 ## License
45
46 MIT — [CONFLICT LLC](https://github.com/ConflictHQ)
+26
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -0,0 +1,26 @@
1
+site_name: Navegador
2
+site_url: https://navegador.dev
3
+site_description: for AI coding agents — code structure, business rules, and architectural decisions in one queryable graph
4
+site_author: CONFLICT LLC
5
+repo_url: https://github.com/ConflictHQ/navegador
6
+repo_name: ConflictHQ/navegador
7
+
8
+theme:
9
+ name: material
10
+ logo: assets/images/conflict-logo.svg
11
+ favicon: assets/images/favicon.png
12
+ font:
13
+ text: Roboto
14
+ code: Roboto Mono
15
+ palette:
16
+ - media: "(prefers-color-scheme: dark)"
17
+ scheme: slate
18
+ primary: custom
19
+ accent: custom
20
+ toggle:
21
+ icon: material/brightness-4
22
+ name: Switch to light mode
23
+ - media: "(prefers-color-scheme: light)"
24
+ scheme: default
25
+ primary: custom
26
+
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -0,0 +1,26 @@
1 site_name: Navegador
2 site_url: https://navegador.dev
3 site_description: for AI coding agents — code structure, business rules, and architectural decisions in one queryable graph
4 site_author: CONFLICT LLC
5 repo_url: https://github.com/ConflictHQ/navegador
6 repo_name: ConflictHQ/navegador
7
8 theme:
9 name: material
10 logo: assets/images/conflict-logo.svg
11 favicon: assets/images/favicon.png
12 font:
13 text: Roboto
14 code: Roboto Mono
15 palette:
16 - media: "(prefers-color-scheme: dark)"
17 scheme: slate
18 primary: custom
19 accent: custom
20 toggle:
21 icon: material/brightness-4
22 name: Switch to light mode
23 - media: "(prefers-color-scheme: light)"
24 scheme: default
25 primary: custom
26
--- a/navegador/__init__.py
+++ b/navegador/__init__.py
@@ -0,0 +1,6 @@
1
+"""
2
+Navegador — AST + knowledge graph context engine for AI coding agent0.
3
+"""
4
+
5
+__version__ = "0.1""
6
+Navegador — AST + knowledge
--- a/navegador/__init__.py
+++ b/navegador/__init__.py
@@ -0,0 +1,6 @@
 
 
 
 
 
 
--- a/navegador/__init__.py
+++ b/navegador/__init__.py
@@ -0,0 +1,6 @@
1 """
2 Navegador — AST + knowledge graph context engine for AI coding agent0.
3 """
4
5 __version__ = "0.1""
6 Navegador — AST + knowledge

No diff available

--- a/navegador/cli/commands.py
+++ b/navegador/cli/commands.py
@@ -0,0 +1,41 @@
1
+"""
2
+N)
3
+ "fmt""ingest reposools without MCP ovme,rationale=rationale, date=datDecisiocodefrom pathlib import Pathe_type", "code_label", """
4
+N)
5
+ store
6
+
7
+ node_rows = (""
8
+N)
9
+ "fmt"""
10
+N)
11
+ """
12
+N)
13
+ "--redis", "redis_u"""
14
+N "fmt "fmt"""
15
+N)
16
+ """""
17
+N)
18
+ "fmt"""
19
+N)
20
+ DB_OPTIONas JSON."Show graph stat data = {"nodes": store.node_count(), "edges": store.edge_count()}dataelse:Graph stats", show_header=Truedataclick.option( ,
21
+ N)
22
+ source graph import GraphStore
23
+ MCP
24
+ AS)
25
+click.option( ,
26
+ N)
27
+ )
28
+@click.option( the navegador, sh into the nave default="markdown")and print context for a filstoreile(file_path)
29
+ output = if fmt == "markdow show_header=Trueajson(outpu_header=Truquery"option( ,
30
+ N)
31
+ )) by namstoreoption( ,
32
+ N)
33
+ )
34
+def stats(db: strrepos, load context, table.add_row("Nodes", str(store.node_count()))
35
+ table.add_row("Edges", str(option( ,
36
+ N)
37
+ )
38
+@click.option("--host", default="127.0.0.1", show_default=True)
39
+@click.option("--port", default=8765, show_default=True)
40
+def mcp(db: str, host: str, port: intdef store_factory():
41
+ store_factoryf
--- a/navegador/cli/commands.py
+++ b/navegador/cli/commands.py
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/navegador/cli/commands.py
+++ b/navegador/cli/commands.py
@@ -0,0 +1,41 @@
1 """
2 N)
3 "fmt""ingest reposools without MCP ovme,rationale=rationale, date=datDecisiocodefrom pathlib import Pathe_type", "code_label", """
4 N)
5 store
6
7 node_rows = (""
8 N)
9 "fmt"""
10 N)
11 """
12 N)
13 "--redis", "redis_u"""
14 N "fmt "fmt"""
15 N)
16 """""
17 N)
18 "fmt"""
19 N)
20 DB_OPTIONas JSON."Show graph stat data = {"nodes": store.node_count(), "edges": store.edge_count()}dataelse:Graph stats", show_header=Truedataclick.option( ,
21 N)
22 source graph import GraphStore
23 MCP
24 AS)
25 click.option( ,
26 N)
27 )
28 @click.option( the navegador, sh into the nave default="markdown")and print context for a filstoreile(file_path)
29 output = if fmt == "markdow show_header=Trueajson(outpu_header=Truquery"option( ,
30 N)
31 )) by namstoreoption( ,
32 N)
33 )
34 def stats(db: strrepos, load context, table.add_row("Nodes", str(store.node_count()))
35 table.add_row("Edges", str(option( ,
36 N)
37 )
38 @click.option("--host", default="127.0.0.1", show_default=True)
39 @click.option("--port", default=8765, show_default=True)
40 def mcp(db: str, host: str, port: intdef store_factory():
41 store_factoryf
--- a/navegador/context/__init__.py
+++ b/navegador/context/__init__.py
@@ -0,0 +1,3 @@
1
+from .loader import ContextLoader
2
+
3
+__all__ = ["ContextLoader"]
--- a/navegador/context/__init__.py
+++ b/navegador/context/__init__.py
@@ -0,0 +1,3 @@
 
 
 
--- a/navegador/context/__init__.py
+++ b/navegador/context/__init__.py
@@ -0,0 +1,3 @@
1 from .loader import ContextLoader
2
3 __all__ = ["ContextLoader"]
--- a/navegador/context/loader.py
+++ b/navegador/context/loader.py
@@ -0,0 +1,99 @@
1
+"""
2
+ContextLoader — builds structured context bundles from the graph.
3
+
4
+A context bundle contains:
5
+- The target node (file / function / class)
6
+- Its immediate neighbors up to a configurable depth
7
+- Relationships between those nodes
8
+- Source snippets (optional)
9
+
10
+Output can be:
11
+- dict (structured JSON-serialisable)
12
+- markdown string (, JSON string, or markdown fo)
13
+"""
14
+
15
+import json
16
+import logging
17
+from dataclasses import dataclass, field
18
+from pathlib import Path
19
+from typing import Any
20
+
21
+from navegador.graph import GraphStoreath
22
+from typing import Any
23
+
24
+fqueries
25
+
26
+logger = logging.getLogger(__name__)
27
+
28
+
29
+@dataclass
30
+class ContextNode:
31
+ type: str
32
+ name: str
33
+ file_path: strstr
34
+ file_path: str = ""
35
+ line_start: int | None = None
36
+ docstring: str | None = None
37
+ signature: str | None = None
38
+ date: str | None = None
39
+
40
+
41
+@dataclass
42
+class ContextBundle:
43
+ target: ContextNode
44
+ nodes: list[ContextNode] = field(default_factory=list)
45
+ edges: list[dict[str, str]] = field(default_factory=list)
46
+ metadata: dict[str, Any] = field(default_factory=dict)
47
+
48
+ def to_dict(self) -> dict[str, Any]:
49
+ return {
50
+ "target": vars(self.target),
51
+ "nodes": [vars(n) for n in self.nodes],
52
+ "edges": self.edges,
53
+ "metadata": self.metadata,
54
+ }
55
+
56
+ def to_json(self, indent: int = 2) -> str:
57
+ return json.dumps(self.to_dict(), indent=indent)
58
+
59
+ def to_markdown(self) -> str:
60
+ lines = [
61
+ f self.target.file_path:
62
+ nes = [
63
+ f"# Context: `{self.target.name}`",
64
+ f"**Type:** docstring:
65
+ader — builds s"""
66
+ContextLoader — builds struct}"]`{self.target.file_path} or self.target.description}"]
67
+ if self.target.signature:
68
+ lines += ["", f"```python\n{self.target.signature}\n```"]
69
+
70
+ ( if self.nodes:
71
+ ):[0], row[1] signatus:
72
+ """
73
+Contextr — builds structured context bundles from the graph.
74
+
75
+A contex"""
76
+ContextLoader — builds structured context bundles from the graph.
77
+
78
+A context bundle contains:
79
+- The target node (file / function / class)
80
+- Its immediate neighbors up to a configurable depth
81
+- Relationships between those nodes
82
+- Source snippets (optional)
83
+ts (optional)
84
+
85
+Output can be:
86
+- dict (structured JSON-serialisable)
87
+- m)
88
+"""
89
+ContextLoader — builds structured context bundles from the graph.
90
+
91
+A context bundle contains:
92
+- The target node (file / function / class)
93
+- Its immediate neighbors up to a configurable depth
94
+- RelationshLoader — builds structured context bundles from the graph.
95
+
96
+A context)
97
+ = None
98
+ source: str | None = None
99
+ descr
--- a/navegador/context/loader.py
+++ b/navegador/context/loader.py
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/navegador/context/loader.py
+++ b/navegador/context/loader.py
@@ -0,0 +1,99 @@
1 """
2 ContextLoader — builds structured context bundles from the graph.
3
4 A context bundle contains:
5 - The target node (file / function / class)
6 - Its immediate neighbors up to a configurable depth
7 - Relationships between those nodes
8 - Source snippets (optional)
9
10 Output can be:
11 - dict (structured JSON-serialisable)
12 - markdown string (, JSON string, or markdown fo)
13 """
14
15 import json
16 import logging
17 from dataclasses import dataclass, field
18 from pathlib import Path
19 from typing import Any
20
21 from navegador.graph import GraphStoreath
22 from typing import Any
23
24 fqueries
25
26 logger = logging.getLogger(__name__)
27
28
29 @dataclass
30 class ContextNode:
31 type: str
32 name: str
33 file_path: strstr
34 file_path: str = ""
35 line_start: int | None = None
36 docstring: str | None = None
37 signature: str | None = None
38 date: str | None = None
39
40
41 @dataclass
42 class ContextBundle:
43 target: ContextNode
44 nodes: list[ContextNode] = field(default_factory=list)
45 edges: list[dict[str, str]] = field(default_factory=list)
46 metadata: dict[str, Any] = field(default_factory=dict)
47
48 def to_dict(self) -> dict[str, Any]:
49 return {
50 "target": vars(self.target),
51 "nodes": [vars(n) for n in self.nodes],
52 "edges": self.edges,
53 "metadata": self.metadata,
54 }
55
56 def to_json(self, indent: int = 2) -> str:
57 return json.dumps(self.to_dict(), indent=indent)
58
59 def to_markdown(self) -> str:
60 lines = [
61 f self.target.file_path:
62 nes = [
63 f"# Context: `{self.target.name}`",
64 f"**Type:** docstring:
65 ader — builds s"""
66 ContextLoader — builds struct}"]`{self.target.file_path} or self.target.description}"]
67 if self.target.signature:
68 lines += ["", f"```python\n{self.target.signature}\n```"]
69
70 ( if self.nodes:
71 ):[0], row[1] signatus:
72 """
73 Contextr — builds structured context bundles from the graph.
74
75 A contex"""
76 ContextLoader — builds structured context bundles from the graph.
77
78 A context bundle contains:
79 - The target node (file / function / class)
80 - Its immediate neighbors up to a configurable depth
81 - Relationships between those nodes
82 - Source snippets (optional)
83 ts (optional)
84
85 Output can be:
86 - dict (structured JSON-serialisable)
87 - m)
88 """
89 ContextLoader — builds structured context bundles from the graph.
90
91 A context bundle contains:
92 - The target node (file / function / class)
93 - Its immediate neighbors up to a configurable depth
94 - RelationshLoader — builds structured context bundles from the graph.
95
96 A context)
97 = None
98 source: str | None = None
99 descr
--- a/navegador/graph/__init__.py
+++ b/navegador/graph/__init__.py
@@ -0,0 +1,4 @@
1
+from .schema import EdgeType, NodeLabel
2
+from .store import GraphStore
3
+
4
+__all__ = ["GraphStore", "NodeLabel", "EdgeType"]
--- a/navegador/graph/__init__.py
+++ b/navegador/graph/__init__.py
@@ -0,0 +1,4 @@
 
 
 
 
--- a/navegador/graph/__init__.py
+++ b/navegador/graph/__init__.py
@@ -0,0 +1,4 @@
1 from .schema import EdgeType, NodeLabel
2 from .store import GraphStore
3
4 __all__ = ["GraphStore", "NodeLabel", "EdgeType"]
--- a/navegador/graph/queries.py
+++ b/navegador/graph/queries.py
@@ -0,0 +1,50 @@
1
+"""
2
+Common context loading.
3
+"""
4
+
5
+# Find al
6
+FILE_CONTENTS = """
7
+MATCH (f:File {path: $path})-[:CONTAINS]->(n)
8
+RETURN labels(n)[0] AS type, n.name AS name, n.line_start AS line,
9
+ n.docstring AS docstring, n.signature AS signature
10
+ORDER BY n.line_start
11
+"""
12
+
13
+DIRECT_IMPORTS = """
14
+MATCH (n {file_path: $file_path, name: $name})-[:IMPORTS]->(dep)
15
+RETURN labels(dep)[0] AS type, dep.name AS name, dep.file_path AS file_path
16
+"""
17
+
18
+# ── Code: call graph ──────────────────────────────────────────────────────────
19
+
20
+# file_path is optional — if empty, match by name only across all files
21
+CALLERS = """
22
+MATCH (caller)-[:CALLS*1..$depth]->(fn)
23
+WHERE fn.name = $name AND ($file_path = '' OR fn.file_path = $file_path)
24
+RETURN DISTINCT labels(caller)[0] AS type, caller.name AS name,
25
+ caller.file_path AS file_path, caller.line_start AS line
26
+"""
27
+
28
+CALLEES = """
29
+MATCH (fn)-[:CALLS*1..$depth]->(callee)
30
+WHERE fn.name = $name AND ($file_path = '' OR fn.file_path = $file_path)
31
+RETURN DISTINCT labels(callee)[0] AS type, callee.name AS name,
32
+ callee.file_path AS file_path, callee.line_start AS line
33
+"""
34
+
35
+# ── Code: class hierarchy ─────────────────────────────────────────────────────
36
+
37
+CLASS_HIERARCHY = """
38
+MATCH (c:Class {name: $name})-[:INHERITS*]->(parent)
39
+RETURN parent.name AS name, parent.file_path AS file_path
40
+"""
41
+
42
+SUBCLASSES = """
43
+MATCH (child:Class)-[:INHERITS*]->(c:Class {name: $name})
44
+RETURN child.name AS name, child.file_path AS file_path
45
+"""
46
+
47
+# ── Code: decorators ─────────────────────────────────────────────────────────
48
+
49
+# All functions/methods carrying a given decorator
50
+DECORATED_
--- a/navegador/graph/queries.py
+++ b/navegador/graph/queries.py
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/navegador/graph/queries.py
+++ b/navegador/graph/queries.py
@@ -0,0 +1,50 @@
1 """
2 Common context loading.
3 """
4
5 # Find al
6 FILE_CONTENTS = """
7 MATCH (f:File {path: $path})-[:CONTAINS]->(n)
8 RETURN labels(n)[0] AS type, n.name AS name, n.line_start AS line,
9 n.docstring AS docstring, n.signature AS signature
10 ORDER BY n.line_start
11 """
12
13 DIRECT_IMPORTS = """
14 MATCH (n {file_path: $file_path, name: $name})-[:IMPORTS]->(dep)
15 RETURN labels(dep)[0] AS type, dep.name AS name, dep.file_path AS file_path
16 """
17
18 # ── Code: call graph ──────────────────────────────────────────────────────────
19
20 # file_path is optional — if empty, match by name only across all files
21 CALLERS = """
22 MATCH (caller)-[:CALLS*1..$depth]->(fn)
23 WHERE fn.name = $name AND ($file_path = '' OR fn.file_path = $file_path)
24 RETURN DISTINCT labels(caller)[0] AS type, caller.name AS name,
25 caller.file_path AS file_path, caller.line_start AS line
26 """
27
28 CALLEES = """
29 MATCH (fn)-[:CALLS*1..$depth]->(callee)
30 WHERE fn.name = $name AND ($file_path = '' OR fn.file_path = $file_path)
31 RETURN DISTINCT labels(callee)[0] AS type, callee.name AS name,
32 callee.file_path AS file_path, callee.line_start AS line
33 """
34
35 # ── Code: class hierarchy ─────────────────────────────────────────────────────
36
37 CLASS_HIERARCHY = """
38 MATCH (c:Class {name: $name})-[:INHERITS*]->(parent)
39 RETURN parent.name AS name, parent.file_path AS file_path
40 """
41
42 SUBCLASSES = """
43 MATCH (child:Class)-[:INHERITS*]->(c:Class {name: $name})
44 RETURN child.name AS name, child.file_path AS file_path
45 """
46
47 # ── Code: decorators ─────────────────────────────────────────────────────────
48
49 # All functions/methods carrying a given decorator
50 DECORATED_
--- a/navegador/graph/schema.py
+++ b/navegador/graph/schema.py
@@ -0,0 +1,48 @@
1
+"""
2
+Graph schema — node labels and edge types for the navegador property graph.
3
+
4
+Node properties vary by label but all share: name, file_path, line_start, line_end.
5
+"""
6
+
7
+from enum import StrEnum
8
+
9
+
10
+class NodeLabel(StrEnum):
11
+ �───────
12
+ Repository = "Repository"
13
+ File = "File"
14
+ Module = "Module"
15
+ Class = "Class"
16
+ Function = "Function"
17
+ Method = "Method"
18
+ Variable = "Variable"
19
+ Import = "Import"
20
+ or, owner, or stakeholder
21
+
22
+
23
+classtructuralo one grap -CONTAIN�─────�, classe ses, calls, imports)
24
+ KNOWLEDGE la connected by IMPLEMENTS, DO# dependencies connected by IMPLEMENTS, DOC
25
+ DecTATES
26
+edges, so agents can traverseto the business ruleModule/Package level dependenge down to the exact co code that implements it.
27
+"""
28
+
29
+fr"""
30
+Graph schema — node labels and edge types for the navegador property ��─────────────────────────────────────────────
31
+ Repository = "Repository"
32
+ File = "File"
33
+ Module = "Module"
34
+ Class = "Class"
35
+ Function = "Function"
36
+ MMethod = "Method"
37
+ Variable = "Variable"
38
+ Import = "Importt"
39
+ Decorator = "Decorator"
40
+
41
+ # ── Knowledge layer ──────� ge layer ───────────────────── ��───────────────────────� ───────────
42
+ Domain = "Domain" # logicidea
43
+ Rule = "R─────�Method��─� ───────────
44
+ Domain = "Domain" # logi, "signature", "class_nameels and edge types"""
45
+Grap��─� ───────────
46
+ Domain = "Domain" # logical grouping (auth, billing,pt = "Concept" # a named business entity or idea
47
+ Rule = "Rule" # a connstraint, invariant, or business rule
48
+ Decision = "Decision" # an architectural or product decision + rationale
--- a/navegador/graph/schema.py
+++ b/navegador/graph/schema.py
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/navegador/graph/schema.py
+++ b/navegador/graph/schema.py
@@ -0,0 +1,48 @@
1 """
2 Graph schema — node labels and edge types for the navegador property graph.
3
4 Node properties vary by label but all share: name, file_path, line_start, line_end.
5 """
6
7 from enum import StrEnum
8
9
10 class NodeLabel(StrEnum):
11 �───────
12 Repository = "Repository"
13 File = "File"
14 Module = "Module"
15 Class = "Class"
16 Function = "Function"
17 Method = "Method"
18 Variable = "Variable"
19 Import = "Import"
20 or, owner, or stakeholder
21
22
23 classtructuralo one grap -CONTAIN�─────�, classe ses, calls, imports)
24 KNOWLEDGE la connected by IMPLEMENTS, DO# dependencies connected by IMPLEMENTS, DOC
25 DecTATES
26 edges, so agents can traverseto the business ruleModule/Package level dependenge down to the exact co code that implements it.
27 """
28
29 fr"""
30 Graph schema — node labels and edge types for the navegador property ��─────────────────────────────────────────────
31 Repository = "Repository"
32 File = "File"
33 Module = "Module"
34 Class = "Class"
35 Function = "Function"
36 MMethod = "Method"
37 Variable = "Variable"
38 Import = "Importt"
39 Decorator = "Decorator"
40
41 # ── Knowledge layer ──────� ge layer ───────────────────── ��───────────────────────� ───────────
42 Domain = "Domain" # logicidea
43 Rule = "R─────�Method��─� ───────────
44 Domain = "Domain" # logi, "signature", "class_nameels and edge types"""
45 Grap��─� ───────────
46 Domain = "Domain" # logical grouping (auth, billing,pt = "Concept" # a named business entity or idea
47 Rule = "Rule" # a connstraint, invariant, or business rule
48 Decision = "Decision" # an architectural or product decision + rationale
--- a/navegador/graph/store.py
+++ b/navegador/graph/store.py
@@ -0,0 +1,75 @@
1
+"""
2
+GraphStore — thin wrapper over FalkorDB (SQLite or Redis backend).
3
+
4
+Usage:
5
+ # SQLite (local, zero-infra)
6
+ store = GraphStore.sqlite(".navegador/graph.db")
7
+
8
+ # Redis-backed FalkorDB (production)
9
+ store = GraphStore.redis("redis://localhost:6379")
10
+"""
11
+
12
+import logging
13
+from pathlib import Path
14
+from typing import Any
15
+
16
+logger = logging.getLogger(__name__)
17
+
18
+
19
+class GraphStore:
20
+ """
21
+ Wraps a FalkorDB graph, providing helpers for navegador node/edge operations.
22
+
23
+ The underlying graph is named "navegador" within the database.
24
+ """
25
+
26
+ GRAPH_NAME = "navegador"
27
+
28
+ def __init__(self, client: Any) -> None:
29
+ self._client = client
30
+ self._graph = client.select_graph(self.GRAPH_NAME)
31
+
32
+ # ── Constructors ──────────────────────────────────────────────────────────
33
+
34
+ @classmethod
35
+ def sqlite(cls, db_path: str, file_path)."""rt] # provided by falkordblite
36
+ except ImportError as e:
37
+ raise ImportError(
38
+ (
39
+ "Install graph dependenciel)
40
+ returSET {prop_str}"
41
+ ) str, params: dict[str, Any] ) from e
42
+
43
+ db_path = Path(db_path)
44
+ db_path.parent.mkdir(parents=True, exist_ok=True)
45
+ client = FalkorDB(str(db_path))
46
+ logger.info("GraphStore opened (SQLite/falkordblite): %s", db_path)
47
+ return cls(client)
48
+
49
+ @classmethod
50
+ def redis(cls, url: str = "redis://localhost:6379") -> "GraphStore":
51
+ AND ".join(f"a.{k} =raph (production use).
52
+
53
+ AND ".join(f"b.{k} =is
54
+ """
55
+ try:
56
+ import falkordb # type: ignore[import]
57
+ except ImportError as e:
58
+ raise ImportError("Install falkordb: pip install FalkorDB redis") from e
59
+
60
+ client = falkordb.FalkorDB.from_url(url)
61
+ logger.info("GraphStore opened (Redis): %s", url)
62
+ return cls(client)
63
+
64
+ # ── Core operations ───────────────────────────────────────────────────────
65
+
66
+ def query(self, cypher: str, params: dict[str, Any] | None = None) -> Any:
67
+ """Execute a raw Cypher query and return the result."""
68
+ return self._graph.query(cypher, params or {})
69
+
70
+ def create_node(self, label: str, props: dict[str, Any]) -> None:
71
+ """Upsert a node by (label, name[, file_path])."""
72
+ # Ensure merge key fields exist
73
+ props.setdefault("name", "")
74
+ props.setdefault("file_path", "")
75
+ # Filter out None values — FalkorDB rejects them as par
--- a/navegador/graph/store.py
+++ b/navegador/graph/store.py
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/navegador/graph/store.py
+++ b/navegador/graph/store.py
@@ -0,0 +1,75 @@
1 """
2 GraphStore — thin wrapper over FalkorDB (SQLite or Redis backend).
3
4 Usage:
5 # SQLite (local, zero-infra)
6 store = GraphStore.sqlite(".navegador/graph.db")
7
8 # Redis-backed FalkorDB (production)
9 store = GraphStore.redis("redis://localhost:6379")
10 """
11
12 import logging
13 from pathlib import Path
14 from typing import Any
15
16 logger = logging.getLogger(__name__)
17
18
19 class GraphStore:
20 """
21 Wraps a FalkorDB graph, providing helpers for navegador node/edge operations.
22
23 The underlying graph is named "navegador" within the database.
24 """
25
26 GRAPH_NAME = "navegador"
27
28 def __init__(self, client: Any) -> None:
29 self._client = client
30 self._graph = client.select_graph(self.GRAPH_NAME)
31
32 # ── Constructors ──────────────────────────────────────────────────────────
33
34 @classmethod
35 def sqlite(cls, db_path: str, file_path)."""rt] # provided by falkordblite
36 except ImportError as e:
37 raise ImportError(
38 (
39 "Install graph dependenciel)
40 returSET {prop_str}"
41 ) str, params: dict[str, Any] ) from e
42
43 db_path = Path(db_path)
44 db_path.parent.mkdir(parents=True, exist_ok=True)
45 client = FalkorDB(str(db_path))
46 logger.info("GraphStore opened (SQLite/falkordblite): %s", db_path)
47 return cls(client)
48
49 @classmethod
50 def redis(cls, url: str = "redis://localhost:6379") -> "GraphStore":
51 AND ".join(f"a.{k} =raph (production use).
52
53 AND ".join(f"b.{k} =is
54 """
55 try:
56 import falkordb # type: ignore[import]
57 except ImportError as e:
58 raise ImportError("Install falkordb: pip install FalkorDB redis") from e
59
60 client = falkordb.FalkorDB.from_url(url)
61 logger.info("GraphStore opened (Redis): %s", url)
62 return cls(client)
63
64 # ── Core operations ───────────────────────────────────────────────────────
65
66 def query(self, cypher: str, params: dict[str, Any] | None = None) -> Any:
67 """Execute a raw Cypher query and return the result."""
68 return self._graph.query(cypher, params or {})
69
70 def create_node(self, label: str, props: dict[str, Any]) -> None:
71 """Upsert a node by (label, name[, file_path])."""
72 # Ensure merge key fields exist
73 props.setdefault("name", "")
74 props.setdefault("file_path", "")
75 # Filter out None values — FalkorDB rejects them as par
--- a/navegador/ingestion/__init__.py
+++ b/navegador/ingestion/__init__.py
@@ -0,0 +1,2 @@
1
+e import KnowledgeIngester
2
+from .
--- a/navegador/ingestion/__init__.py
+++ b/navegador/ingestion/__init__.py
@@ -0,0 +1,2 @@
 
 
--- a/navegador/ingestion/__init__.py
+++ b/navegador/ingestion/__init__.py
@@ -0,0 +1,2 @@
1 e import KnowledgeIngester
2 from .
--- a/navegador/ingestion/parser.py
+++ b/navegador/ingestion/parser.py
@@ -0,0 +1,74 @@
1
+"""
2
+RepoIngester — walks a repository, parses source files with tree-sitter,
3
+and writes nodes + thlib import.schema import NodeLabel
4
+from navegador.graph.store import GraphStore
5
+
6
+logger = logging.getLoggeEdgeType,er(__name__)
7
+
8
+# File extensions → language key
9
+LANGUAGE_MAP: dict[str, str] = {
10
+ ".py": "python",
11
+ ".ts": "types"python",
12
+ ".ts":esc cript",
13
+ ".js": "ja "typescript",
14
+ ".js":avascript",
15
+ ".go": "go "javascriptes a GraphStore.
16
+
17
+ Usage:
18
+ store = GraphStore.sqlite(".navegador/graph.db")
19
+ ingespath/to/repsitter,
20
+and writes nodes + edges into the GraphStore.
21
+
22
+Supported languages (all via tree-sitter):
23
+ Python .py
24
+ b import Path
25
+
26
+from naom navegador.graph.store
27
+
28
+# File extensiotlin",
29
+ ".kts": "kotlin",
30
+ ".cs": "csharp",
31
+ ".php": "php",
32
+ ".rb": "ruby",
33
+ ".swself,port logging
34
+import tim clear: bool = Fals"""
35
+h tree-sitter,
36
+and writes nodes + edges intore.
37
+
38
+Supported language.
39
+"""
40
+RepoIngester — walks a repository, parses source ry, parses source files with tree-sitter,
41
+and writes nodes + edges into the GraphStore.
42
+
43
+Supported langu"""
44
+RepoIngester — walks a repository, parses source files with tree-sitter,
45
+and writes nodes + edges into the GraphStore.
46
+
47
+Supported languages (all via tree-sitter):
48
+ Python .py
49
+ TypeScript .ts .tsx
50
+ JavaScript .js .jsx
51
+ Go .go
52
+ Rust .r"files": 0, "functions": t",
53
+ ".tsx": "typescript",
54
+ ".js": "javascript",
55
+ ".jsx": "javascriptNodeLabel.Repository, {
56
+ File extensiotlin",
57
+ ".kts":}repository, parses source files with tree-sitter,
58
+and writes nodes + edges into the GraphStore.
59
+
60
+Supported languages (all via tree-sitter):
61
+ Python .py
62
+ TypeScript .ts .tsx
63
+ JavaScript .js .jsx
64
+ Go .go
65
+ Rust .rs
66
+ Javlogging
67
+from pathlib import.schema import NodeLabel
68
+from navegador.graph.store import GraphStore
69
+
70
+logger = logging.getLogger(__name__)
71
+
72
+# File extensions → language key
73
+LANGUAGE_MAP: dict[str, str] = {
74
+ ".
--- a/navegador/ingestion/parser.py
+++ b/navegador/ingestion/parser.py
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/navegador/ingestion/parser.py
+++ b/navegador/ingestion/parser.py
@@ -0,0 +1,74 @@
1 """
2 RepoIngester — walks a repository, parses source files with tree-sitter,
3 and writes nodes + thlib import.schema import NodeLabel
4 from navegador.graph.store import GraphStore
5
6 logger = logging.getLoggeEdgeType,er(__name__)
7
8 # File extensions → language key
9 LANGUAGE_MAP: dict[str, str] = {
10 ".py": "python",
11 ".ts": "types"python",
12 ".ts":esc cript",
13 ".js": "ja "typescript",
14 ".js":avascript",
15 ".go": "go "javascriptes a GraphStore.
16
17 Usage:
18 store = GraphStore.sqlite(".navegador/graph.db")
19 ingespath/to/repsitter,
20 and writes nodes + edges into the GraphStore.
21
22 Supported languages (all via tree-sitter):
23 Python .py
24 b import Path
25
26 from naom navegador.graph.store
27
28 # File extensiotlin",
29 ".kts": "kotlin",
30 ".cs": "csharp",
31 ".php": "php",
32 ".rb": "ruby",
33 ".swself,port logging
34 import tim clear: bool = Fals"""
35 h tree-sitter,
36 and writes nodes + edges intore.
37
38 Supported language.
39 """
40 RepoIngester — walks a repository, parses source ry, parses source files with tree-sitter,
41 and writes nodes + edges into the GraphStore.
42
43 Supported langu"""
44 RepoIngester — walks a repository, parses source files with tree-sitter,
45 and writes nodes + edges into the GraphStore.
46
47 Supported languages (all via tree-sitter):
48 Python .py
49 TypeScript .ts .tsx
50 JavaScript .js .jsx
51 Go .go
52 Rust .r"files": 0, "functions": t",
53 ".tsx": "typescript",
54 ".js": "javascript",
55 ".jsx": "javascriptNodeLabel.Repository, {
56 File extensiotlin",
57 ".kts":}repository, parses source files with tree-sitter,
58 and writes nodes + edges into the GraphStore.
59
60 Supported languages (all via tree-sitter):
61 Python .py
62 TypeScript .ts .tsx
63 JavaScript .js .jsx
64 Go .go
65 Rust .rs
66 Javlogging
67 from pathlib import.schema import NodeLabel
68 from navegador.graph.store import GraphStore
69
70 logger = logging.getLogger(__name__)
71
72 # File extensions → language key
73 LANGUAGE_MAP: dict[str, str] = {
74 ".
--- a/navegador/ingestion/python.py
+++ b/navegador/ingestion/python.py
@@ -0,0 +1,158 @@
1
+"""
2
+Python AST parser — extracts classes, functions, imports, calls, and
3
+their relationships from .py files using tree-sitter.
4
+"""
5
+
6
+import logging
7
+from pathlib import Path
8
+
9
+from navegador.graph.schema import EdgeType, NodeLabel
10
+from navegador.graph.store import GraphStore
11
+from navegador.ingestion.parser import LanguageParser
12
+
13
+logger = logging.getLogger(__name__)
14
+
15
+
16
+def _get_python_language():
17
+ try:
18
+ import tree_sitter_python as tspython # type: ignore[import]
19
+ from tree_sitter import Language
20
+ return Language(tspython.language())
21
+ except ImportError as e:
22
+ raise ImportError(
23
+ ython: p
24
+ raise ImportError("Insta
25
+ install tree-sitter-python") from e
26
+
27
+
28
+def _get_parser():
29
+ from tree_sitter import Pars parser = Parser(_get_python_language())
30
+ return parser
31
+
32
+
33
+def _node_text(node, source: bytes) -> str:
34
+ return source[node.start_byte:node.end_byte].decode("utf-8", errors="replace")
35
+
36
+
37
+def _get_docstring(node, source: bytes) -> str | None:
38
+ """Extract the first string literal from a function/class body as docstring."""
39
+ body = next((c for c in node.children if c.type == "block"), None)
40
+ if not body:
41
+ return None
42
+ first_stmt = next((c for c in body.children if c.type == "expression_statement"), None)
43
+ if not first_stmt:
44
+ return None
45
+ string_node = next((c for c in first_stmt.children if c.type in ("string", "string_content")), None raw = _node_text(string_node, source)
46
+ return raw.strip('"""').strip("'''").strip('"').strip("'").strip()
47
+ return None
48
+
49
+
50
+class PythonParser(LanguageParser):
51
+ def __init__(self) -> None:
52
+ self._parser = _get_parser()
53
+
54
+ def parse_file(self, path: Path, repo_root: Path, store: GraphStore) -> dict[str, int]:
55
+ source = path.read_bytes()
56
+ tree = self._parser.parse(source)
57
+ rel_path = str(path.relative_to(repo_root))
58
+
59
+ stats = {"functions": 0, "classes": 0, "edges": 0}
60
+
61
+ # File nodeNodeLabel.File, {
62
+racts classes, funct"""
63
+Python AST parser �path.name,
64
+ "pa"language": "python",
65
+ "python",
66
+ })
67
+
68
+ },
69
+ )
70
+
71
+ self._walk(tree.root_node, source, rel_path, store, stats,andle_import_from(
72
+ self, n
73
+ store: Graph class_name: str | None) -> None:
74
+ ocstring(node, source: b source: bytes) -> str:
75
+ return source[node.start_byte : node.end_byte].decode("utf-8", errors="replace")
76
+
77
+
78
+def _get_docstring(node, source: bytes) -> str | None:
79
+ """Extract the first string literal from a function/class body as docstring."""
80
+ body = next((c for c in node.children if c.type == "block"), None)
81
+ if not body:
82
+ return None
83
+ first_stmt = next((c for c in body.children if c.type == "expression_statement"), None)
84
+ if not first_stmt:
85
+ return None
86
+ string_node = next(
87
+ (c for c in first_stmt.children if c.type in ("string", "string_content")), None
88
+ )
89
+ if string_node:
90
+ raw = _node_text(string_node, source)
91
+ return raw.strip('""andle_import_fr
92
+ store: GraphStore, stats: dict) -> None:
93
+ for child in node.children:
94
+ elf, path: Path, repo_root: Path, store: GraphStore) -> dict[str, int]:
95
+ source = path.reaNodeLabel.Import,parser.parse(source)
96
+ ive_to(repo_root))
97
+
98
+ stats "do"line_start": node.start_point[0File,
99
+ {
100
+ } = _node_text(chilstore.createNodeLabel.File, {"path": file_path},
101
+ line_start": node.start_point[ = _node_text(chilype.IMPORTS,
102
+ p("'''").strip('"'_from(andle_import_fr
103
+ ode, source)
104
+ docst stats: dict) -> None:
105
+ NodeLabel.Import,parser.parse(source)
106
+ ive_to(repo_root))
107
+
108
+ stats "do"line_start": node.start_point[0"module": module,
109
+ } = _node_text(chilstore.createNodeLabel.File, {"path": file_path},
110
+ line_start": node.start_point[ = _node_text(chilype.IMPORTS,
111
+ def _handle_class(andle_import_fr
112
+ store: GraphStore, stats: dict) -> None:
113
+ odeLabel.Class,
114
+ {
115
+ "name": name,
116
+ "file_path": file_path,
117
+ "line_start": node.start_point[0] + 1,
118
+ "line_end": node.end_point[0] + 1,
119
+ "dostore.create_node(NodeLabel.Class, {
120
+racts class"""
121
+Python AST parser — extracts classes, functions, imports, calls, and
122
+their relationships from .py files using tree-sitter.
123
+"""
124
+
125
+import logg}""
126
+Python AST — extracts cline_start": node.starpath": file_path},
127
+ )
128
+ stats["classes"] += 1
129
+ stats["edges"] += 1
130
+
131
+ # Inheritance
132
+ for child in node.children:
133
+ if child.type == "argument_list":
134
+ for arg in child.children:
135
+ if arg.type == "identifier":
136
+ parent_name = _node_text(arg, source)
137
+ store.create_edge(
138
+ line_start": node.start_point[0] + 1,
139
+ "module": module,
140
+ },
141
+ )
142
+ store.create_edge(
143
+ NodeLabel.File,
144
+ {"path": file_path},
145
+ EdgeType.IMPORTS,
146
+ NodeLabel.Import,
147
+ {"name": name, "file_path": file_path},
148
+ )
149
+ stats["edges"] += 1
150
+
151
+ def _handle_class(
152
+ self, node, source: bytes, file_path: str, store: GraphStore, stats: dict
153
+ ) -> None:
154
+ name_node = next((c for c in node.children if c.typandle_import_fr
155
+ ode, source)
156
+ docst dict, class_name: str | NoneJ@QG,Bu@1o7,1:
157
+B@16i,H: if class_name
158
+ 8@a0,1: 1R@1~E,F: container_key,J@21G,4f@21k,9:self, fn_a@p0,M@27l,9@1PA,K@280,V@1JF,J@QG,5n@29S,1~@2Fb,4q@2Hy,2n8gf5;
--- a/navegador/ingestion/python.py
+++ b/navegador/ingestion/python.py
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/navegador/ingestion/python.py
+++ b/navegador/ingestion/python.py
@@ -0,0 +1,158 @@
1 """
2 Python AST parser — extracts classes, functions, imports, calls, and
3 their relationships from .py files using tree-sitter.
4 """
5
6 import logging
7 from pathlib import Path
8
9 from navegador.graph.schema import EdgeType, NodeLabel
10 from navegador.graph.store import GraphStore
11 from navegador.ingestion.parser import LanguageParser
12
13 logger = logging.getLogger(__name__)
14
15
16 def _get_python_language():
17 try:
18 import tree_sitter_python as tspython # type: ignore[import]
19 from tree_sitter import Language
20 return Language(tspython.language())
21 except ImportError as e:
22 raise ImportError(
23 ython: p
24 raise ImportError("Insta
25 install tree-sitter-python") from e
26
27
28 def _get_parser():
29 from tree_sitter import Pars parser = Parser(_get_python_language())
30 return parser
31
32
33 def _node_text(node, source: bytes) -> str:
34 return source[node.start_byte:node.end_byte].decode("utf-8", errors="replace")
35
36
37 def _get_docstring(node, source: bytes) -> str | None:
38 """Extract the first string literal from a function/class body as docstring."""
39 body = next((c for c in node.children if c.type == "block"), None)
40 if not body:
41 return None
42 first_stmt = next((c for c in body.children if c.type == "expression_statement"), None)
43 if not first_stmt:
44 return None
45 string_node = next((c for c in first_stmt.children if c.type in ("string", "string_content")), None raw = _node_text(string_node, source)
46 return raw.strip('"""').strip("'''").strip('"').strip("'").strip()
47 return None
48
49
50 class PythonParser(LanguageParser):
51 def __init__(self) -> None:
52 self._parser = _get_parser()
53
54 def parse_file(self, path: Path, repo_root: Path, store: GraphStore) -> dict[str, int]:
55 source = path.read_bytes()
56 tree = self._parser.parse(source)
57 rel_path = str(path.relative_to(repo_root))
58
59 stats = {"functions": 0, "classes": 0, "edges": 0}
60
61 # File nodeNodeLabel.File, {
62 racts classes, funct"""
63 Python AST parser �path.name,
64 "pa"language": "python",
65 "python",
66 })
67
68 },
69 )
70
71 self._walk(tree.root_node, source, rel_path, store, stats,andle_import_from(
72 self, n
73 store: Graph class_name: str | None) -> None:
74 ocstring(node, source: b source: bytes) -> str:
75 return source[node.start_byte : node.end_byte].decode("utf-8", errors="replace")
76
77
78 def _get_docstring(node, source: bytes) -> str | None:
79 """Extract the first string literal from a function/class body as docstring."""
80 body = next((c for c in node.children if c.type == "block"), None)
81 if not body:
82 return None
83 first_stmt = next((c for c in body.children if c.type == "expression_statement"), None)
84 if not first_stmt:
85 return None
86 string_node = next(
87 (c for c in first_stmt.children if c.type in ("string", "string_content")), None
88 )
89 if string_node:
90 raw = _node_text(string_node, source)
91 return raw.strip('""andle_import_fr
92 store: GraphStore, stats: dict) -> None:
93 for child in node.children:
94 elf, path: Path, repo_root: Path, store: GraphStore) -> dict[str, int]:
95 source = path.reaNodeLabel.Import,parser.parse(source)
96 ive_to(repo_root))
97
98 stats "do"line_start": node.start_point[0File,
99 {
100 } = _node_text(chilstore.createNodeLabel.File, {"path": file_path},
101 line_start": node.start_point[ = _node_text(chilype.IMPORTS,
102 p("'''").strip('"'_from(andle_import_fr
103 ode, source)
104 docst stats: dict) -> None:
105 NodeLabel.Import,parser.parse(source)
106 ive_to(repo_root))
107
108 stats "do"line_start": node.start_point[0"module": module,
109 } = _node_text(chilstore.createNodeLabel.File, {"path": file_path},
110 line_start": node.start_point[ = _node_text(chilype.IMPORTS,
111 def _handle_class(andle_import_fr
112 store: GraphStore, stats: dict) -> None:
113 odeLabel.Class,
114 {
115 "name": name,
116 "file_path": file_path,
117 "line_start": node.start_point[0] + 1,
118 "line_end": node.end_point[0] + 1,
119 "dostore.create_node(NodeLabel.Class, {
120 racts class"""
121 Python AST parser — extracts classes, functions, imports, calls, and
122 their relationships from .py files using tree-sitter.
123 """
124
125 import logg}""
126 Python AST — extracts cline_start": node.starpath": file_path},
127 )
128 stats["classes"] += 1
129 stats["edges"] += 1
130
131 # Inheritance
132 for child in node.children:
133 if child.type == "argument_list":
134 for arg in child.children:
135 if arg.type == "identifier":
136 parent_name = _node_text(arg, source)
137 store.create_edge(
138 line_start": node.start_point[0] + 1,
139 "module": module,
140 },
141 )
142 store.create_edge(
143 NodeLabel.File,
144 {"path": file_path},
145 EdgeType.IMPORTS,
146 NodeLabel.Import,
147 {"name": name, "file_path": file_path},
148 )
149 stats["edges"] += 1
150
151 def _handle_class(
152 self, node, source: bytes, file_path: str, store: GraphStore, stats: dict
153 ) -> None:
154 name_node = next((c for c in node.children if c.typandle_import_fr
155 ode, source)
156 docst dict, class_name: str | NoneJ@QG,Bu@1o7,1:
157 B@16i,H: if class_name
158 8@a0,1: 1R@1~E,F: container_key,J@21G,4f@21k,9:self, fn_a@p0,M@27l,9@1PA,K@280,V@1JF,J@QG,5n@29S,1~@2Fb,4q@2Hy,2n8gf5;
--- a/navegador/ingestion/typescript.py
+++ b/navegador/ingestion/typescript.py
@@ -0,0 +1,79 @@
1
+"""
2
+TypeScriptplaceholder for tree-sitter-typescript.
3
+Full implementation follows the same pattern as python.py.
4
+"""
5
+
6
+import logging
7
+from pathlib import Path
8
+
9
+from navegador.graph.schema import EdgeType, NodeLabel
10
+from navegador.graph.store import GraphStore
11
+from navegador.ingestion.parser import LanguageParser
12
+
13
+logger = logging.getLogger(__name__)
14
+
15
+
16
+def _get_ts_language(language: str):
17
+ try:
18
+ if language == "typescript":
19
+ import tree_sitter_typescript as tsts # type: ignore[import]
20
+ from tree_sitter import Language
21
+ return Language(tsts.language_typescript())
22
+ else:
23
+ import tree_sitter_javascript as tsjs # type: ignore[import]
24
+ from t return Language(ts return Language(tsjs.language())
25
+ except ImportError as e:
26
+ raise ImportError(
27
+ f"Install tree-sitter-{language}: pip install tree-sitter-{language}"
28
+ ) from e
29
+
30
+
31
+raise ImportError _node_text(node, source: bytes) -> str:
32
+ return source[node.start_byte : node.end_def _handle_cla
33
+ "docstrin: GraphStore, stats: dictce, file_pat: str | None) -> None:
34
+ f prev.type == "coblings = list(parent.children)
35
+ idx = next((i for i, c in enumerate(siblings) if c.id == node.id), -1)
36
+ if idx <= 0:
37
+ return ""
38
+ prev = siblings[idx - 1]
39
+ if prev.type == "comment":
40
+ raw = _node_text(prev, source).strip()
41
+ if raw.startswith("/**"):
42
+ return raw.strip("/**").strip("*/").strip()
43
+ return ""
44
+
45
+
46
+class TypeScriptParser(LanguageParser):
47
+ """Parses TypeScript/JavaScript source files into the navegador graph."""
48
+
49
+ def __init__(self, language: str = "typescript") -> None:
50
+ from tree_sitter import Parser ]
51
+
52
+ self._parser =h},
53
+
54
+ le(self, path: Path, repo_root: Path, store: GraphStore) -> dict[str, int]:
55
+ source = path.read_bytes()
56
+ tree = self._parser.parse(source)
57
+ rel_path = str(path.relative_to(repo_root))
58
+NodeLa
59
+ def _han return Language(tsjs., "arrow_function",""
60
+TypeScript/JavaScScr def _han stats["edges"] += 1
61
+ ath.read_bytes()
62
+ tre idx = next((i for i, c in enumerate(siblings) if c.id == node.id), -1)
63
+ if idx <= 0:
64
+
65
+ store.create_edge(
66
+, {"path": file_path},
67
+ ile_path},
68
+ IMPORTSode or not value_node:NodeLabel.Import,
69
+ name = _node_text(name_node,# Extract "from '...'" module path
70
+ store.create_edge(
71
+""
72
+TypeScript/Javaparser — extracts classes, intersource[child.start_bchild.end_byte].decode(8m@2j2,2G@18i,~@1f0,z@1wi,v:source[name_node.start_byte:name_node.end_byte].decode()
73
+
74
+L@2yl,3H@1Ei,4:"",
75
+J@2n0,V@2Dz,3M@1Iw,48@1YD,Z@1b~,1h@1rs,1X@1tx,21@1vf,v:source[name_node.start_byte:name_node.end_byte].decode()
76
+
77
+8@2_v,~@24w,L@2yl,37@26G,2:""3n@29W,1:
78
+8@1Sk,B: else {"H@1UG,J@1f~,z@2Dz,Q: EdgeType.CONTAINS, label,M@2B~,T@2CR,R@1qW,i@2Gl,3trZxW;)
79
+
--- a/navegador/ingestion/typescript.py
+++ b/navegador/ingestion/typescript.py
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/navegador/ingestion/typescript.py
+++ b/navegador/ingestion/typescript.py
@@ -0,0 +1,79 @@
1 """
2 TypeScriptplaceholder for tree-sitter-typescript.
3 Full implementation follows the same pattern as python.py.
4 """
5
6 import logging
7 from pathlib import Path
8
9 from navegador.graph.schema import EdgeType, NodeLabel
10 from navegador.graph.store import GraphStore
11 from navegador.ingestion.parser import LanguageParser
12
13 logger = logging.getLogger(__name__)
14
15
16 def _get_ts_language(language: str):
17 try:
18 if language == "typescript":
19 import tree_sitter_typescript as tsts # type: ignore[import]
20 from tree_sitter import Language
21 return Language(tsts.language_typescript())
22 else:
23 import tree_sitter_javascript as tsjs # type: ignore[import]
24 from t return Language(ts return Language(tsjs.language())
25 except ImportError as e:
26 raise ImportError(
27 f"Install tree-sitter-{language}: pip install tree-sitter-{language}"
28 ) from e
29
30
31 raise ImportError _node_text(node, source: bytes) -> str:
32 return source[node.start_byte : node.end_def _handle_cla
33 "docstrin: GraphStore, stats: dictce, file_pat: str | None) -> None:
34 f prev.type == "coblings = list(parent.children)
35 idx = next((i for i, c in enumerate(siblings) if c.id == node.id), -1)
36 if idx <= 0:
37 return ""
38 prev = siblings[idx - 1]
39 if prev.type == "comment":
40 raw = _node_text(prev, source).strip()
41 if raw.startswith("/**"):
42 return raw.strip("/**").strip("*/").strip()
43 return ""
44
45
46 class TypeScriptParser(LanguageParser):
47 """Parses TypeScript/JavaScript source files into the navegador graph."""
48
49 def __init__(self, language: str = "typescript") -> None:
50 from tree_sitter import Parser ]
51
52 self._parser =h},
53
54 le(self, path: Path, repo_root: Path, store: GraphStore) -> dict[str, int]:
55 source = path.read_bytes()
56 tree = self._parser.parse(source)
57 rel_path = str(path.relative_to(repo_root))
58 NodeLa
59 def _han return Language(tsjs., "arrow_function",""
60 TypeScript/JavaScScr def _han stats["edges"] += 1
61 ath.read_bytes()
62 tre idx = next((i for i, c in enumerate(siblings) if c.id == node.id), -1)
63 if idx <= 0:
64
65 store.create_edge(
66 , {"path": file_path},
67 ile_path},
68 IMPORTSode or not value_node:NodeLabel.Import,
69 name = _node_text(name_node,# Extract "from '...'" module path
70 store.create_edge(
71 ""
72 TypeScript/Javaparser — extracts classes, intersource[child.start_bchild.end_byte].decode(8m@2j2,2G@18i,~@1f0,z@1wi,v:source[name_node.start_byte:name_node.end_byte].decode()
73
74 L@2yl,3H@1Ei,4:"",
75 J@2n0,V@2Dz,3M@1Iw,48@1YD,Z@1b~,1h@1rs,1X@1tx,21@1vf,v:source[name_node.start_byte:name_node.end_byte].decode()
76
77 8@2_v,~@24w,L@2yl,37@26G,2:""3n@29W,1:
78 8@1Sk,B: else {"H@1UG,J@1f~,z@2Dz,Q: EdgeType.CONTAINS, label,M@2B~,T@2CR,R@1qW,i@2Gl,3trZxW;)
79
--- a/navegador/mcp/__init__.py
+++ b/navegador/mcp/__init__.py
@@ -0,0 +1,3 @@
1
+from .server import create_mcp_server
2
+
3
+__all__ = ["create_mcp_server"]
--- a/navegador/mcp/__init__.py
+++ b/navegador/mcp/__init__.py
@@ -0,0 +1,3 @@
 
 
 
--- a/navegador/mcp/__init__.py
+++ b/navegador/mcp/__init__.py
@@ -0,0 +1,3 @@
1 from .server import create_mcp_server
2
3 __all__ = ["create_mcp_server"]
--- a/navegador/mcp/server.py
+++ b/navegador/mcp/server.py
@@ -0,0 +1,97 @@
1
+server.stdio import stdio_suments.get("format", "markdown")
2
+ text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
3
+ return [TextContent(type="text", text=text)]
4
+
5
+ elif name == "load_function_context":
6
+ bundle = load depth=arguments.get("depth", 2),
7
+ guments.get("formatormat", "markdown")
8
+ text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
9
+ return [TextContent(type="text", text=text)]
10
+
11
+ elif name == "load_class_context":
12
+ bundle = loader.load_class(arguments["name"], arguments["file_path"])
13
+ fmt = arguments.get("format", "markdown")
14
+ text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
15
+ return [TextContent(type="text", text=text)]
16
+
17
+ elif name == "search_symbols":
18
+ results = loader.search(arguments["query"],undle.to_markdown limit=arguments.get("lim
19
+ sitoryelse bundle.to_json()
20
+ clear": {xt tools to AI cod "fault": "",
21
+ "default": False"name"]return [TextContent(type=" depth=arguments.get("depth", 2),
22
+ guments.get("formatormat", "markdown")
23
+ tile — exposes graph qes graph context tools t and their relationships bundle = loader.load_class(arguments["name"], arguments["file_path"])
24
+ fmt = arguments.get("format", "markdownn")
25
+ text = bundle.to_markdown() if fmt == "markdown" ed": ["name"]return [TextCo"name"]return [TextCont "format"])
26
+ fmt = afault": "",
27
+ fault": "",
28
+ function — exposes graph query"], limit
29
+ext toollse bundle.to_json()
30
+ return [TextContent(type="text", text=text)]
31
+
32
+ elif name == "load_class_context":
33
+ bundle = loader.load_class(arguments["name"], arguments["file_path"])
34
+ fmt = arguments.get("format", "markdown")
35
+ text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
36
+ return [TextContent(type="text", text=text)]
37
+
38
+ down limit=argumentfault": "",
39
+ fault": "",
40
+ },
41
+ },
42
+ "required": ["name"],
43
+ },
44
+ ),
45
+ Tool(pCP server — exposes graph query"], limit
46
+ read-only mode.",
47
+ ("limit", 20))
48
+ if not results:
49
+ return [TextContent(tundle.to_markdownf"- **{r.type}** `{r.name}` — {r.description or ''} for r in results
50
+ "
51
+Navegador MCP server — exposes graph context tools to AI coding poses graph context tools CP servarguments["cypher"])
52
+ fault"down limit=argumentfault": "",
53
+ fault": "",
54
+ },
55
+ },
56
+ "required": ["name"],
57
+ },
58
+ ),
59
+ Tool(
60
+
61
+ TextContent inputSchema={
62
+ type=" is None:
63
+ _text",
64
+ ts disabled in read-on)]
65
+ from navegador.ingestion import RepoIngester
66
+
67
+ ingester = RepoIngester(loader.store)
68
+ stats = ingestarguments["file_path"])
69
+ fmt = arguments.get("format", "markdown")
70
+ text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
71
+ return [TextContent(type="text", text=text)]
72
+
73
+ elif name == "load_function_context":
74
+ bundle = load depth=arguments.get("depth", 2),
75
+ guments.get("formatormat", "markdown")
76
+ text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
77
+ return [TextContent(type="text", text=text)]
78
+
79
+ elif name == "load_class_context":
80
+ bundle = loader.load_class(arguments["name"], arguments["file_path"])
81
+ fmt = arguments.get("format", "markdown")
82
+ text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
83
+ return [TextContent(type="text", text=text)]
84
+
85
+ elif name == "search_symbols":
86
+ results = loader.search(arguments["query"],undle.to_markdown limit=arguments.get("lim
87
+ "
88
+Navegador MCP server — exposes graph context tools to AI coding poses graph context tools to AI coding agents.
89
+
90
+Run:
91
+ navegador mcp --db .navegador/grapCP server — exposes graph query"], limit
92
+ read-only mode.",
93
+ ("limit", 20))
94
+ if not results:
95
+ return [TextContent(tundle.to_markdownf"- **{r.type}** `{r.name}` — {r.description or ''} for r in results
96
+ "
97
+Navegador MCP server — exposes graph context tools to AI coding poses graph context tools CP servarguments["cypher"])
--- a/navegador/mcp/server.py
+++ b/navegador/mcp/server.py
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/navegador/mcp/server.py
+++ b/navegador/mcp/server.py
@@ -0,0 +1,97 @@
1 server.stdio import stdio_suments.get("format", "markdown")
2 text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
3 return [TextContent(type="text", text=text)]
4
5 elif name == "load_function_context":
6 bundle = load depth=arguments.get("depth", 2),
7 guments.get("formatormat", "markdown")
8 text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
9 return [TextContent(type="text", text=text)]
10
11 elif name == "load_class_context":
12 bundle = loader.load_class(arguments["name"], arguments["file_path"])
13 fmt = arguments.get("format", "markdown")
14 text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
15 return [TextContent(type="text", text=text)]
16
17 elif name == "search_symbols":
18 results = loader.search(arguments["query"],undle.to_markdown limit=arguments.get("lim
19 sitoryelse bundle.to_json()
20 clear": {xt tools to AI cod "fault": "",
21 "default": False"name"]return [TextContent(type=" depth=arguments.get("depth", 2),
22 guments.get("formatormat", "markdown")
23 tile — exposes graph qes graph context tools t and their relationships bundle = loader.load_class(arguments["name"], arguments["file_path"])
24 fmt = arguments.get("format", "markdownn")
25 text = bundle.to_markdown() if fmt == "markdown" ed": ["name"]return [TextCo"name"]return [TextCont "format"])
26 fmt = afault": "",
27 fault": "",
28 function — exposes graph query"], limit
29 ext toollse bundle.to_json()
30 return [TextContent(type="text", text=text)]
31
32 elif name == "load_class_context":
33 bundle = loader.load_class(arguments["name"], arguments["file_path"])
34 fmt = arguments.get("format", "markdown")
35 text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
36 return [TextContent(type="text", text=text)]
37
38 down limit=argumentfault": "",
39 fault": "",
40 },
41 },
42 "required": ["name"],
43 },
44 ),
45 Tool(pCP server — exposes graph query"], limit
46 read-only mode.",
47 ("limit", 20))
48 if not results:
49 return [TextContent(tundle.to_markdownf"- **{r.type}** `{r.name}` — {r.description or ''} for r in results
50 "
51 Navegador MCP server — exposes graph context tools to AI coding poses graph context tools CP servarguments["cypher"])
52 fault"down limit=argumentfault": "",
53 fault": "",
54 },
55 },
56 "required": ["name"],
57 },
58 ),
59 Tool(
60
61 TextContent inputSchema={
62 type=" is None:
63 _text",
64 ts disabled in read-on)]
65 from navegador.ingestion import RepoIngester
66
67 ingester = RepoIngester(loader.store)
68 stats = ingestarguments["file_path"])
69 fmt = arguments.get("format", "markdown")
70 text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
71 return [TextContent(type="text", text=text)]
72
73 elif name == "load_function_context":
74 bundle = load depth=arguments.get("depth", 2),
75 guments.get("formatormat", "markdown")
76 text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
77 return [TextContent(type="text", text=text)]
78
79 elif name == "load_class_context":
80 bundle = loader.load_class(arguments["name"], arguments["file_path"])
81 fmt = arguments.get("format", "markdown")
82 text = bundle.to_markdown() if fmt == "markdown" else bundle.to_json()
83 return [TextContent(type="text", text=text)]
84
85 elif name == "search_symbols":
86 results = loader.search(arguments["query"],undle.to_markdown limit=arguments.get("lim
87 "
88 Navegador MCP server — exposes graph context tools to AI coding poses graph context tools to AI coding agents.
89
90 Run:
91 navegador mcp --db .navegador/grapCP server — exposes graph query"], limit
92 read-only mode.",
93 ("limit", 20))
94 if not results:
95 return [TextContent(tundle.to_markdownf"- **{r.type}** `{r.name}` — {r.description or ''} for r in results
96 "
97 Navegador MCP server — exposes graph context tools to AI coding poses graph context tools CP servarguments["cypher"])
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -0,0 +1 @@
1
+[build-sys
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -0,0 +1 @@
 
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -0,0 +1 @@
1 [build-sys

No diff available

--- a/tests/test_context.py
+++ b/tests/test_context.py
@@ -0,0 +1,6 @@
1
+"""Tests for context bundle serialization (no graph required)."""test_bundle_to_dict():
2
+ bundled = bundle.to_dict()
3
+
4
+
5
+def test_bundle_to_json():undle.to_json())
6
+ assert datamd = bundleassert "get_user" in md
--- a/tests/test_context.py
+++ b/tests/test_context.py
@@ -0,0 +1,6 @@
 
 
 
 
 
 
--- a/tests/test_context.py
+++ b/tests/test_context.py
@@ -0,0 +1,6 @@
1 """Tests for context bundle serialization (no graph required)."""test_bundle_to_dict():
2 bundled = bundle.to_dict()
3
4
5 def test_bundle_to_json():undle.to_json())
6 assert datamd = bundleassert "get_user" in md
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -0,0 +1,4 @@
1
+from navegador.graph.schema import ""Tests for graph sche"""Tests for graph schem assert NodeLabel.File == "File"
2
+ assert NodeLabel.assert NodeLabel.Class == "Class assert Edgassert EdgeType.IMPORTS == "IMPORTS"
3
+ assert EdgeType.CONTAINS == "CONTAINS"
4
+ assert EdgeT
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -0,0 +1,4 @@
 
 
 
 
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -0,0 +1,4 @@
1 from navegador.graph.schema import ""Tests for graph sche"""Tests for graph schem assert NodeLabel.File == "File"
2 assert NodeLabel.assert NodeLabel.Class == "Class assert Edgassert EdgeType.IMPORTS == "IMPORTS"
3 assert EdgeType.CONTAINS == "CONTAINS"
4 assert EdgeT

Keyboard Shortcuts

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