Graph-powered code intelligence engine — indexes your codebase into a knowledge graph and exposes it via MCP tools for AI agents and a CLI for developers.
$ synaptiq analyze .
Phase 1: Walking files... 142 files found
Phase 3: Parsing code... 142/142
Phase 5: Tracing calls... 847 calls resolved
Phase 7: Analyzing types... 234 type relationships
Phase 8: Detecting communities... 8 clusters found
Phase 9: Detecting execution flows... 34 processes found
Phase 10: Finding dead code... 12 unreachable symbols
Phase 11: Analyzing git history... 18 coupled file pairs
Done in 4.2s — 623 symbols, 1,847 edges, 8 clusters, 34 flows
Most code intelligence tools treat your codebase as flat text. Synaptiq builds a structural graph — every function, class, import, call, type reference, and execution flow becomes a node or edge in a queryable knowledge graph. AI agents using Synaptiq don't just search for keywords; they understand how your code is connected.
For AI agents (Claude Code, Cursor, agent swarms):
- "What breaks if I change this function?" — blast radius via call graph + type references + git coupling
- "What code is never called?" — dead code detection with framework-aware exemptions
- "Show me the login flow end-to-end" — execution flow tracing from entry points through the call graph
- "Which files always change together?" — git history change coupling analysis
For developers:
- Instant answers to architectural questions without grepping through files
- Find dead code, tightly coupled files, and execution flows automatically
- Raw Cypher queries against your codebase's knowledge graph
- Watch mode that re-indexes on every save
Zero cloud dependencies. Everything runs locally — parsing, graph storage, embeddings, search. No API keys, no data leaving your machine.
- Features
- Supported Languages
- Installation
- Quick Start
- CLI Reference
- MCP Integration
- Multi-Agent Concurrency
- Knowledge Graph Model
- Architecture
- Example Workflows
- How It Compares
- Development
Synaptiq doesn't just parse your code — it builds a deep structural understanding through 11 sequential analysis phases:
| Phase | What It Does |
|---|---|
| 1. File Walking | Walks repo respecting .gitignore, filters by supported languages |
| 2. Structure | Creates File/Folder hierarchy with CONTAINS relationships |
| 3. Parsing | tree-sitter AST extraction — functions, classes, methods, interfaces, enums, type aliases |
| 4. Import Resolution | Resolves import statements to actual files (relative, absolute, bare specifiers) |
| 5. Call Tracing | Maps function calls with confidence scores (1.0 = exact match, 0.5 = fuzzy) |
| 6. Heritage | Tracks class inheritance (EXTENDS) and interface implementation (IMPLEMENTS) |
| 7. Type Analysis | Extracts type references from parameters, return types, and variable annotations |
| 8. Community Detection | Leiden algorithm clusters related symbols into functional communities |
| 9. Process Detection | Framework-aware entry point detection + BFS flow tracing |
| 10. Dead Code Detection | Multi-pass analysis with override, protocol, and decorator awareness |
| 11. Change Coupling | Git history analysis — finds files that always change together |
Three search strategies fused with Reciprocal Rank Fusion:
- BM25 full-text search — fast exact name and keyword matching via KuzuDB FTS
- Semantic vector search — conceptual queries via 384-dim embeddings (BAAI/bge-small-en-v1.5)
- Fuzzy name search — Levenshtein fallback for typos and partial matches
Test files are automatically down-ranked (0.5x), source-level functions/classes boosted (1.2x).
Finds unreachable symbols with intelligence — not just "zero callers" but a multi-pass analysis:
- Initial scan — flags functions/methods/classes with no incoming calls
- Exemptions — entry points, exports, constructors, test code, dunder methods,
__init__.pypublic symbols, decorated functions,@propertymethods - Override pass — un-flags methods that override non-dead base class methods (handles dynamic dispatch)
- Protocol conformance — un-flags methods on classes conforming to Protocol interfaces
- Protocol stubs — un-flags all methods on Protocol classes (interface contracts)
When you're about to change a symbol, Synaptiq traces upstream through:
- Call graph — every function that calls this one, recursively (batched BFS — one query per depth level)
- Type references — every function that takes, returns, or stores this type
- Git coupling — files that historically change alongside this one
Uses the Leiden algorithm (igraph + leidenalg) to automatically discover functional clusters in your codebase. Each community gets a cohesion score and auto-generated label based on member file paths.
Detects entry points using framework-aware patterns:
- Python:
@app.route,@router.get,@click.command,test_*functions,__main__blocks - JavaScript/TypeScript: Express handlers, exported functions,
handler/middlewarepatterns
Then traces BFS execution flows from each entry point through the call graph, classifying flows as intra-community or cross-community.
Analyzes 6 months of git history to find hidden dependencies that static analysis misses:
coupling(A, B) = co_changes(A, B) / max(changes(A), changes(B))
Files with coupling strength >= 0.3 and 3+ co-changes get linked. Surfaces coupled files in impact analysis.
Live re-indexing powered by a Rust-based file watcher (watchfiles):
$ synaptiq watch
Watching /Users/you/project for changes...
[10:32:15] src/auth/validate.py modified -> re-indexed (0.3s)
[10:33:02] 2 files modified -> re-indexed (0.5s)- File-local phases run immediately on change — parsing happens without holding any lock
- Global phases (communities, processes, dead code) batch every 30 seconds
- The CPU-intensive parse step and the I/O-intensive storage write are split so locks are held for the shortest possible window
Structural diff between branches using git worktrees (no stashing required):
$ synaptiq diff main..feature
Symbols added (4):
+ process_payment (Function) -- src/payments/stripe.py
+ PaymentIntent (Class) -- src/payments/models.py
Symbols modified (2):
~ checkout_handler (Function) -- src/routes/checkout.py
Symbols removed (1):
- old_charge (Function) -- src/payments/legacy.py| Language | Extensions | Parser |
|---|---|---|
| Python | .py |
tree-sitter-python |
| TypeScript | .ts, .tsx |
tree-sitter-typescript |
| JavaScript | .js, .jsx, .mjs, .cjs |
tree-sitter-javascript |
# With uv (recommended) — installs as a global CLI tool
uv tool install synaptiq
# With pip
pip install synaptiq
# As a project dependency
uv add synaptiq
# With Neo4j backend support
pip install synaptiq[neo4j]Requires Python 3.11+.
git clone https://github.com/scanadi/synaptiq.git
cd synaptiq
uv sync --all-extras
uv run synaptiq --helpcd your-project
synaptiq analyze .# Search for symbols
synaptiq query "authentication handler"
# Get full context on a symbol
synaptiq context validate_user
# Check blast radius before changing something
synaptiq impact UserModel --depth 3
# Find dead code
synaptiq dead-code
# Run a raw Cypher query
synaptiq cypher "MATCH (n:Function) WHERE n.is_dead = true RETURN n.name, n.file_path"# Watch mode — re-indexes on every save
synaptiq watch
# Or re-analyze manually
synaptiq analyze .synaptiq analyze [PATH] Index a repository (default: current directory)
--full Force full rebuild (skip incremental)
synaptiq status Show index status for current repo
synaptiq list List all indexed repositories
synaptiq clean Delete index for current repo
--force / -f Skip confirmation prompt
synaptiq query QUERY Hybrid search the knowledge graph
--limit / -n N Max results (default: 20)
synaptiq context SYMBOL 360-degree view of a symbol
synaptiq impact SYMBOL Blast radius analysis
--depth / -d N BFS traversal depth (default: 3)
synaptiq dead-code List all detected dead code
synaptiq cypher QUERY Execute a raw Cypher query (read-only)
synaptiq watch Watch mode — live re-indexing on file changes
synaptiq diff BASE..HEAD Structural branch comparison
synaptiq setup Print MCP configuration JSON
--claude For Claude Code
--cursor For Cursor
synaptiq mcp Start the MCP server (stdio transport)
synaptiq serve Start MCP server with daemon features
--watch, -w Enable live file watching with auto-reindex
synaptiq --version Print version
Synaptiq exposes its full intelligence as an MCP server, giving AI agents like Claude Code and Cursor deep structural understanding of your codebase.
Add to your .claude/settings.json or project .mcp.json:
{
"mcpServers": {
"synaptiq": {
"command": "synaptiq",
"args": ["serve", "--watch"]
}
}
}This starts the MCP server with live file watching — the knowledge graph updates automatically as you edit code. To run without watching, use "args": ["mcp"] instead.
Or run the setup helper:
synaptiq setup --claudeFor richer Claude Code integration, copy the bundled skill into your project:
cp -r "$(python -c 'import synaptiq; import pathlib; print(pathlib.Path(synaptiq.__file__).parent.parent.parent)')"/.claude/skills/synaptiq .claude/skills/Or manually copy .claude/skills/synaptiq/SKILL.md from the Synaptiq repo. This teaches Claude when and how to use Synaptiq's tools, the knowledge graph schema, Cypher query patterns, and investigation workflows.
For a batteries-included Claude Code experience, install the Synaptiq Claude Plugin — it bundles the MCP server, skill, setup command, and a session hook in a single install:
claude plugins add scanadi/synaptiq-claude-pluginThen run /synaptiq:setup to install Synaptiq, index your codebase, and configure the MCP connection automatically.
Add to your Cursor MCP settings:
{
"synaptiq": {
"command": "synaptiq",
"args": ["serve", "--watch"]
}
}Or run:
synaptiq setup --cursorOnce connected, your AI agent gets access to these tools:
| Tool | Description |
|---|---|
synaptiq_list_repos |
List all indexed repositories with stats |
synaptiq_query |
Hybrid search (BM25 + vector + fuzzy) across all symbols |
synaptiq_context |
360-degree view — callers, callees, type refs, community, processes |
synaptiq_impact |
Blast radius — all symbols affected by changing the target |
synaptiq_dead_code |
List all unreachable symbols grouped by file |
synaptiq_detect_changes |
Map a git diff to affected symbols in the graph |
synaptiq_cypher |
Execute read-only Cypher queries against the knowledge graph |
Every tool response includes a next-step hint guiding the agent through a natural investigation workflow:
query -> "Next: Use context() on a specific symbol for the full picture."
context -> "Next: Use impact() if planning changes to this symbol."
impact -> "Tip: Review each affected symbol before making changes."
| Resource URI | Description |
|---|---|
synaptiq://overview |
Node and relationship counts by type |
synaptiq://dead-code |
Full dead code report |
synaptiq://schema |
Graph schema reference for writing Cypher queries |
Synaptiq is designed for environments where multiple AI agents hit the same codebase simultaneously — Claude Code with sub-agents, Cursor with background indexing, or any swarm-of-agents setup.
flowchart TB
A1(["🤖 Agent 1"])
A2(["🤖 Agent 2"])
A3(["🤖 Agent 3"])
AN(["🤖 Agent N"])
subgraph PRIMARY ["🟢 Primary Instance"]
MCP["📡 MCP Server"]
FW["👁️ File Watcher<br/>watchfiles"]
RWL{{"🔒 AsyncRWLock<br/>concurrent reads · exclusive writes"}}
DB[(🗄️ KuzuDB + Pool<br/>8 read connections)]
SOCK["🔌 Unix Socket Server<br/>16 concurrent slots"]
MCP --> RWL
FW --> RWL
RWL --> DB
SOCK --> RWL
end
P1["🔵 Proxy 1<br/>MCP + Socket Client"]
P2["🔵 Proxy 2<br/>MCP + Socket Client"]
PN["🔵 Proxy N ···"]
A1 -->|MCP stdio| MCP
A2 --> P1
A3 --> P2
AN --> PN
P1 -->|unix socket| SOCK
P2 -->|unix socket| SOCK
PN -->|unix socket| SOCK
style PRIMARY fill:#ecfdf5,stroke:#10b981,stroke-width:2px
classDef agent fill:#fbbf24,stroke:#d97706,color:#000,stroke-width:2px
classDef server fill:#6ee7b7,stroke:#10b981,color:#000,stroke-width:2px
classDef lock fill:#c4b5fd,stroke:#8b5cf6,color:#000,stroke-width:2px
classDef database fill:#67e8f9,stroke:#06b6d4,color:#000,stroke-width:2px
classDef socket fill:#fda4af,stroke:#f43f5e,color:#000,stroke-width:2px
classDef proxy fill:#93c5fd,stroke:#3b82f6,color:#000,stroke-width:2px
class A1,A2,A3,AN agent
class MCP,FW server
class RWL lock
class DB database
class SOCK socket
class P1,P2,PN proxy
How it works:
- The first
synaptiq serve --watchbecomes the primary — it owns the database, runs the file watcher, and opens a Unix domain socket - Subsequent instances detect the lock and become proxies — they forward all queries through the socket to the primary
- Role detection is automatic via
fcntl.flock()— no configuration needed - If the primary crashes, the OS releases the lock and the next instance takes over
| Layer | Mechanism | Purpose |
|---|---|---|
| Storage reads | Connection pool (8 kuzu.Connection objects) |
Multiple threads query in parallel |
| Storage writes | Dedicated write connection + AsyncRWLock | Exclusive access during index updates |
| MCP dispatch | AsyncRWLock.reader() with 120s timeout |
Concurrent tool/resource calls |
| Socket server | asyncio.Semaphore(16) backpressure |
Limits concurrent proxy dispatches |
| Socket client | Request-ID multiplexing + asyncio.Future |
Concurrent calls on a single connection |
| File watcher | Split parse/write pattern | CPU work runs lock-free; lock held only for I/O |
| Process lock | fcntl.flock() on .synaptiq/synaptiq.lock |
Primary election, auto-released on crash |
| Proxy recovery | Auto-reconnect (3 attempts, exponential backoff) | Handles primary restarts gracefully |
The AsyncRWLock allows multiple readers to query simultaneously while giving priority to writers:
- Reads (agent queries): Run concurrently — multiple agents can search, inspect context, and analyze impact at the same time
- Writes (file watcher updates): Get exclusive access, and pending writes block new reads to prevent writer starvation
- Timeout: All lock acquisitions have a 60-second timeout to prevent deadlocks
- Split processing: The watcher parses files without the lock (CPU-intensive), then acquires the write lock only for the storage I/O step
Unix domain socket paths are limited to 104 bytes on macOS. When the .synaptiq/synaptiq.sock path exceeds 100 bytes (common with deep directory nesting), Synaptiq automatically falls back to /tmp/synaptiq-<hash>.sock using a truncated SHA-256 hash of the data directory.
| Label | Description |
|---|---|
File |
Source file |
Folder |
Directory |
Function |
Top-level function |
Class |
Class definition |
Method |
Method within a class |
Interface |
Interface / Protocol definition |
TypeAlias |
Type alias |
Enum |
Enumeration |
Community |
Auto-detected functional cluster |
Process |
Detected execution flow |
| Type | Description | Key Properties |
|---|---|---|
CONTAINS |
Folder -> File/Symbol hierarchy | — |
DEFINES |
File -> Symbol it defines | — |
CALLS |
Symbol -> Symbol it calls | confidence (0.0-1.0) |
IMPORTS |
File -> File it imports from | symbols (names list) |
EXTENDS |
Class -> Class it extends | — |
IMPLEMENTS |
Class -> Interface it implements | — |
USES_TYPE |
Symbol -> Type it references | role (param/return/variable) |
EXPORTS |
File -> Symbol it exports | — |
MEMBER_OF |
Symbol -> Community it belongs to | — |
STEP_IN_PROCESS |
Symbol -> Process it participates in | step_number |
COUPLED_WITH |
File -> File that co-changes with it | strength, co_changes |
{label}:{relative_path}:{symbol_name}
Examples:
function:src/auth/validate.py:validate_user
class:src/models/user.py:User
method:src/models/user.py:User.save
file:src/auth/validate.py:
flowchart TB
SRC(["📁 Source Code<br/>.py · .ts · .js · .tsx · .jsx"])
PIPELINE["⚙️ Ingestion Pipeline — 11 Phases<br/>walk → structure → parse → imports → calls<br/>heritage → types → communities<br/>processes → dead_code → coupling"]
KG(["🧠 KnowledgeGraph<br/>in-memory during build"])
KUZU[(KuzuDB<br/>graph)]
FTS[(FTS<br/>BM25)]
VEC[(Vector<br/>HNSW)]
PROTO["⬡ StorageBackend Protocol"]
MCP_OUT["📡 MCP Server<br/>stdio transport"]
CLI_OUT["⌨️ CLI<br/>Typer + Rich"]
CLAUDE(["🤖 Claude Code · Cursor"])
DEV(["👤 Developer Terminal"])
SRC --> PIPELINE --> KG
KG --> KUZU
KG --> FTS
KG --> VEC
KUZU --> PROTO
FTS --> PROTO
VEC --> PROTO
PROTO --> MCP_OUT
PROTO --> CLI_OUT
MCP_OUT --> CLAUDE
CLI_OUT --> DEV
classDef source fill:#d8b4fe,stroke:#a855f7,color:#000,stroke-width:2px
classDef pipeline fill:#fed7aa,stroke:#f97316,color:#000,stroke-width:2px
classDef kg fill:#6ee7b7,stroke:#10b981,color:#000,stroke-width:2px
classDef store fill:#93c5fd,stroke:#3b82f6,color:#000,stroke-width:2px
classDef proto fill:#cbd5e1,stroke:#64748b,color:#000,stroke-width:2px
classDef iface fill:#5eead4,stroke:#14b8a6,color:#000,stroke-width:2px
classDef user fill:#fbbf24,stroke:#d97706,color:#000,stroke-width:2px
class SRC source
class PIPELINE pipeline
class KG kg
class KUZU,FTS,VEC store
class PROTO proto
class MCP_OUT,CLI_OUT iface
class CLAUDE,DEV user
| Layer | Technology | Purpose |
|---|---|---|
| Parsing | tree-sitter | Language-agnostic AST extraction |
| Graph Storage | KuzuDB | Embedded graph database with Cypher, FTS, and vector support |
| Graph Algorithms | igraph + leidenalg | Leiden community detection |
| Embeddings | fastembed | ONNX-based 384-dim vectors (~100MB, no PyTorch) |
| MCP Protocol | mcp SDK | AI agent communication via stdio |
| CLI | Typer + Rich | Terminal interface with progress bars |
| File Watching | watchfiles | Rust-based file system watcher |
| Gitignore | pathspec | Full .gitignore pattern matching |
Everything lives locally in your repo:
your-project/
+-- .synaptiq/
+-- kuzu/ # KuzuDB graph database (graph + FTS + vectors)
+-- meta.json # Index metadata and stats
+-- synaptiq.lock # Process lock (present while serve is running)
Add .synaptiq/ to your .gitignore.
The storage layer is abstracted behind a StorageBackend Protocol — KuzuDB is the default, with an optional Neo4j backend available via pip install synaptiq[neo4j].
# See everything connected to User
synaptiq context User
# Check blast radius
synaptiq impact User --depth 3
# Find which files always change with user.py
synaptiq cypher "MATCH (a:File)-[r:CodeRelation]->(b:File) \
WHERE a.name = 'user.py' AND r.rel_type = 'coupled_with' \
RETURN b.name, r.strength ORDER BY r.strength DESC"synaptiq dead-codesynaptiq cypher "MATCH (p:Process) RETURN p.name, p.properties ORDER BY p.name"synaptiq cypher "MATCH (a:File)-[r:CodeRelation]->(b:File) \
WHERE r.rel_type = 'coupled_with' \
RETURN a.name, b.name, r.strength \
ORDER BY r.strength DESC LIMIT 20"# Structural diff
synaptiq diff main..feature
# Or pipe a git diff through detect_changes (via MCP)
git diff main | synaptiq detect-changes| Capability | grep/ripgrep | LSP | Synaptiq |
|---|---|---|---|
| Text search | Yes | No | Yes (hybrid BM25 + vector) |
| Go to definition | No | Yes | Yes (graph traversal) |
| Find all callers | No | Partial | Yes (full call graph with confidence) |
| Type relationships | No | Yes | Yes (param/return/variable roles) |
| Dead code detection | No | No | Yes (multi-pass, framework-aware) |
| Execution flow tracing | No | No | Yes (entry point -> flow) |
| Community detection | No | No | Yes (Leiden algorithm) |
| Change coupling (git) | No | No | Yes (6-month co-change analysis) |
| Impact analysis | No | No | Yes (calls + types + git coupling) |
| AI agent integration | No | Partial | Yes (full MCP server) |
| Multi-agent concurrency | No | No | Yes (RWLock + connection pool) |
| Structural branch diff | No | No | Yes (node/edge level) |
| Watch mode | No | Yes | Yes (Rust-based, 500ms debounce) |
| Works offline | Yes | Yes | Yes |
git clone https://github.com/scanadi/synaptiq.git
cd synaptiq
uv sync --all-extras
# Run tests (581 tests)
uv run pytest
# Run only fast unit tests (skip e2e)
uv run pytest tests/core/ tests/cli/ tests/mcp/
# Lint
uv run ruff check src/ tests/
# Format
uv run ruff format src/ tests/
# Run from source
uv run synaptiq --helpsrc/synaptiq/
+-- cli/main.py # Typer CLI (analyze, query, serve, watch, ...)
+-- mcp/
| +-- server.py # MCP server (tools + resources + dispatch)
| +-- tools.py # Tool handler implementations
| +-- resources.py # Resource handler implementations
+-- core/
| +-- ingestion/
| | +-- pipeline.py # 11-phase pipeline orchestrator
| | +-- walker.py # File discovery respecting .gitignore
| | +-- structure.py # Folder/File hierarchy
| | +-- parser_phase.py # tree-sitter AST extraction
| | +-- imports.py # Import resolution
| | +-- calls.py # Call tracing with confidence
| | +-- heritage.py # Inheritance and implementation
| | +-- types.py # Type reference extraction
| | +-- community.py # Leiden clustering
| | +-- processes.py # Entry point + flow detection
| | +-- dead_code.py # Multi-pass dead code analysis
| | +-- coupling.py # Git co-change analysis
| | +-- watcher.py # File watcher with split parse/write
| +-- storage/
| | +-- base.py # StorageBackend protocol
| | +-- kuzu_backend.py # KuzuDB implementation + connection pool
| +-- daemon/
| | +-- rwlock.py # Write-preferring AsyncRWLock
| | +-- lock.py # fcntl.flock() based process lock
| | +-- socket_server.py # Unix socket server (primary)
| | +-- socket_client.py # Unix socket client (proxy)
| +-- search/hybrid.py # BM25 + vector + fuzzy fusion
| +-- parsers/ # Language-specific tree-sitter parsers
+-- config/
+-- languages.py # Language registry
+-- ignore.py # Gitignore loading
See CONTRIBUTING.md for detailed contribution guidelines.
Synaptiq was originally inspired by and built upon axon by @harshkedia177. This project has since evolved into an independent codebase with its own architecture, features, and direction.
MIT — see LICENSE.