* perf(scope-resolution): use owner-keyed lookup for Step 2 member resolution (#1656) * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(scope-resolution): index Const/Static in FieldRegistry for Step 2 lookup Extend FieldRegistry to hold multiple defs per (owner, name), reconcile Const and Static into the owner-keyed index, and wire lookupAllByOwner through the production hook so Step 2 does not drop field kinds the registry never indexed. Pass explicitReceiver on read/write reference sites and document undefined-vs-empty hook semantics for defs fallback. Co-authored-by: Cursor <cursoragent@cursor.com> * perf(scope-resolution): centralize O(1) owned-member hook and guard hot path Extract lookupOwnedMembersByOwner for the production Step 2 hook so merges stay O(1) per registry with no defs.byId scan. Add a perf-contract unit test that throws if byId.values runs when the hook is wired. Reuse a frozen empty sentinel on double miss to avoid per-probe allocations. Co-authored-by: Cursor <cursoragent@cursor.com> * chore: drop unused buildFieldRegistry import * chore(scope-resolution): apply ce-code-review safe_auto fixes - Drop unreachable return + unused values() capture in perf-contract trap (Finding #7) - Type lookupOwnedMembersByOwner ownerDefId as DefId (Finding #9) - Add Static-kind Step 2 lookup test mirroring the Const case (Finding #11) * docs(field-registry): document lookupFieldByOwner first-wins semantics Audit of all 6 production callers (call-processor.ts:2279, walkers.ts:535, receiver-bound-calls.ts:380+730, type-env.ts:627+631) confirms none depends on last-wins precedence — all treat the return as a generic 'field with this name owned by this class'. Clarify the JSDoc to surface the semantic change introduced when FieldRegistry moved from last-wins to append-order storage (ce-code-review finding #2). * test(scope-resolution): extend Step 2 perf contract to implicit-self, MRO, field paths Adds three sibling tests under the Step 2 perf contract describe block, each asserting defs.byId.values() does NOT execute when ownedMembersByOwner is wired: - implicit-self receiver via typeBindings.self (no explicitReceiver branch) - 2-level MRO chain (Child extends Parent, save resolves on Parent at depth 1) - FieldRegistry read via Step 2 (property lookup, separate registry path) Pins the perf invariant on every distinct entry into walkReceiverTypeBinding so a regression bypassing the hook on any sub-path now fails CI immediately (ce-code-review finding #8). * test(resolve-references): cover arity-overload filtering via resolveReferenceSites Pins the orchestration-layer wiring of providers.arityCompatibility: hook returns [save(arity 1), save(arity 2)], referenceSite.arity = 1, arityCompatibility verdicts 'compatible'/'incompatible' by parameterCount, exactly one reference emitted with toDef = the arity-1 overload. registries.test.ts already covered arity at the buildMethodRegistry level; this adds the missing entry-point check that resolveReferenceSites threads providers correctly through to lookupCore.Step5 (ce-code-review finding #10). * test(resolve-references): add hook-on vs hook-off parity test Runs resolveReferenceSites twice on the same fixture (Parent.save method hit + Child.name field hit, Child extends Parent MRO chain) — once with ownedMembersByOwner wired to a synthetic registry, once with the hook absent so collectOwnedMembers takes the defs.byId fallback. Asserts: - stats are identical (sitesProcessed / referencesEmitted / unresolved) - referenceIndex.bySourceScope entries have equal length - toDef sets are equal - each per-site reference (including evidence and depth) is .toEqual Locks the semantic-parity claim in code while both paths still exist. Will be removed alongside the fallback in finding #1 (ce-code-review #3). * test(typescript): probe Step 2 MRO walk against ambient (declare class) base Adds typescript-ambient-base-class fixture with an export declare class AmbientBase + Derived extends AmbientBase and a call site d.ambientMethod(). Integration assertions: - Both classes are detected - EXTENDS edge Derived → AmbientBase emitted - CALLS edge to ambient.ts:ambientMethod resolved via MRO walk Probes the ce-code-review #6 concern that ambient-only owners (whose bodies are never parsed) might be silently skipped by Step 2 after the owner-keyed lookup change. Result: the call resolves correctly — the method signature inside the declare class body still flows through reconcileOwnership into model.methods, so the hook returns the right ancestor hits. Residual risk is empirically closed. * feat(scope-resolution): route nested types via owner-keyed TypeRegistry Closes the Step 2 contract footgun where 'hook returns [] = authoritative miss' silently dropped any owned def whose NodeLabel was outside the method/field if-chain in reconcileOwnership. - TypeRegistry: add nestedByOwner Map + lookupAllByOwner(owner, simple) + registerByOwner(owner, simple, def). Mirrors MethodRegistry/ FieldRegistry shape; cleared with the rest on cascade clear. - reconcileOwnership: route class-like NodeLabels (Class/Interface/Enum/ Struct/Union/Trait/TypeAlias/Typedef/Record/Delegate/Annotation/ Template/Namespace) via types.registerByOwner. New nestedTypesRegistered stat. Idempotent skip via nodeId match. - validateOwnershipParity: extend the I9 invariant check to nested types. - lookupOwnedMembersByOwner: merge methods + fields + nested-type hits; short-circuit when any one source contributes the full result. Unblocks future receiver-MRO registries that need to resolve 'Outer.Inner' through the receiver's type-binding chain (ce-code-review finding #5a). * refactor(scope-resolution): make ownedMembersByOwner required; delete byId fallback Per ce-code-review finding #1, the optional-hook design encoded a silent O(|defs|) perf cliff into the type system: any RegistryContext built without the hook regressed Step 2 to scanning every def per probe with no warning. Production wires the hook unconditionally; the fallback was exercised only by tests. - RegistryContext.ownedMembersByOwner: required, returns readonly SymbolDefinition[] (no | undefined). Implementations MUST return [] on authoritative miss. - collectOwnedMembers in lookup-core.ts collapses to a one-line forward to the hook; the defs.byId.values() scan and simpleNameOf helper are deleted (simpleNameOf had no other consumers). - ResolveReferencesInput.ownedMembersByOwner: required to match. - Tests: drop three fallback-path tests (registries Const fallback, resolveReferenceSites no-hook fallback, resolveReferenceSites Const- undefined fallback) and the hook-vs-fallback parity test added by finding #3. makeCtx in registries.test.ts now defaults to a real owner-keyed scan over the test fixture defs so tests that don't care about the hook keep working. * perf(free-call-fallback): cache global callables by simple name once per pass pickUniqueGlobalCallable scanned scopes.defs.byId.values() on every free-call fallback site. After PR #1656 fixed Step 2, this scan became the dominant remaining O(|defs|) hot path on large repos (ce-code-review finding #4). - buildGlobalCallableIndex builds a Map<simpleName, SymbolDefinition[]> over scopes.defs once at the top of emitFreeCallFallback. Same filter the per-site scan applied: Function / Method / Constructor, keyed by the last .-segment of qualifiedName. - pickUniqueGlobalCallable consumes the prebuilt index via O(1) Map.get instead of iterating every def. Per-site complexity drops from O(|defs|) to O(|defs with this simple name|). - Cost: O(|defs|) once per pass instead of O(|defs| * |free-call sites|). Subsequent narrowing (arity, conversion-rank) and the model-side fallback (model.symbols.lookupCallableByName + model.methods.lookupMethodByName) are unchanged. * chore(autofix): apply prettier + eslint fixes via /autofix command * ci: trigger build --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Gergo Magyar <abhigyan1.patwari@gmail.com> |
||
|---|---|---|
| .. | ||
| .claude | ||
| hooks/claude | ||
| scripts | ||
| shadow-parity-dashboard | ||
| skills | ||
| src | ||
| test | ||
| vendor | ||
| .env.example | ||
| .npmignore | ||
| CHANGELOG.md | ||
| Dockerfile.test | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| tsconfig.test.json | ||
| vitest.config.ts | ||
GitNexus
Graph-powered code intelligence for AI agents. Index any codebase into a knowledge graph, then query it via MCP or CLI.
Works with Cursor, Claude Code, Codex, Windsurf, Cline, OpenCode, and any MCP-compatible tool.
Why?
AI coding tools don't understand your codebase structure. They edit a function without knowing 47 other functions depend on it. GitNexus fixes this by precomputing every dependency, call chain, and relationship into a queryable graph.
Three commands to give your AI agent full codebase awareness.
Quick Start
# Index your repo (run from repo root)
npx gitnexus analyze
That's it. This indexes the codebase, installs agent skills, registers Claude Code hooks, and creates AGENTS.md / CLAUDE.md context files — all in one command.
To configure MCP for your editor, run npx gitnexus setup once — or set it up manually below.
gitnexus setup auto-detects your editors and writes the correct global MCP config. You only need to run it once.
Editor Support
| Editor | MCP | Skills | Hooks (auto-augment) | Support |
|---|---|---|---|---|
| Claude Code | Yes | Yes | Yes (PreToolUse) | Full |
| Cursor | Yes | Yes | Yes (postToolUse, manual install) | Full |
| Codex | Yes | Yes | — | MCP + Skills |
| Windsurf | Yes | — | — | MCP |
| OpenCode | Yes | Yes | — | MCP + Skills |
Claude Code gets the deepest integration: MCP tools + agent skills + PreToolUse hooks that automatically enrich grep/glob/bash calls with knowledge graph context.
Community Integrations
| Agent | Install | Source |
|---|---|---|
| pi | pi install npm:pi-gitnexus |
pi-gitnexus |
MCP Setup (manual)
If you prefer to configure manually instead of using gitnexus setup:
Claude Code (full support — MCP + skills + hooks)
# macOS / Linux
claude mcp add gitnexus -- npx -y gitnexus@latest mcp
# Windows
claude mcp add gitnexus -- cmd /c npx -y gitnexus@latest mcp
Codex (full support — MCP + skills)
codex mcp add gitnexus -- npx -y gitnexus@latest mcp
Cursor / Windsurf
Add to ~/.cursor/mcp.json (global — works for all projects):
{
"mcpServers": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@latest", "mcp"]
}
}
}
OpenCode
Add to ~/.config/opencode/config.json:
{
"mcp": {
"gitnexus": {
"command": "npx",
"args": ["-y", "gitnexus@latest", "mcp"]
}
}
}
How It Works
GitNexus builds a complete knowledge graph of your codebase through a multi-phase indexing pipeline:
- Structure — Walks the file tree and maps folder/file relationships
- Parsing — Extracts functions, classes, methods, and interfaces using Tree-sitter ASTs
- Resolution — Resolves imports and function calls across files with language-aware logic
- Field & Property Type Resolution — Tracks field types across classes and interfaces for deep chain resolution (e.g.,
user.address.city.getName()) - Return-Type-Aware Variable Binding — Infers variable types from function return types, enabling accurate call-result binding
- Field & Property Type Resolution — Tracks field types across classes and interfaces for deep chain resolution (e.g.,
- Clustering — Groups related symbols into functional communities
- Processes — Traces execution flows from entry points through call chains
- Search — Builds hybrid search indexes for fast retrieval
The result is a LadybugDB graph database stored locally in .gitnexus/ with full-text search and semantic embeddings.
MCP Tools
Your AI agent gets these tools automatically:
| Tool | What It Does | repo Param |
|---|---|---|
list_repos |
Discover all indexed repositories | — |
query |
Process-grouped hybrid search (BM25 + semantic + RRF) | Optional |
context |
360-degree symbol view — categorized refs, process participation | Optional |
impact |
Blast radius analysis with depth grouping and confidence | Optional |
detect_changes |
Git-diff impact — maps changed lines to affected processes | Optional |
rename |
Multi-file coordinated rename with graph + text search | Optional |
cypher |
Raw Cypher graph queries | Optional |
With one indexed repo, the
repoparam is optional. With multiple, specify which:query({query: "auth", repo: "my-app"}).
MCP Resources
| Resource | Purpose |
|---|---|
gitnexus://repos |
List all indexed repositories (read first) |
gitnexus://repo/{name}/context |
Codebase stats, staleness check, and available tools |
gitnexus://repo/{name}/clusters |
All functional clusters with cohesion scores |
gitnexus://repo/{name}/cluster/{name} |
Cluster members and details |
gitnexus://repo/{name}/processes |
All execution flows |
gitnexus://repo/{name}/process/{name} |
Full process trace with steps |
gitnexus://repo/{name}/schema |
Graph schema for Cypher queries |
MCP Prompts
| Prompt | What It Does |
|---|---|
detect_impact |
Pre-commit change analysis — scope, affected processes, risk level |
generate_map |
Architecture documentation from the knowledge graph with mermaid diagrams |
CLI Commands
gitnexus setup # Configure MCP for your editors (one-time)
gitnexus analyze [path] # Index a repository (or update stale index)
gitnexus analyze --force # Force full re-index
gitnexus analyze --embeddings # Enable embedding generation (slower, better search)
gitnexus analyze --skip-agents-md # Preserve custom AGENTS.md/CLAUDE.md gitnexus section edits
gitnexus analyze --verbose # Log skipped files when parsers are unavailable
gitnexus analyze --max-file-size 1024 # Skip files larger than N KB (default: 512, cap: 32768)
gitnexus analyze --worker-timeout 60 # Increase worker idle timeout for slow parses
gitnexus mcp # Start MCP server (stdio) — serves all indexed repos
gitnexus serve # Start local HTTP server (multi-repo) for web UI
gitnexus index # Register an existing .gitnexus/ folder into the global registry
gitnexus list # List all indexed repositories
gitnexus status # Show index status for current repo
gitnexus clean # Delete index for current repo
gitnexus clean --all --force # Delete all indexes
gitnexus wiki [path] # Generate LLM-powered docs from knowledge graph
gitnexus wiki --model <model> # Wiki with custom LLM model (default: gpt-4o-mini)
# Repository groups (multi-repo / monorepo service tracking)
gitnexus group create <name> # Create a repository group
gitnexus group add <group> <groupPath> <registryName> # Add a repo to a group. <groupPath> is a hierarchy path (e.g. hr/hiring/backend); <registryName> is the repo's name from the registry (see `gitnexus list`)
gitnexus group remove <group> <groupPath> # Remove a repo from a group by its hierarchy path
gitnexus group list [name] # List groups, or show one group's config
gitnexus group sync <name> # Extract contracts and match across repos/services
gitnexus group contracts <name> # Inspect extracted contracts and cross-links
gitnexus group query <name> <q> # Search execution flows across all repos in a group
gitnexus group status <name> # Check staleness of repos in a group
Remote Embeddings
Set these env vars to use a remote OpenAI-compatible /v1/embeddings endpoint instead of the local model:
export GITNEXUS_EMBEDDING_URL=http://your-server:8080/v1
export GITNEXUS_EMBEDDING_MODEL=BAAI/bge-large-en-v1.5
export GITNEXUS_EMBEDDING_DIMS=1024 # optional, default 384
export GITNEXUS_EMBEDDING_API_KEY=your-key # optional, default: "unused"
gitnexus analyze . --embeddings
Works with Infinity, vLLM, TEI, llama.cpp, Ollama, LM Studio, or OpenAI. When unset, local embeddings are used unchanged.
Multi-Repo Support
GitNexus supports indexing multiple repositories. Each gitnexus analyze registers the repo in a global registry (~/.gitnexus/registry.json). The MCP server serves all indexed repos automatically.
Supported Languages
TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust, PHP, Kotlin, Swift, Ruby
Language Feature Matrix
| Language | Imports | Named Bindings | Exports | Heritage | Type Annotations | Constructor Inference | Config | Frameworks | Entry Points |
|---|---|---|---|---|---|---|---|---|---|
| TypeScript | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| JavaScript | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | ✓ |
| Python | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Java | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| Kotlin | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| C# | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Go | ✓ | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Rust | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| PHP | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | ✓ | ✓ |
| Ruby | ✓ | — | ✓ | ✓ | — | ✓ | — | ✓ | ✓ |
| Swift | — | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| C | — | — | ✓ | — | ✓ | ✓ | — | ✓ | ✓ |
| C++ | — | — | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
Imports — cross-file import resolution · Named Bindings — import { X as Y } / re-export tracking · Exports — public/exported symbol detection · Heritage — class inheritance, interfaces, mixins · Type Annotations — explicit type extraction for receiver resolution · Constructor Inference — infer receiver type from constructor calls (self/this resolution included for all languages) · Config — language toolchain config parsing (tsconfig, go.mod, etc.) · Frameworks — AST-based framework pattern detection · Entry Points — entry point scoring heuristics
Agent Skills
GitNexus ships with skill files that teach AI agents how to use the tools effectively:
- Exploring — Navigate unfamiliar code using the knowledge graph
- Debugging — Trace bugs through call chains
- Impact Analysis — Analyze blast radius before changes
- Refactoring — Plan safe refactors using dependency mapping
Installed automatically by both gitnexus analyze (per-repo) and gitnexus setup (global).
Requirements
- Node.js >= 18
- Git repository (uses git for commit tracking)
Release candidates
Stable releases publish to the default latest dist-tag. When a pull request
with non-documentation changes merges into main, an automated workflow also
publishes a prerelease build under the rc dist-tag, so early adopters can
try in-flight fixes without waiting for the next stable cut. (Docs-only
merges are skipped.)
# Try the latest release candidate (pre-stable — may change at any time)
npm install -g gitnexus@rc
# — or —
npx gitnexus@rc analyze
Release-candidate versions follow the standard semver prerelease format
X.Y.Z-rc.N, where X.Y.Z is the next stable target (bumped from the
current latest by patch by default; minor or major when kicking off a
bigger cycle) and N increments per published rc. Example sequence:
1.6.2-rc.1, 1.6.2-rc.2, …, then once 1.6.2 ships stable,
1.6.3-rc.1. See the Releases page
for the full list; stable latest is unaffected.
Troubleshooting
Cannot destructure property 'package' of 'node.target' as it is null
This crash was caused by a dependency URL format that is incompatible with certain npm/arborist versions (npm/cli#8126). It is fixed in gitnexus v1.6.2+. Upgrade to the latest version:
npx gitnexus@latest analyze # always uses the newest release
# — or —
npm install -g gitnexus@latest # upgrade a global install
If you still hit npm install issues after upgrading, these generic workarounds may help:
npm install -g npm@latest # update npm itself
npm cache clean --force # clear a possibly corrupt cache
Installation fails with native module errors
Some optional language grammars (Dart, Kotlin, Swift) require native compilation. If they fail, GitNexus still works — those languages will be skipped.
If npm install -g gitnexus fails on native modules:
# Ensure build tools are available (Linux/macOS)
# Ubuntu/Debian: sudo apt install python3 make g++
# macOS: xcode-select --install
# Retry installation
npm install -g gitnexus
Analyze warns about unavailable FTS or VECTOR extensions
GitNexus uses optional DuckDB extensions for BM25 and vector search. The gitnexus serve and MCP read paths only ever try to LOAD the extensions — they never block on a network install. The analyze command, by default, attempts one bounded out-of-process INSTALL if LOAD fails and proceeds even when that install times out, so the index is always written to disk; BM25/vector search degrade gracefully until the extensions become available.
Configure the behavior with two environment variables:
| Variable | Values | Default | Effect |
|---|---|---|---|
GITNEXUS_LBUG_EXTENSION_INSTALL |
auto, load-only, never |
auto |
auto runs one bounded INSTALL if LOAD fails. load-only only uses already-installed extensions (recommended for offline / firewalled environments). never skips optional extensions entirely. |
GITNEXUS_LBUG_EXTENSION_INSTALL_TIMEOUT_MS |
positive integer | 15000 |
Wall-clock budget for the out-of-process INSTALL child before it is killed. |
# Offline/airgapped: never reach the network for extensions
GITNEXUS_LBUG_EXTENSION_INSTALL=load-only npx gitnexus analyze
# Slow network: give extension downloads more time
GITNEXUS_LBUG_EXTENSION_INSTALL_TIMEOUT_MS=30000 npx gitnexus analyze
Analysis runs out of memory
For very large repositories:
# Increase Node.js heap size
NODE_OPTIONS="--max-old-space-size=16384" npx gitnexus analyze
# Exclude large directories
echo "vendor/" >> .gitnexusignore
echo "dist/" >> .gitnexusignore
Large files are being skipped
By default the walker skips files larger than 512 KB (see log line Skipped N large files (>512KB)). Raise the threshold via either the CLI flag or the environment variable — both accept a value in KB:
# CLI flag (takes precedence over the env var)
npx gitnexus analyze --max-file-size 2048 # skip only files > 2 MB
# Environment variable (persists across commands)
export GITNEXUS_MAX_FILE_SIZE=2048
npx gitnexus analyze
Values above 32768 KB (32 MB) are clamped to the tree-sitter parser ceiling; invalid values fall back to the 512 KB default with a one-time warning. When an override is active, analyze prints the effective threshold in its startup banner (e.g. GITNEXUS_MAX_FILE_SIZE: effective threshold 2048KB (default 512KB)).
Analyze reports a worker timeout
Worker parse timeouts are recoverable. GitNexus retries stalled worker jobs with backoff, splits large jobs to isolate slow files, and falls back to the sequential parser when needed. If a large repository needs more time per worker job, use either:
# CLI flag, in seconds
npx gitnexus analyze --worker-timeout 60
# Environment variable, in milliseconds
export GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=60000
npx gitnexus analyze
For repositories with very large source files, GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES controls the worker job byte budget. The default is 8388608 bytes (8 MB).
Privacy
- All processing happens locally on your machine
- No code is sent to any server
- Index stored in
.gitnexus/inside your repo (gitignored) - Global registry at
~/.gitnexus/stores only paths and metadata
Web UI
GitNexus also has a browser-based UI at gitnexus.vercel.app — 100% client-side, your code never leaves the browser.
Local Backend Mode: Run gitnexus serve and open the web UI locally — it auto-detects the server and shows all your indexed repos, with full AI chat support. No need to re-upload or re-index. The agent's tools (Cypher queries, search, code navigation) route through the backend HTTP API automatically.
License
Free for non-commercial use. Contact for commercial licensing.