* [+] Add django route discovery to create cross-link for multi-repo * [+] Update ingestion * [~] Fix bugs and abstraction violation * feat(python-http): add keyword url= and variable propagation for consumer detection - Add REQUESTS_KEYWORD_URL_PATTERNS for requests.get(url='...') keyword args - Add WRAPPER_URI_PATTERNS for generic wrapper.fetch(uri='...') calls - Add WRAPPER_URI_VAR_PATTERNS + buildLocalStringMap for uri=variable propagation - Add LOCAL_STRING_ASSIGNMENTS to track uri='...' assignments - Wire both direct-string and variable-propagation loops in scan() - Add normalizeConsumerPath() helper Note: Automatic cross-link detection remains limited for runtime-computed URLs (URLs built via .format(), string concat, or module constants). Manual manifest links needed for known cross-repo contracts. * [+] add extract uri and url keywork pattern for request http * feat(python-http): add variable propagation for uri=/url= consumer patterns Re-add LOCAL_STRING_ASSIGNMENTS, WRAPPER_URI_VAR_PATTERNS, buildLocalStringMap(), and normalizeConsumerPath() lost during cherry-pick merge of upstream keyword-URL commit. Together with the upstream WRAPPER_URI_PATTERNS and REQUESTS_KEYWORD_URL_PATTERNS, we now detect: - requests.get(url='literal') keyword args - wrapper.fetch(uri='literal') keyword args - wrapper.fetch(uri=variable) where variable was assigned a string literal * fix(group): discover Django roots relative to manage.py dir + multi-project (#1836 R1) A Django project not at the repo root (e.g. backend/manage.py) discovered zero routes: the settings module path was resolved repo-root-relative only, so backend/myproj/settings.py was never found and discovery returned null. Resolve settings, star-imported base settings, ROOT_URLCONF, and the root urls.py against the manage.py's own directory first, then the repo root (resolvedSettingsPath is now project-dir-aware so relative imports anchor correctly). Iterate every manage.py so a monorepo with several Django projects yields each project's root — the provider hook becomes plural (discoverRootRouteFiles → string[]) and the main-thread pass loops over all roots (inner-scoped continues, parser hoisted once per language). Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(group): remove dead code in Django root discovery (#1836 R9) - Collapse the identical if/else in extractStarImports to one push. - Drop the unreachable baseModule.startsWith('.') branch (baseModule is always a resolved slash-path or a bare absolute module — never dot-prefixed). - Import DjangoFileReader from django.ts instead of re-declaring the type. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): walk Django includes once per prefix, not per file (#1836 R2) The include() recursion guard was keyed on file path alone and shared across the whole walk, so a urlconf included under two prefixes (a "diamond" — the same app mounted at /v1/ and /v2/) emitted routes for only the first mount. Key the guard on (resolvedFilePath, accumulatedPrefix) at all three sites (function entry, path()-wrapped include, bare include) so a file reached under two distinct prefixes is walked once per prefix while a genuine cycle (same file + same prefix) still terminates — null/'' prefixes collapse to one key so a no-prefix re-entry is treated as a cycle. MAX_INCLUDE_DEPTH remains the backstop. Adds diamond + self-include-cycle tests. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): extract Django routes from non-list urlpatterns (#1836 R3) findUrlpatternsLists only accepted a list-literal RHS, so common shapes yielded zero routes: concatenation (urlpatterns = a + b), wrapper calls (format_suffix_patterns([...]), i18n_patterns, staticfiles_urlpatterns), and tuples. Add collectUrlpatternContainers to descend binary_operator operands, known wrapper-call list arguments, and tuples. Inherently-dynamic forms (DRF router.urls, comprehensions, bare names) still yield nothing but now emit a debug log so the silent-zero case is observable rather than mysterious. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(group): thread Django parser explicitly, drop module singleton (#1836 R4) extractDjangoRoutes relied on a module-level _djangoParser set via setDjangoParser before each call — hidden state that would break if a second language ever used the include re-parse path, and an easy-to-forget contract. Pass the tree-sitter parser as an explicit parameter of extractDjangoRoutes (the extractRoutes provider hook already receives it) and delete the global plus its setter. The Python provider wires it directly; tests pass the parser in place of the removed setDjangoParser() call. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ingestion): isolate a throwing extractRoutes in the cross-file route pass (#1836 R5) The main-thread cross-file route pass called provider.extractRoutes without a guard, so a throw (e.g. a future grammar edge case in the include() walk) would propagate out of the parse phase and abort the entire analyze — unlike the worker, which isolates per-file failures. Wrap the per-root extractRoutes call in try/catch that logs a warning and continues to the next root. Export extractCrossFileRoutes and add a unit test driving a stub provider whose extractRoutes throws, asserting the pass returns [] and does not propagate. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(ingestion): bucket only route-capable languages in cross-file pass (#1836 R6) extractCrossFileRoutes runs in the deferred band on every analyze (incl. warm all-cache-hit runs). It now derives the set of languages whose provider exposes the cross-file route hooks once, returns early if none do, and buckets only those languages' paths — so a non-framework repo no longer pays to bucket the languages it doesn't use here. Route results are intentionally not persisted across runs, so a Django repo still re-derives its routes each analyze; documented inline that cross-run route caching is a deliberate follow-up rather than implemented here. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(group): prettier-format http-patterns/python.ts (#1836 R7) The file was not formatted to the root .prettierrc (the consumer-path normalizer used single-line try/catch and method chains), so the CI quality/format check (`prettier --check .`) failed. Reflow only — no logic change (`git diff -w` confines the change to normalizeConsumerPath's layout). Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): dedup Python URI detections by byte offset, not line arithmetic (#1836 R8) The wrapper-URI dedup key was lineNum*1000+methodRow, which can collide for distinct calls in files over 1000 lines (carry into the row term) and can fail to dedup a genuine duplicate when a node straddles a line boundary. Key on node byte offsets (`${pathNode.startIndex}:${methodNode.startIndex}`), matching the sibling seenVarDetections dedup a few lines below. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ingestion): end-to-end Django cross-file route extraction (#1836 R10) Adds an integration test that runs runPipelineFromRepo against a Django fixture whose project lives under backend/, asserting the resulting Route graph nodes (/health, /api/items, /api/items/<int:pk>). This exercises the previously-untested main-thread orchestration glue (discovery → parse → extractRoutes → allExtractedRoutes → Route nodes) and, because the project is in a subdirectory, regresses the subdir-discovery fix (R1) — a repo-root-only resolver would discover nothing and emit zero Route nodes. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): anchor Django include() resolution at the project root (#1836 review F1) resolveIncludedFile tried the bare repo-root candidate (app/urls.py) before the project-relative one, so in a monorepo with both a repo-root app/ and a backend/ Django project that also has an app/, include('app.urls') from the backend project resolved to the WRONG service's routes. Probe up-tree from the root urls.py for the nearest manage.py (the Django project root / sys.path entry) and try that-anchored candidate first. Absolute module paths like `app.urls` now resolve to <projectRoot>/app/urls.py unambiguously. When no manage.py is reachable (e.g. unit tests with a urls-only reader) the prior strategy order is preserved. Adds a monorepo wrong-app test. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): drop bogus Django provider source-scan, use graph routes (#1836 review F2) The DJANGO_PATH_PATTERNS / DJANGO_URL_PATTERNS source scan emitted an HTTP provider contract for every path()/re_path()/url() string literal, without checking it was inside urlpatterns, without skipping include() mount points, and without composing the include() prefix across files. For `path('api/', include('app.urls'))` + child `path('items/', view)` it emitted providers for `/api` (a mount, not a route) and `/items` (un-prefixed) — which survived the exact-contract-ID dedup alongside the correct graph route `/api/items`, polluting cross-repo matching with false providers. Remove the Django provider patterns and their scan blocks. Django provider contracts come from the graph Route nodes, which the ingestion route extractor builds with includes already composed (and now correctly, per the other fixes). Python HTTP *consumer* patterns (requests/wrapper) are unaffected. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(group): match method-agnostic Django providers to any-method consumers (#1836 review F3) Django function views are method-agnostic, so extractDjangoRoutes emits httpMethod '*'. That '*' was dropped by normalizeRouteMethod and then defaulted to GET by the contract extractor, while the matcher only expanded wildcard *consumers* — so a `POST /api/items` consumer never matched the Django provider that was silently narrowed to GET. - routes.ts: preserve '*' as a method-agnostic marker on the Route node, so the contract layer emits a wildcard provider (http::*::path) instead of GET. - matching.ts: make findMatchingKeys symmetric — a specific-method consumer now matches an exact-method provider OR a wildcard (http::*::) provider on the same path, mirroring the existing wildcard-consumer expansion. Co-Authored-By: HuyNguyenDinh <61400397+HuyNguyenDinh@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Dinh Huy <huynd86@fpt.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| .claude | ||
| bench | ||
| hooks | ||
| scripts | ||
| 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, Antigravity (Google), 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.
On npm 11.x?
npxcan crash during install (Cannot destructure property 'package' of 'node.target'). Use the pnpm form instead:pnpm --allow-build=@ladybugdb/core --allow-build=gitnexus --allow-build=tree-sitter dlx gitnexus@latest analyzeSee Troubleshooting →
npx gitnexuscrashes withnode.target is null(npm 11) for the full matrix (global install, npm downgrade).
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. To configure only selected integrations, pass --coding-agent/-c with a comma-separated list or repeat the option, for example gitnexus setup -c cursor,codex.
Editor Support
| Editor | MCP | Skills | Hooks (auto-augment) | Support |
|---|---|---|---|---|
| Claude Code | Yes | Yes | Yes (PreToolUse) | Full |
| Cursor | Yes | Yes | Yes (postToolUse, manual install) | Full |
| Antigravity (Google) | Yes | Yes | Yes (AfterTool, Gemini CLI hooks schema) | 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 (paginated — limit/offset) |
— |
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({search_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 detected editors (one-time; use -c to select)
gitnexus uninstall # Preview removal of GitNexus MCP/skills/hooks (add --force to apply)
gitnexus analyze [path] # Index a repository (or update stale index)
gitnexus analyze --repair-fts # Fast path: rebuild/verify only FTS indexes on existing index data
gitnexus analyze --force # Full rebuild: re-parse + graph rebuild + FTS rebuild
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 analyze --wal-checkpoint-threshold 67108864 # 64 MiB. Control LadybugDB WAL auto-checkpoint threshold (default: 67108864 = 64 MiB; -1 keeps Ladybug stock ~16 MiB)
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)
# Direct graph queries — the same tools the MCP server exposes, no MCP daemon needed
gitnexus query "<concept>" # Process-grouped hybrid search
gitnexus context <symbol> [--uid <uid> | --file <path>] # 360° symbol view; flags disambiguate a shared name
gitnexus impact <symbol> [--uid <uid> | --file <path> | --kind <kind>] # Blast radius; flags disambiguate a shared name
gitnexus detect-changes # Map the working-tree diff to affected symbols and execution flows
gitnexus cypher "<query>" # Run a raw Cypher query against the knowledge graph
# 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
gitnexus uninstallreversesgitnexus setup— it removes the GitNexus MCP entries, hooks, and skill directories it added to each detected editor. Skill directories are identified by bundled gitnexus skill name (e.g.gitnexus-cli/), so if you customized files inside an installed skill directory, back them up first. It is a dry-run preview by default and prints the exact paths it would remove; pass--forceto apply. Per-repo indexes (gitnexus clean --all) and the global npm package (npm uninstall -g gitnexus) are left for you to remove.
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 error comes from npm 11.x's arborist while installing gitnexus (often via npx), before gitnexus code runs. It is triggered by platform-filtered optionalDependencies in native packages such as onnxruntime-node / @huggingface/transformers (used when indexing with --embeddings). GitNexus cannot catch it at runtime — use one of these workarounds:
pnpm --allow-build=@ladybugdb/core --allow-build=gitnexus --allow-build=tree-sitter dlx gitnexus@latest analyze # auto-selected when pnpm + npm 11+
npm install -g gitnexus@latest # global install avoids per-run npx reify
gitnexus analyze # if already installed globally
On pnpm 10+, lifecycle scripts are blocked unless explicitly allowed — the resolver adds --allow-build for @ladybugdb/core, gitnexus, and tree-sitter automatically when it picks pnpm dlx.
If you must stay on npm 11.x without pnpm, downgrade npm toolchain-wide (last resort):
npm install -g npm@10.9.0
See #1939 and the original #819 thread. An older variant of this crash (tree-sitter-dart tarball URL) was fixed in gitnexus v1.6.2+ (#820); if you still see install failures after upgrading, clear cache:
npm cache clean --force
npx gitnexus@latest analyze
ERR_DLOPEN_FAILED / lbugjs.node missing (pnpm dlx, pnpx)
GitNexus depends on @ladybugdb/core, whose native database addon
(lbugjs.node) is placed by a postinstall script. pnpm dlx, pnpx, and any
install run with --ignore-scripts skip lifecycle scripts, so the addon is
never put in place and the runtime crashes with ERR_DLOPEN_FAILED:
Error: dlopen(.../@ladybugdb/core/lbugjs.node, ...): tried: '...' (no such file)
code: 'ERR_DLOPEN_FAILED'
Options that run install scripts:
# pnpm dlx with explicit build permission (one-off, no global install required)
pnpm --allow-build=@ladybugdb/core --allow-build=gitnexus --allow-build=tree-sitter \
dlx gitnexus@latest serve
# npm: global install (recommended on npm 11+; bare npx may crash — see section above)
npm install -g gitnexus@latest
gitnexus serve
# npx (npm < 11, or after upgrading npm)
npx gitnexus@latest serve
# pnpm: global install with build scripts allowed (pnpm 10.2+; no approve-builds -g on pnpm 11+)
pnpm add -g --allow-build=@ladybugdb/core --allow-build=gitnexus --allow-build=tree-sitter gitnexus
gitnexus serve
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. |
GITNEXUS_WAL_CHECKPOINT_THRESHOLD |
integer >= -1 |
67108864 (64 MiB) |
LadybugDB WAL auto-checkpoint threshold during analyze (bytes). Auto-checkpoint remains enabled; -1 keeps Ladybug's stock ~16 MiB. Larger thresholds reduce checkpoint frequency but increase the WAL size at rotation time — choose a smaller value on disk-constrained environments. |
# 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 quarantines a file that repeatedly crashes its worker (respawning the slot so the pool keeps going). 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).
Worker pool resilience tuning
Three env vars expose the pool's resilience layers (respawn budget, cumulative-timeout cap, circuit breaker). Defaults are tuned for typical repos; bump them when an analyze legitimately needs more retries, or lower them to fail-fast on a known-bad shape.
| Variable | Default | Effect |
|---|---|---|
GITNEXUS_WORKER_MAX_RESPAWNS_PER_SLOT |
3 |
Max replacement spawns per slot before the slot is dropped from the active rotation. |
GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS |
5 × subBatchTimeoutMs |
Total retry wall-time budget per job before quarantining. Bounds exponentially-growing retry waits. |
GITNEXUS_WORKER_CONSECUTIVE_FAILURE_THRESHOLD |
max(3, poolSize) |
Per-slot consecutive deaths before the pool's circuit breaker trips. After tripping, dispatches require a fresh pool. |
Graph cleanup tuning
After scope resolution, analyze prunes inert block-local value symbols (a function-local const/let/var that ends up with only its structural File→DEFINES edge) to keep the graph focused on cross-symbol relationships. Module/file-scope symbols, class members, and any local with a real edge are always kept.
| Variable | Default | Effect |
|---|---|---|
GITNEXUS_KEEP_LOCAL_VALUE_SYMBOLS |
unset | Set to 1/true to keep inert block-local value symbols instead of pruning them. |
Programmatic callers can pass keepLocalValueSymbols: true in PipelineOptions instead of setting the env var.
Hook augmentation/notifications are silently skipped
The Claude Code / Antigravity hooks intentionally stay silent on normal skip
paths so strict hook runners (e.g. Codex PreToolUse) never see unexpected
output. A search may not be augmented — or a stale-index reminder may not appear
on stderr — when the GitNexus MCP server owns the repo DB, when the DB-lock probe
times out and fails closed, or when the index is already current.
To see why a hook skipped, set GITNEXUS_DEBUG=1 and re-run the action — the hook
writes the reason (e.g. [GitNexus] augment skipped: MCP server owns DB) and the
stale-index hint to its stderr:
GITNEXUS_DEBUG=1 <your command> # surfaces hook skip/diagnostic reasons on stderr
Only GITNEXUS_DEBUG=1 and GITNEXUS_DEBUG=true enable diagnostics; every other
value (including 0 and false) is treated as off. Diagnostics go to stderr
only — the hook's structured stdout (the JSON the agent consumes) is unaffected.
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.