Find a file
Gergő Magyar 3d30b94c46
fix(parse): survive non-cloneable worker results so large-repo analyze doesn't crash (#2112) (#2135)
* fix(parse): survive non-cloneable worker results so large-repo analyze doesn't crash (#2112)

A parse worker delivers its accumulated result to the main thread via
postMessage, which structured-clones the payload synchronously on the
worker thread and throws a DataCloneError on the first value it can't
serialize. The reporter's case was a node `properties` value pointing at
a native `toString`. The worker re-posted the throw as {type:'error'},
the pool counted it as a worker death, and under
GITNEXUS_WORKER_POOL_SIZE=1 the same graph re-threw on every respawn
until the slot's budget was exhausted and the whole parse phase aborted
-- defeating even the conservative single-worker workaround.

Add a clone-safety net at the worker result boundary. On a clone failure
the worker isolates the offending file, strips the non-cloneable value
from a plain extraction record (keeping the record -- strictly-missing
data, never wrong) or drops a whole ParsedFile so scope-resolution
re-derives it on the main thread with intact edge data, records the
affected paths on the result, warns naming the field + file so the leak
is diagnosable, and re-posts. Healthy runs are byte-identical: the net
runs only after a real DataCloneError, so there is zero overhead on the
fast path. Skipped paths surface via the parsing processor alongside the
skipped-language telemetry. The strip drops the same values the store
path's JSON.stringify already silently removes, so store/no-store runs
converge.

Scope: PR-1 -- failure mode C, the deterministic POOL_SIZE=1 killer. The
timeout/native-abort graceful-degradation cascade (failure modes A & B)
is coupled to downstream-exclusion + a hard worker watchdog and is
tracked as follow-up work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(parse): fail-closed clone-safety recovery + bound recursion depth (#2135 review)

The clone-safety recovery path could re-arm the #2112 worker-death cascade it
was built to prevent: in postResultCloneSafe the sanitizer call and the re-post
sat outside the try/catch, and containsNonCloneable/stripNonCloneable recursed
with a cycle guard but no depth bound. A throw inside the sanitizer (a RangeError
from a deeply-nested record, reproduced at depth >=3000) escaped to the message
handler's {type:'error'}, which under GITNEXUS_WORKER_POOL_SIZE=1 is the
respawn-budget-exhaustion abort.

Wrap the sanitizer + re-post in their own try/catch so any throw fails closed to
a primitive-only {type:'error'} deliberately, and thread a MAX_CLONE_DEPTH bound
through both scan/strip functions so an over-deep subtree is treated as
non-cloneable (dropped/undefined) instead of overflowing the stack. The
isStructuredCloneable catch-all is left broad on purpose — it bounds
structuredClone's own internal recursion in the non-plain-object probe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(parse): harden clone-safety against throwing getters and detached buffers (#2135 review)

Two sanitizer-defeat vectors let the re-post throw a DataCloneError again:

- A throwing getter on a record: containsNonCloneable/stripNonCloneable read
  obj[key], so a getter that throws escaped the scan/strip pass. Read defensively
  — a throwing property read is treated as non-cloneable (scan returns true,
  strip drops the property).
- A detached ArrayBuffer/TypedArray: both passed buffers/views through
  unconditionally, but structuredClone rejects a detached one, so the re-post
  threw. Route buffers/views through the authoritative isStructuredCloneable
  probe instead. No byteLength heuristic — a legitimately empty new Uint8Array(0)
  also has byteLength 0 yet clones fine, so a length check would false-positive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(parse): memoize stripped copies so DAG-aliased records aren't over-dropped (#2135 review)

stripNonCloneable carried a shared `seen` WeakSet and returned the ORIGINAL
(un-stripped) value on revisit. When a non-cloneable was reachable via two paths
(a DAG), the second path spliced the original function-bearing object back into
the output, so the rebuilt element failed the last-resort isStructuredCloneable
guard and the whole record was dropped as "unsalvageable" — contradicting the
"record kept, value stripped" contract.

Replace the WeakSet with a Map<object, stripped-copy>: allocate the empty copy,
memoize it before recursing into children (so cycles return the in-progress
copy), and return the memoized copy on revisit. DAG-aliased subtrees now collapse
to one shared stripped copy and are kept-and-stripped, not dropped. The array
branch moves from .map() to allocate-then-push so its identity can be
pre-inserted. Object Map/Set keys aren't identity-preserved across stripping —
acceptable because parse-result Maps are primitive-keyed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* perf(parse): single-pass clone-safety scan preserving array identity (#2135 review)

makeWorkerResultCloneSafe scanned each dirty array twice — a field-level
whole-array containsNonCloneable probe, then a per-element pass — and always
reassigned the field. Fold into one per-element pass that builds the output
array lazily (copying the clean prefix only once the first dirty element
appears) and reassigns the field only when something changed. A fully-clean
array is now scanned once and keeps its referential identity; the clean prefix
of a dirty array is copied by reference. Behavior is otherwise identical
(failure-path-only code).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(parse): drop unused generic + pin clone-safe field names to keyof (#2135 review)

makeWorkerResultCloneSafe carried a generic `<T extends Record<string,unknown>>`
that was never load-bearing (it mutates in place and returns {skipped}), and the
call site passed untyped string-literal option sets — so renaming `parsedFiles`
or `skippedPaths` would silently disable the drop-whole / skip protection.

Drop the generic (plain `Record<string,unknown>` param) and type the option sets
at the call site as `Set<keyof ParseWorkerResult>`, so a field rename is now a
compile error. The `as unknown as Record<string,unknown>` widening stays — it's
the standard cast for a no-index-signature interface (TS rejects a single-step
`as`); the function genuinely operates structurally on the result's arrays.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(parse): keep the per-file reason in the clone-safety skip log (#2135 review)

The processor's skipped-file warning logged only the paths, dropping the
per-file reason the worker already attached — losing the distinction between a
recoverable "stripped N value(s)" and a whole-record "dropped" entry. Format each
entry as `path (reason)` so the aggregate line carries the diagnostic detail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(parse): deterministic findFilePath attribution for ParsedNode (#2135 review)

findFilePath swept all child objects one level deep in Object.keys order, so a
ParsedNode could be attributed to a sibling child's path-like key instead of its
real path at properties.filePath. Check the known `properties` child first, then
fall back to the generic sweep, so node attribution is deterministic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(parse): zero skippedPaths in the slim cache result (#2135 review)

slimParseWorkerResultsForCache spread the worker result without clearing the
clone-safety skippedPaths telemetry, so a sanitized result persisted its skip
list into the on-disk parse-cache shard. Replay already ignores the field; zero
it (like calls/assignments/parsedFiles) to keep shards lean and the intent
explicit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(parse): exercise real postResultCloneSafe wiring + tighten RED control (#2135 review)

The integration GREEN worker re-implemented postResultCloneSafe inline, so the
production wiring (the {type:'warning'} post + the skippedPaths append) had no
coverage, and the RED control asserted a bare .rejects.toThrow() that any
failure would satisfy.

Extract postResultCloneSafe into a side-effect-free module (post-result.ts) —
importing it from the parse-worker entry module would construct the parser, post
ready, and attach the real handler — and have the GREEN test worker import and
call the real one. Tighten the RED matcher to the actual abort contract
(/circuit breaker|consecutive failures|respawn budget|could not be cloned/),
which also documents that the raw poison result aborts via the pool's
consecutive-failure circuit breaker under POOL_SIZE=1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(parse): recover the clone-safety net from any post failure, not only DataCloneError (#2135 review)

The V8 structured-clone research surfaced the net's one real correctness hole:
structuredClone invokes getters, and a getter that THROWS surfaces its own error
(a RangeError, etc.) — NOT a DataCloneError (confirmed against a real
MessageChannel). postResultCloneSafe gated recovery on isDataCloneError, so such
a throw re-threw past the sanitizer and re-armed, under POOL_SIZE=1, the
worker-death cascade the net exists to prevent.

Attempt the sanitize + re-post recovery for ANY first-post failure (the sanitizer
already reads properties defensively, so a throwing getter is dropped), falling
closed to a primitive-only {type:'error'} only if the re-post still fails. Adds
an integration case: a node with a throwing getter is recovered and delivered,
not re-thrown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(parse): name the exact offending key path in the clone-skip diagnostic (#2135 review)

The clone-safety net's skip reason named only the array field + file ("stripped
1 value from nodes"), not the offending property key — which is precisely why
the original #2112 leak stayed unpinned. Thread a dotted key path through
stripNonCloneable (recording each stripped value's path: properties.toString,
meta.data[3], …) and surface the first few in the reason ("from nodes:
properties.toString"). Now a single log line — or the contract/strict checks —
names the leaking property, so a residual runtime escape can be fixed at source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(parse): clone contract — a representative ParseWorkerResult is structured-cloneable (#2135 review)

Shape-regression guard: builds a representative ParseWorkerResult (typed as the
real interface) and asserts isStructuredCloneable. Typing it as ParseWorkerResult
makes adding a new boundary field a compile error here until the test is updated,
and the runtime assert catches a field whose type regresses to a non-cloneable
shape — independent of language input.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(parse): strict-mode clone gate (GITNEXUS_STRICT_CLONE) — fail loudly instead of silent sanitize (#2135 review)

The runtime net's silent recovery in production is exactly what let the original
#2112 leak stay unpinned. Add an opt-in strict mode (GITNEXUS_STRICT_CLONE=1,
inherited by workers): on a clone failure, postResultCloneSafe THROWS with the
exact offending key path instead of sanitizing + delivering, so a leak
introduced by a future provider/extractor change fails loudly at its origin
(CI/dev) rather than being quietly stripped. Off in production, where the net
keeps the run alive.

Adds a self-contained integration case (sets the flag, asserts the poison run
rejects with the key path) and skips the synthetic-poison suite under a global
strict run (its value there is running the REAL-extractor integration tests
under strict). Wiring a strict CI lane (GITNEXUS_STRICT_CLONE=1 on a vitest
integration step) is left to the maintainer — it needs a green full-suite
verification and touches the protected workflow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(server): don't ship pipelineResult across the analyze-worker IPC boundary (#2112)

The forked analyze worker reports completion to the parent over
child_process IPC, which uses Node's DEFAULT 'json' serialization
(api.ts forks with no `serialization:` option). `AnalyzeResult.pipelineResult`
is populated on every successful analysis and carries `pipelineResult.graph`
— the live KnowledgeGraph closure object. Sending the raw result is wrong
three ways: (1) the graph's nodes/relationships getters force-materialize
the entire graph into two arrays and JSON-stringify them on every analyze,
discarded immediately (a multi-hundred-MB no-op on a large repo — the #2112
scenario); (2) the graph's methods are own function properties that JSON
drops silently, so a surviving graph is a husk whose forEachNode() throws far
from the cause; (3) a BigInt/circular value anywhere in the payload makes
process.send throw TypeError synchronously — caught and re-sent as
{type:'error'}, mis-reporting a SUCCESSFUL analysis (DB already written) as a
FAILURE. This is the #2112 failure family on the server path, and unlike the
parse-worker result boundary it has no clone-safety net.

The parent (api.ts) reads only result.repoName; pipelineResult's real
consumers (CLI skill generation, cli/analyze.ts) call runFullAnalysis
in-process and never cross this fork. So project the result down to an
explicit JSON-safe allowlist of scalar fields. Typed as
Omit<AnalyzeResult,'pipelineResult'> so a future non-serializable field added
to AnalyzeResult fails to compile until handled here deliberately.

Found by the #2112 cross-process serialization-boundary audit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ingestion): Cloneable<T> + assertCloneable() compile-time clone-boundary guard (#2143)

The runtime clone-safety net is the production backstop; this is its
compile-time complement. The worker result is plain data except a few
`unknown`-typed sinks (a node's `properties` bag, the provider
`extractTemplateConstraints` / `collectCaptureSideChannel` hook returns) —
`unknown` lets a non-serializable value (a function, a leaked tree-sitter
SyntaxNode, …) cross the structured-clone boundary with no compile-time
guard. That is the structural hole #2112 leaked through.

`Cloneable<T>` is a homomorphic recursive mapped type that maps a function or
symbol member to `never`, so a struct carrying one is no longer assignable to
its own `Cloneable<T>`. `assertCloneable(value)` is a runtime identity (zero
cost) whose parameter is `T extends Cloneable<T> ? T : Cloneable<T>`, so a
clone-unsafe argument fails to compile, naming the offending key.

Because it is a homomorphic mapped type it preserves `interface` shapes and
`readonly` modifiers and needs NO index signature on the payload types — this
sidesteps the "closed interface is not assignable to a recursive
index-signature type" wall that blocked the original value-typed-`Cloneable`
attempt (the reason #2143 was deferred from PR #2135). The conditional
parameter type avoids the `T extends Cloneable<T>` circular-constraint error.

Tests: runtime identity contract, plus type-level @ts-expect-error assertions
(enforced by tsconfig.test.json) that a function/symbol member is rejected and
clean interface payloads are accepted. Applied to the real provider hooks in
the next commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ingestion): guard provider clone-boundary hooks with assertCloneable (#2143)

Apply the compile-time guard to the provider hooks that feed the `unknown`-typed
worker-result sinks, so a future non-serializable value in their payloads is a
compile error at the source site rather than a runtime DataCloneError at the
worker post:

- C++  extractTemplateConstraints  (CppConstraintPayload)
- C++  collectCaptureSideChannel    (CppCaptureSideChannel)
- C    collectCaptureSideChannel    (CCaptureSideChannel)
- Kotlin collectCaptureSideChannel  (KotlinCaptureSideChannel)

The C++ template-constraint adapter previously returned `unknown`; it now
returns the concrete `CppConstraintPayload | undefined` and routes its payload
through `assertCloneable`. The side-channel hooks are wrapped at their provider
wiring sites. `assertCloneable` is a runtime identity, so behavior is unchanged
(C static-linkage + C++ constraint suites stay green); the guarantee is the
type-check — src tsc now proves every nested member of those real payload trees
is structured-clone safe.

Test: type-level assertions (enforced by tsconfig.test.json) that each concrete
payload type is `Cloneable<T>`, INDEPENDENT of the provider wiring — so the
regression is caught even if the assertCloneable wrapper is later removed.
Proven non-vacuous (a function-bearing type fails the same assertion).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(parse): scan an array's non-index own properties in the clone sanitizer (#2135 review)

structuredClone serializes an array's NON-index own-enumerable properties (e.g.
`arr.meta = fn`) and throws DataCloneError on a non-cloneable one. The clone
sanitizer's array branches iterated numeric indices only, so such an array was
waved through (containsNonCloneable returned false, makeWorkerResultCloneSafe
left the field unrewritten with skipped:[]) — the re-post then threw, fell
through to the fail-closed {type:'error'}, and re-armed the POOL_SIZE=1 cascade
the net exists to prevent.

Add isArrayIndexKey() and, in BOTH containsNonCloneable and stripNonCloneable
array branches (kept in lockstep), scan/strip the non-index own-enumerable keys
after the index loop. A cloneable non-index prop is carried onto the stripped
copy; a non-cloneable one is stripped and recorded. Not reachable from current
parse output (no extractor attaches non-index array props) — a defense-in-depth
hole closed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(parse): contain a throw inside the clone sanitizer instead of escaping to fail-closed (#2135 review)

findFilePath was documented "never throws" but read element properties
unguarded in its generic sweep — a throwing getter at a non-path key (or a
Proxy with a throwing ownKeys trap) threw out of makeWorkerResultCloneSafe, past
postResultCloneSafe's recovery, to the fail-closed {type:'error'} that under
POOL_SIZE=1 re-arms the cascade the net prevents. Likewise a Proxy with a
throwing getPrototypeOf trap throws inside containsNonCloneable's instanceof
checks.

- findFilePath/pathFromChild now read via safeGet (try/catch) and guard
  Object.keys, honoring the "never throws" contract.
- Each element's sanitize in makeWorkerResultCloneSafe is wrapped: a throw during
  scan/strip drops that one element (recorded as "sanitizer error") rather than
  sinking the whole result — so one pathological element can't fail-close the run.
- Corrected the makeWorkerResultCloneSafe JSDoc ("ONLY after a DataCloneError" →
  after ANY post failure, matching the caller) and documented the deliberate
  failure-path double-traversal (the non-allocating pre-scan is what preserves
  clean-element referential identity).

Tests: a throwing getter on a path-less element is stripped & delivered (not
escaped); a Proxy structural-trap element is dropped, clean siblings survive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(parse): add a final cloneable postcondition gate to the clone sanitizer (#2135 review)

makeWorkerResultCloneSafe rewrote only ARRAY result fields, so a future
non-array sink (a nested object / Map result field) carrying a non-cloneable
value — or an array field whose own non-index property the element loop didn't
reach — would survive the sanitizer and throw on the re-post. Add a final
`if (!isStructuredCloneable(result))` gate that strips any remaining offending
field in place, making "the returned result is structured-cloneable" a hard
postcondition independent of future ParseWorkerResult shape. Failure-path-only
and a no-op once the array loop already made the result clean (the per-field
probe short-circuits every clean field, so it adds no work or skip entries then).

Tests: a function on a non-array result field is stripped & the result becomes
cloneable; the gate adds no skip entry when the array loop already cleaned up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(parse): reject an `any`-typed member in the Cloneable<T> compile-time guard (#2135 review)

`Cloneable<any>` previously resolved to `any` (not `never`), so a payload with
an `any`-typed member — the most likely escape hatch, since `unknown` is already
blocked — passed `assertCloneable` with no compile error. Add an `IsAny<T>`
branch (the canonical `0 extends 1 & T` probe) as the FIRST arm so `any` resolves
to `never`, matching how `unknown` is already rejected. It must precede the
primitive arm: `any extends CloneablePrimitive` would otherwise resolve to `any`
and re-admit it.

The IsAny-first arm perturbs inference for a bare `undefined` literal argument
(T infers as `unknown` → never); real consumers pass `X | undefined` unions
(the provider hooks), which are unaffected (src tsc clean), so the runtime
identity test now uses a `string | undefined` value — the realistic shape.

Tests: an `any` member fails `assertCloneable` (@ts-expect-error, enforced by
tsconfig.test.json) and `Cloneable<any>` resolves to `never` at the type level.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(server): type the analyze-worker IPC projection as a Pick allowlist, not Omit (#2135 review)

`AnalyzeResultIpc = Omit<AnalyzeResult,'pipelineResult'>` kept every other field
in the type — including optional ones like `isPrimaryBranch?` — so the type
advertised a field the runtime allowlist never sends, and the doc-comment's
"a future field fails to compile until handled here" only held for REQUIRED
fields. Switch to `Pick<AnalyzeResult, …the six scalar fields…>`: the allowlist
IS the type, so the projection return literal is exhaustive by construction
(omitting a key is a compile error) and a new `AnalyzeResult` field is simply
absent from the wire until deliberately added here. `isPrimaryBranch` is
intentionally excluded (nothing consumes it server-side over this fork; the
parent reads only `repoName`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(parse): remove the now-dead isDataCloneError export (#2135 review)

postResultCloneSafe recovers on ANY fast-path post failure and never inspects
the error type (a throwing getter surfaces a RangeError, not a DataCloneError —
gating on the type was the original net-gap bug). isDataCloneError has no
production caller; it was only exercised by its own unit test. Remove the
function and that test block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(parse): use the exported SkippedPath type in parsing-processor (#2135 review)

The clone-safety telemetry accumulator inlined `Array<{path,reason}>` — a
structural duplicate of the exported `SkippedPath`. Import and use the canonical
type so a future rename of its fields is a compile error here instead of a silent
structural drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(parse): document the cloneable-return contract on the worker-boundary hooks (#2135 review)

extractTemplateConstraints and collectCaptureSideChannel return `unknown` and
feed values across the worker structured-clone boundary, but the hook contracts
didn't state the cloneability requirement — a future language implementing them
without care could leak a non-serializable value. Document that the return MUST
be structured-clone-safe and should be wrapped with assertCloneable, so the
guarantee is a compile error at the source (#2143).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(parse): assert the clone-skip telemetry surfaces in the GREEN integration case (#2135 review)

The GREEN clone-safety integration test asserted only graph content (all files
present), not that the skippedPaths / {type:'warning'} wiring its docstring
claims to cover actually fired. Capture the production logger via _captureLogger
and assert the sanitize telemetry names the offending file (poison.ts) AND the
exact stripped key path (properties.toString) — proving the worker's
skippedPaths append + the parsing-processor warning surfaced end to end.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(server): cover the IPC projection against a real KnowledgeGraph (#2135 review)

The IPC projection tests used a hand-built hostile object. Add a case that puts
a real createKnowledgeGraph (whose nodes/relationships getters would materialize
the whole graph under JSON.stringify) in pipelineResult and asserts the
projection drops it entirely — the serialized payload stays under 300 bytes
(a materialized 50-node graph would be thousands), with the scalar fields intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(parse): cover the unsalvageable-drop branch and the skippedPaths merge union (#2135 review)

Two untested clone-safety branches from the tri-review:

- "dropped unsalvageable": a dirty element whose stripped copy is STILL not
  structured-cloneable must be dropped, not delivered (else the re-post throws).
  Add a deterministic test (a non-plain member with a stateful getter that the
  strip-time probe sees clean but that turns into a function on the post-strip
  verification) asserting the element is dropped and the run survives.

- mergeResult skippedPaths union across sub-batches. mergeResult (and its
  appendAll helper) was module-private in the parse-worker ENTRY module, which a
  main-thread test can't import (it runs MessagePort setup). Extract it to a
  side-effect-free result-merge.ts (mirroring post-result.ts) and unit-test the
  union (including the `??=` target-init path), the skippedLanguages sum, and
  array append. parse-worker imports it back; verified the built worker still
  parses + merges via the real-worker integration path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* style(parse): root-prettier format the clone-safety review-fix files (#2135 review)

Clears the failing `quality / format` CI gate (root prettier, not the
gitnexus-local config). Reformats the pre-existing #2143 wrapping lines in
c-cpp.ts + kotlin.ts plus the clone-safety review-fix files touched in this
PR-update (clone-safety.ts and the new/updated tests). Formatting-only — no
behavior change; tsc, the type-level assertions (tsconfig.test.json), and the
unit + integration suites stay green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(parse): avoid js/trivial-conditional in the type-level clone assertions (#2135 review)

CodeQL flagged the `expect(a && b && c).toBe(true)` lines in the type-level test
assertions as js/trivial-conditional: after type erasure the operands are
constant `true`, so the `&&` chain always evaluates the same. Replace the `&&`
chain with array equality (`expect([...]).toEqual([true, ...])`) — no
conditional, and the real assertions remain the `const x: …IsNever = true` /
`: IsCloneable<…> = true` annotations (enforced by tsconfig.test.json, which
fail to compile if a guard regresses).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 13:47:22 +01:00
.claude feat(mcp): paginate list_repos to avoid client token truncation (#2119) (#2120) 2026-06-09 19:59:54 +01:00
.claude-plugin chore: release v1.6.7 (#2126) 2026-06-09 21:37:33 +01:00
.cursor feat(review): add PR reviewer swarm agents (#1851) 2026-05-29 18:24:16 +01:00
.devcontainer feat(install): toolchain-free tree-sitter via vendored prebuilds (#2113) 2026-06-09 18:16:24 +01:00
.gemini/commands feat(review): add PR reviewer swarm agents (#1851) 2026-05-29 18:24:16 +01:00
.github fix(ci): make the prebuild PR push re-run-safe (--force-with-lease -> --force) (#2123) 2026-06-09 20:28:08 +01:00
.history/gitnexus fix(test): add --repo to CLI e2e tool tests for multi-repo environment 2026-03-18 08:12:25 +00:00
.husky feat: configure prettier with pre-commit hook (#563) 2026-03-28 14:58:04 +00:00
.sisyphus/drafts fixed constructor to method relation not getting stored in kuzu issue 2026-01-26 22:58:15 +05:30
deploy/kubernetes ci(docker): mirror signed images to Docker Hub alongside GHCR (#1029) 2026-04-23 18:59:26 +01:00
eslint-rules fix(windows): 32767-char tree-sitter crash + VECTOR extension SIGSEGV (#1433) 2026-05-10 16:00:36 +01:00
eval chore(deps): bump aiohttp in /eval in the uv group across 1 directory (#2008) 2026-06-04 07:02:09 +01:00
gitnexus fix(parse): survive non-cloneable worker results so large-repo analyze doesn't crash (#2112) (#2135) 2026-06-10 13:47:22 +01:00
gitnexus-claude-plugin fix(hooks): silence MCP-owned-DB augment skip for strict hook runners (#1913) (#2134) 2026-06-10 09:09:41 +01:00
gitnexus-cursor-integration fix(mcp): prevent sibling-clone repo ID collisions and correct generated MCP tool names (#2067) 2026-06-07 10:57:15 +01:00
gitnexus-shared feat(ingestion): M0 — taint/PDG substrate (schema + seams + spikes) (#2080) (#2092) 2026-06-08 18:56:10 +01:00
gitnexus-test-setup feat: merge gitnexus-mcp into gitnexus package - unified CLI+MCP 2026-02-04 01:12:41 +05:30
gitnexus-web feat(ingestion): M0 — taint/PDG substrate (schema + seams + spikes) (#2080) (#2092) 2026-06-08 18:56:10 +01:00
pr-swarm-review feat(review): add PR reviewer swarm agents (#1851) 2026-05-29 18:24:16 +01:00
.cursorrules docs: agent development framework, GitHub templates, eval refactor (#479) 2026-03-25 06:48:41 +00:00
.dockerignore fix(ci): Change docker base image from alpine to debian (#1014) 2026-04-21 21:31:58 +01:00
.env.example ci(docker): mirror signed images to Docker Hub alongside GHCR (#1029) 2026-04-23 18:59:26 +01:00
.git-blame-ignore-revs feat: configure eslint with unused import removal (#564) 2026-03-28 15:28:09 +00:00
.gitattributes feat(devcontainer): add devcontainer for Claude/Codex/Cursor CLIs (#1875) 2026-06-02 05:09:01 +01:00
.gitignore feat(review): add PR reviewer swarm agents (#1851) 2026-05-29 18:24:16 +01:00
.gitleaks.toml fix(ci): stabilize gitleaks after #2024 (#2027) 2026-06-04 12:28:59 +01:00
.mcp.json fix: use cross-platform npx command in .mcp.json 2026-02-22 17:46:04 +00:00
.prettierignore chore(quality): exclude test/fixtures from CodeQL, ESLint, and Prettier (#1313) 2026-05-04 09:35:34 +01:00
.prettierrc feat: configure prettier with pre-commit hook (#563) 2026-03-28 14:58:04 +00:00
.windsurfrules resources implemented and agents.md and skills updated to use it 2026-02-05 05:13:48 +05:30
AGENTS.md feat(install): toolchain-free tree-sitter via vendored prebuilds (#2113) 2026-06-09 18:16:24 +01:00
ARCHITECTURE.md fix(parse): correct worker-pool docs drift + surface worker-side stack on crash (#2068) (#2070) 2026-06-08 07:20:12 +01:00
CHANGELOG.md chore: release v1.5.3 — update CHANGELOG and package-lock 2026-04-01 21:35:24 +05:30
CLAUDE.md fix(mcp): prevent sibling-clone repo ID collisions and correct generated MCP tool names (#2067) 2026-06-07 10:57:15 +01:00
compound-engineering.local.md feat: Phase 7 type resolution — return-aware loop inference & PHP class-property iterables (#341) 2026-03-18 08:39:38 +00:00
CONTRIBUTING.md chore: Sync Claude plugin manifests with the 1.6.6 release (#2090) 2026-06-08 16:50:09 +01:00
docker-compose.yaml feat(web): support GITNEXUS_BACKEND_URL env var for Docker deployments (#1286) 2026-05-25 11:21:11 +01:00
docker-server.mjs feat(web): support GITNEXUS_BACKEND_URL env var for Docker deployments (#1286) 2026-05-25 11:21:11 +01:00
docker-server.test.mjs feat(web): support GITNEXUS_BACKEND_URL env var for Docker deployments (#1286) 2026-05-25 11:21:11 +01:00
Dockerfile.cli fix(docker): ship runtime-needed published assets (hooks/, skills/) into the image (#2130) (#2132) 2026-06-10 08:38:37 +01:00
Dockerfile.web fix(security): Pin Docker Node base images, remove runtime package-manager CVE surface, verify Trivy on PRs, and harden Dependabot policy (#1455) 2026-05-09 16:55:31 +01:00
DoD.md docs: add DoD.md repo-wide Definition of Done (#1032) 2026-04-23 08:00:37 +01:00
eslint.config.mjs fix(windows): 32767-char tree-sitter crash + VECTOR extension SIGSEGV (#1433) 2026-05-10 16:00:36 +01:00
GUARDRAILS.md fix: shard parse cache persistence on large repos (#1580) 2026-05-16 07:19:06 +01:00
LICENSE docs: update license copyright holder 2026-02-03 22:54:01 +05:30
llms.txt docs: agent development framework, GitHub templates, eval refactor (#479) 2026-03-25 06:48:41 +00:00
MIGRATION.md feat(mcp): rank context/impact disambiguation candidates and expose kind/file_path hints (#888) 2026-04-18 12:52:42 +01:00
package-lock.json feat: configure eslint with unused import removal (#564) 2026-03-28 15:28:09 +00:00
package.json feat(package): add gitnexus commands for analysis 2026-04-29 16:37:06 +03:00
README.md feat(mcp): paginate list_repos to avoid client token truncation (#2119) (#2120) 2026-06-09 19:59:54 +01:00
RUNBOOK.md docs: agent development framework, GitHub templates, eval refactor (#479) 2026-03-25 06:48:41 +00:00
SECURITY.md ci(security): add automated security and vulnerability scans (#1297) 2026-05-04 08:21:53 +01:00
skills.mdm FEAT: Added support for optional skill generation based on KuzuDB after initial repo analysis (npx gitnexus analyze --skills) (#171) 2026-03-13 08:29:13 +00:00
swift-ingestion-gaps.md docs: add macro declarations to Swift ingestion gaps 2026-03-23 14:21:32 +01:00
TESTING.md refactor(ingestion): delete legacy call-resolution DAG + heritage processor (RING4-1, #942) (#2023) 2026-06-04 11:07:37 +01:00
type-resolution-roadmap.md feat: implement cross-file binding propagation for multiple languages 2026-03-21 07:47:04 +00:00
type-resolution-system.md feat(ingestion): TypeScript registry-primary scope resolution (Ring 3) (#1050) 2026-04-26 08:23:08 +01:00

GitNexus

⚠️ Important Notice: GitNexus has NO official cryptocurrency, token, or coin. Any token/coin using the GitNexus name on Pump.fun or any other platform is not affiliated with, endorsed by, or created by this project or its maintainers. Do not purchase any cryptocurrency claiming association with GitNexus.

abhigyanpatwari%2FGitNexus | Trendshift

Join the official Discord to discuss ideas, issues etc!

Discord npm version License: PolyForm Noncommercial OpenSSF Scorecard CI Workflows

Enterprise (SaaS & Self-hosted) - akonlabs.com

Building nervous system for agent context.

Indexes any codebase into a knowledge graph — every dependency, call chain, cluster, and execution flow — then exposes it through smart tools so AI agents never miss code.

https://github.com/user-attachments/assets/172685ba-8e54-4ea7-9ad1-e31a3398da72

Like DeepWiki, but deeper. DeepWiki helps you understand code. GitNexus lets you analyze it — because a knowledge graph tracks every relationship, not just descriptions.

TL;DR: The Web UI is a quick way to chat with any repo. The CLI + MCP is how you make your AI agent actually reliable — it gives Cursor, Claude Code, Antigravity, Codex, and friends a deep architectural view of your codebase so they stop missing dependencies, breaking call chains, and shipping blind edits. Even smaller models get full architectural clarity, making it compete with Goliath models.


Star History

Star History Chart

Two Ways to Use GitNexus

CLI + MCP Web UI
What Index repos locally, connect AI agents via MCP Visual graph explorer + AI chat in browser
For Daily development with Cursor, Claude Code, Antigravity, Codex, Windsurf, OpenCode Quick exploration, demos, one-off analysis
Scale Full repos, any size Limited by browser memory (~5k files), or unlimited via backend mode
Install npm install -g gitnexus No install — gitnexus.vercel.app
Storage LadybugDB native (fast, persistent) LadybugDB WASM (in-memory, per session)
Parsing Tree-sitter native bindings Tree-sitter WASM
Privacy Everything local, no network Everything in-browser, no server

Bridge mode: gitnexus serve connects the two — the web UI auto-detects the local server and can browse all your CLI-indexed repos without re-uploading or re-indexing.


Enterprise

GitNexus is available as an enterprise offering - either as a fully managed SaaS or a self-hosted deployment. Also available for commercial use of the OSS version with proper licensing.

Enterprise includes:

  • PR Review - automated blast radius analysis on pull requests
  • Auto-updating Code Wiki - always up-to-date documentation (Code Wiki is also available in OSS)
  • Auto-reindexing - knowledge graph stays fresh automatically
  • Multi-repo support - unified graph across repositories
  • OCaml support - additional language coverage
  • Priority feature/language support - request new languages or features

Upcoming:

  • Auto regression forensics
  • End-to-end test generation

👉 Learn more at akonlabs.com

💬 For commercial licensing or enterprise inquiries, ping us on Discord or drop an email at founders@akonlabs.com


Development

  • ARCHITECTURE.md — packages, index → graph → MCP flow, where to change code
  • RUNBOOK.md — analyze, embeddings, stale index, MCP recovery, CI snippets
  • GUARDRAILS.md — safety rules and operational “Signs” for contributors and agents
  • CONTRIBUTING.md — license, setup, commits, and pull requests
  • TESTING.md — test commands for gitnexus and gitnexus-web

The CLI indexes your repository and runs an MCP server that gives AI agents deep 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? npx can crash during install with Cannot destructure property 'package' of 'node.target' (an npm/arborist bug, before GitNexus runs). Use pnpm instead — it builds the native deps explicitly:

pnpm --allow-build=@ladybugdb/core --allow-build=gitnexus --allow-build=tree-sitter dlx gitnexus@latest analyze

Or install globally (npm install -g gitnexus@latest) and run gitnexus analyze. See #1939.

To configure MCP for your editor, run npx gitnexus setup once — or set it up manually below.

Faster install (no C++ toolchain needed): set GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1 before npm install -g gitnexus to skip the vendored grammar materialize/build for tree-sitter-dart, tree-sitter-proto, tree-sitter-swift, and tree-sitter-kotlin — those four won't be parsed, but install completes in seconds without python3/make/g++. Strict =1 only — any other value falls through to the rebuild. See the tree-sitter-kotlin note below.

About tree-sitter-kotlin: like Dart/Proto/Swift, Kotlin is a vendored grammar (under gitnexus/vendor/tree-sitter-kotlin). Upstream tree-sitter-kotlin ships source only (no prebuilt binaries), so GitNexus builds the Kotlin platform prebuilds itself (via the build-tree-sitter-prebuilds GitHub Actions workflow) and vendors them — the same uniform pipeline now used for Dart, Proto, and Swift (Swift's prebuilds were originally copied from upstream; they're now GitNexus-cross-built too). node-gyp-build selects the right .node at require time, so no C/C++ toolchain is needed. If no prebuild matches your platform-arch, only Kotlin (.kt/.kts) parsing is unavailable; the rest of gitnexus is unaffected.

MCP Setup

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 + PostToolUse) 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 enrich searches with graph context + PostToolUse hooks that detect a stale index after commits and prompt the agent to reindex.

¹ Antigravity hooks follow the Gemini CLI hooks reference (Antigravity 2.0 is the documented successor to Gemini CLI). Augmentation runs in AfterTool because BeforeTool has no context-injection channel in the Gemini contract — the agent sees graph context appended to the tool result via hookSpecificOutput.additionalContext. Stale-index hints land in the same channel after a successful git commit/merge/rebase/cherry-pick/pull. The schema may evolve if Antigravity-specific hook docs diverge from Gemini CLI's; the implementation will track those changes.

Community Integrations

Built by the community — not officially maintained, but worth checking out.

Project Author Description
pi-gitnexus @tintinweb GitNexus plugin for pi — pi install npm:pi-gitnexus
gitnexus-stable-ops @ShunsukeHayashi Stable ops & deployment workflows (Miyabi ecosystem)

Have a project built on GitNexus? Open a PR to add it here!

If you prefer manual configuration:

Recommended for fastest startup: install gitnexus globally (npm i -g gitnexus) and run gitnexus setup — this writes an absolute-path MCP config that bypasses npx entirely. The pinned-npx snippets below are a quickstart fallback; on a cold cache the npx install can exceed Claude Code's MCP_TIMEOUT default (~30s).

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 (~/.cursor/mcp.json — global, works for all projects):

{
  "mcpServers": {
    "gitnexus": {
      "command": "npx",
      "args": ["-y", "gitnexus@latest", "mcp"]
    }
  }
}

Antigravity (Google) — ~/.gemini/antigravity/mcp_config.json:

{
  "mcpServers": {
    "gitnexus": {
      "command": "npx",
      "args": ["-y", "gitnexus@latest", "mcp"]
    }
  }
}

gitnexus setup also merges an AfterTool entry into ~/.gemini/settings.json (under the canonical Gemini CLI hooks schema) and installs skills to ~/.gemini/antigravity/skills/. Existing user hooks are preserved. The hook adapter's path is rewritten at install time, so run gitnexus setup rather than hand-editing.

OpenCode (~/.config/opencode/config.json):

{
  "mcp": {
    "gitnexus": {
      "type": "local",
      "command": ["gitnexus", "mcp"]
    }
  }
}

Codex (~/.codex/config.toml for system scope, or .codex/config.toml for project scope):

[mcp_servers.gitnexus]
command = "npx"
args = ["-y", "gitnexus@latest", "mcp"]

CLI Commands

gitnexus setup                   # Configure MCP for your editors (one-time)
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 --skills        # Generate repo-specific skill files from detected communities
gitnexus analyze --skip-embeddings  # Skip embedding generation (faster)
gitnexus analyze --skip-agents-md  # Preserve custom AGENTS.md/CLAUDE.md gitnexus section edits
gitnexus analyze --skip-skills     # Skip installing .claude/skills/gitnexus/ skill files
gitnexus analyze --default-branch develop  # Branch used in the generated regression-compare example (base_ref)
gitnexus analyze --skip-git        # Index folders that are not Git repositories
gitnexus analyze --embeddings [limit]  # Enable embedding generation (slower, better search)
gitnexus analyze --verbose       # Log skipped files when parsers are unavailable
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 analyze --workers <n>        # Parse worker pool size (>=1; default: cores-1, capped at 16, auto-sized to the repo). 0 is rejected — there is no sequential mode.
gitnexus mcp                     # Start MCP server (stdio) — serves all indexed repos
gitnexus serve                   # Start local HTTP server (multi-repo) for web UI connection
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 repository wiki from knowledge graph
gitnexus wiki --model <model>    # Wiki with custom LLM model (default: gpt-4o-mini)
gitnexus wiki --base-url <url>   # Wiki with custom LLM API base URL
gitnexus publish                 # Notify the understand-quickly registry (opt-in, see below)

# 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 uninstall reverses gitnexus 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 --force to apply. Per-repo indexes (gitnexus clean --all) and the global npm package (npm uninstall -g gitnexus) are left for you to remove.

If analyze reports a worker parse timeout on a large or unusual repository, it keeps running and falls back safely. To give slow worker jobs more time, use gitnexus analyze --worker-timeout 60 or set GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=60000. For very large files, GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES controls the worker job byte budget.

Embeddings node limit

gitnexus analyze --embeddings generates semantic search vectors with a default 50,000-node safety cap to protect memory on large repositories. Override the cap when you know the host has enough memory for a larger graph, or disable it entirely for a one-off full embeddings run.

# Generate embeddings with the default 50,000 node safety cap
gitnexus analyze --embeddings

# Disable the safety cap entirely
gitnexus analyze --embeddings 0

# Use a custom cap
gitnexus analyze --embeddings 100000

If embeddings are skipped on a large repository, the indexed graph likely exceeds the default safety cap. Re-run with gitnexus analyze --embeddings 0 to remove the cap, or gitnexus analyze --embeddings <n> to choose a higher limit while still keeping memory bounded.

Project config (.gitnexusrc)

Commit a .gitnexusrc JSON file at the repo root to preconfigure recurring analyze options per project, instead of re-passing the same flags every run. It is read from the resolved repo root (not .gitnexus/, which is gitignored index storage). CLI flags always override .gitnexusrc.

{
  // Default branch used in the generated regression-compare example (base_ref).
  // Use this so a project on `develop`/`master` doesn't get "main" rewritten
  // over its fix on every analyze. (Alias: "branch".)
  "defaultBranch": "develop",
  "skipContextFiles": true, // alias of skipAgentsMd: keep your own AGENTS.md/CLAUDE.md
  "skipSkills": true, // don't install .claude/skills/gitnexus/
  "embeddings": true, // generate embeddings by default
  "workerTimeout": 60
}

A nested analyze block is also accepted (and overrides flat keys for the same option):

{ "analyze": { "defaultBranch": "develop", "skipSkills": true } }

Notes:

  • The default branch is resolved as: --default-branch > .gitnexusrc defaultBranch/branch > auto-detected origin/HEAD > main.
  • skipContextFiles / skipAiContext are aliases for skipAgentsMd — they skip the AGENTS.md / CLAUDE.md block only. They do not imply skipSkills. indexOnly is the stronger option that skips all file injection.
  • Supported keys: defaultBranch (branch), skipAgentsMd (skipContextFiles, skipAiContext), skipSkills, indexOnly, stats/noStats, embeddings, dropEmbeddings, name, allowDuplicateName, maxFileSize, workerTimeout, walCheckpointThreshold, workers, embeddingThreads, embeddingBatchSize, embeddingSubBatchSize, embeddingDevice.
  • The file is JSON only. Unknown keys and invalid values fail fast with an actionable error before analysis starts.

Environment variables

Most analyze knobs are also CLI flags (--workers, --worker-timeout, --max-file-size, --verbose). Use the env-var form when you'd otherwise repeat the same flag every run, or when invoking GitNexus from a long-running host (MCP server, eval-server, CI shell) that already manages its own environment. CLI flags take precedence over env vars; env vars take precedence over built-in defaults.

Variable Default Effect Tune when…
GITNEXUS_WORKER_POOL_SIZE cores - 1, capped at 16 Parse worker pool size (must be ≥ 1). Equivalent to --workers <n>. The worker pool is the sole parse path — there is no sequential parser, so 0 is rejected with an actionable error (the pool self-heals via quarantine + respawn). Constrained containers (cgroup CPU limits) or CI runners with explicit quotas. To narrow down a worker crash set 1 for a single-worker pool — not 0.
GITNEXUS_PARSE_CHUNK_CONCURRENCY 2 Number of chunks whose file contents may be read into memory in parallel while the pool dispatches the current chunk. Worker dispatch itself stays serial. Repos large enough to chunk (multi-MB total source) where disk I/O is a measurable fraction of analyze wall-clock.
GITNEXUS_VERBOSE unset When 1, enables verbose ingestion logs (skipped-file warnings, per-chunk throughput, parse-cache stats). Equivalent to --verbose. Debugging an analyze that "completed" but seems to have missed files; tuning --workers / chunk concurrency against observable throughput.
GITNEXUS_PROFILE_DEFERRED unset When 1, emits [deferred-profile] timing/progress logs for the post-chunk deferred resolution band (imports → heritage → buildHeritageMap → legacy call resolution). Implied by GITNEXUS_VERBOSE. Diagnosing analyze stalls in "Resolving calls (all chunks)" on large Java/Kotlin repos (issue #1741) without the full verbose ingestion noise.
GITNEXUS_PROFILE_DEFERRED_SLOW_MS 3000 (verbose) / 5000 Per-file threshold in ms above which processCallsFromExtracted emits a slow file … log line. Parsed via Number(): accepts integers (5000), scientific notation (2.5e3), decimals (.5), and hex (0x10). Non-finite or non-positive values fall back to the default. Hunting a few outlier files dominating the deferred call-resolution stage; lower to surface more, raise to focus only on the worst.
GITNEXUS_MAX_FILE_SIZE 512 (KB) Walker skip threshold in KB. Hard cap is 32768 (tree-sitter buffer ceiling). Equivalent to --max-file-size <kb>. Indexing repos with intentionally-large source files (generated parsers, vendored bundles) that should still be parsed.
GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS 30000 Worker idle timeout in milliseconds before retry/fallback. Equivalent to --worker-timeout <seconds> × 1000. Slow-parsing files (large minified JS, deeply-nested TS types) that legitimately need more than 30s.
GITNEXUS_WAL_CHECKPOINT_THRESHOLD 67108864 (64 MiB) LadybugDB WAL auto-checkpoint threshold in bytes. Equivalent to --wal-checkpoint-threshold <bytes>. -1 keeps LadybugDB's stock threshold (~16 MiB). Larger thresholds reduce checkpoint frequency but increase the WAL size at rotation time — choose a smaller value on disk-constrained environments. You need a larger or smaller WAL auto-checkpoint threshold for your analyze workload.
GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES 8388608 (8 MB) Per-job byte budget the pool will send to a worker in one postMessage. Very large individual files; mostly diagnostic — bumping past 8 MB risks structured-clone memory pressure.
GITNEXUS_WORKER_MAX_RESPAWNS_PER_SLOT 3 Max replacement spawns per worker slot before the slot is dropped from the active rotation. Bounds respawn loops on a chronically-crashing slot. Hosts where a flaky worker should retry more (raise) or fail-fast (lower) before the slot is dropped.
GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS 5 × subBatchTimeoutMs Total retry wall-time budget per job before quarantining. Combined with timeoutBackoffFactor, prevents exponentially-growing retries from stalling for hours. Slow files that legitimately need long total retry windows; lower to fail-fast on stalls.
GITNEXUS_WORKER_CONSECUTIVE_FAILURE_THRESHOLD max(3, poolSize) Per-slot consecutive deaths before the pool's circuit breaker trips. After tripping, every subsequent dispatch rejects until a fresh pool is created. Hosts where a SIGSEGV-prone native grammar should trip the breaker sooner; CI runners that should fail loudly.
GITNEXUS_CHUNK_BYTE_BUDGET 2097152 (2 MB) Chunk boundary used for cache-key composition and dispatch. Smaller = finer-grained cache hits but more dispatch overhead. Tuning incremental-analyze cache behavior on monorepos.
GITNEXUS_NO_GITIGNORE unset When set, skips .gitignore parsing. .gitnexusignore is still honored. Indexing a repo whose .gitignore excludes files you actually want indexed (e.g., generated code committed for cross-repo lookup).
GITNEXUS_SKIP_OPTIONAL_GRAMMARS unset When =1 strictly, skips the vendored grammar materialize for tree-sitter-dart, tree-sitter-proto, tree-sitter-swift, and tree-sitter-kotlin at install time (and the Dart/Proto source builds). Those four won't be parsed; the install still succeeds. Installing on a host without a C++ toolchain or where the vendored prebuilds don't match; willing to skip Dart/Proto/Swift/Kotlin parsing.

Publishing to understand-quickly (opt-in)

looptech-ai/understand-quickly is a public registry of code-knowledge graphs that lists gitnexus@1 as a first-class format. After registering your repo once (npx @understand-quickly/cli add or the wizard), gitnexus publish fires a single repository_dispatch event so the registry resyncs your entry on demand instead of waiting for the nightly job.

It is opt-in and a no-op without UNDERSTAND_QUICKLY_TOKEN — a fine-grained GitHub PAT with Repository dispatches: write on the registry repo. Nothing else happens; no graph file is uploaded. See the protocol spec for the full contract.

What Your AI Agent Gets

16 tools exposed via MCP (11 per-repo + 5 group):

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
group_list List configured repository groups —
group_sync Extract contracts and match across repos/services —
group_contracts Inspect extracted contracts and cross-links —
group_query Search execution flows across all repos in a group —
group_status Check staleness of repos in a group —

When only one repo is indexed, the repo parameter is optional. With multiple repos, specify which one: query({query: "auth", repo: "my-app"}).

Resources for instant context:

Resource Purpose
gitnexus://repos List all indexed repositories (read this 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

2 MCP prompts for guided workflows:

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

4 agent skills installed to .claude/skills/ automatically:

  • 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

Repo-specific skills generated with --skills:

When you run gitnexus analyze --skills, GitNexus detects the functional areas of your codebase (via Leiden community detection) and generates a SKILL.md file for each one under .claude/skills/generated/. Each skill describes a module's key files, entry points, execution flows, and cross-area connections — so your AI agent gets targeted context for the exact area of code you're working in. Skills are regenerated on each --skills run to stay current with the codebase.


Multi-Repo MCP Architecture

GitNexus uses a global registry so one MCP server can serve multiple indexed repos. No per-project MCP config needed — set it up once and it works everywhere.

flowchart TD
    subgraph CLI [CLI Commands]
        Setup["gitnexus setup"]
        Analyze["gitnexus analyze"]
        Clean["gitnexus clean"]
        List["gitnexus list"]
    end

    subgraph Registry ["~/.gitnexus/"]
        RegFile["registry.json"]
    end

    subgraph Repos [Project Repos]
        RepoA[".gitnexus/ in repo A"]
        RepoB[".gitnexus/ in repo B"]
    end

    subgraph MCP [MCP Server]
        Server["server.ts"]
        Backend["LocalBackend"]
        Pool["Connection Pool"]
        ConnA["LadybugDB conn A"]
        ConnB["LadybugDB conn B"]
    end

    Setup -->|"writes global MCP config"| CursorConfig["~/.cursor/mcp.json"]
    Analyze -->|"registers repo"| RegFile
    Analyze -->|"stores index"| RepoA
    Clean -->|"unregisters repo"| RegFile
    List -->|"reads"| RegFile
    Server -->|"reads registry"| RegFile
    Server --> Backend
    Backend --> Pool
    Pool -->|"lazy open"| ConnA
    Pool -->|"lazy open"| ConnB
    ConnA -->|"queries"| RepoA
    ConnB -->|"queries"| RepoB

How it works: Each gitnexus analyze stores the index in .gitnexus/ inside the repo (portable, gitignored) and registers a pointer in ~/.gitnexus/registry.json. When an AI agent starts, the MCP server reads the registry and can serve any indexed repo. LadybugDB connections are opened lazily on first query and evicted after 5 minutes of inactivity (max 5 concurrent). If only one repo is indexed, the repo parameter is optional on all tools — agents don't need to change anything.


Web UI (browser-based)

A client-side graph explorer and AI chat — your code never leaves your machine.

Try it now: gitnexus.vercel.app — run npx gitnexus@latest serve locally and the page auto-connects to your local backend.

gitnexus_img

Or run the frontend locally:

git clone https://github.com/abhigyanpatwari/gitnexus.git
cd gitnexus/gitnexus-shared && npm install && npm run build
cd ../gitnexus-web && npm install
npm run dev
# Then in another terminal, start the backend the frontend connects to:
npx gitnexus@latest serve

Docker

The official Docker setup ships two signed images orchestrated by docker-compose.yaml. Each image is published to both GitHub Container Registry (GHCR) and Docker Hub — same build, same digest, same Cosign signature — so pick whichever registry you prefer:

Purpose GHCR (default in docker-compose.yaml) Docker Hub mirror
CLI / gitnexus serve backend (HTTP API on port 4747, MCP, indexer) ghcr.io/abhigyanpatwari/gitnexus:latest akonlabs/gitnexus:latest
Static web UI (port 4173) ghcr.io/abhigyanpatwari/gitnexus-web:latest akonlabs/gitnexus-web:latest

Heads-up — image rename. Earlier releases published the web UI under ghcr.io/abhigyanpatwari/gitnexus. Starting with the introduction of the bundled backend, that slug now hosts the CLI/server image and the UI moved to ghcr.io/abhigyanpatwari/gitnexus-web. The previous tags remain available for pulling, but new versions are only published under the new slugs. Update your docker run / compose files accordingly (or just adopt the bundled compose).

One-command setup

docker compose up -d

This starts the server on http://localhost:4747 and the web UI on http://localhost:4173. The UI auto-detects the server because the browser runs on the host and reaches the container via the mapped port.

A named volume (gitnexus-data) persists the global registry, indexes, and cloned repos at /data/gitnexus inside the server container. To make repos on your host machine indexable, set WORKSPACE_DIR before bringing the stack up:

WORKSPACE_DIR=$HOME/code docker compose up -d
# Inside the server container the directory is mounted read-only at /workspace.
docker compose exec gitnexus-server gitnexus index /workspace/my-repo

Direct docker run

# Server
docker run --rm -d \
  --name gitnexus-server \
  -p 4747:4747 \
  -v gitnexus-data:/data/gitnexus \
  ghcr.io/abhigyanpatwari/gitnexus:latest

# Web UI
docker run --rm -d \
  --name gitnexus-web \
  -p 4173:4173 \
  ghcr.io/abhigyanpatwari/gitnexus-web:latest

Optional env file (override image tags, container names, ports, workspace dir):

cp .env.example .env
docker compose --env-file .env up -d

Versioning & supply-chain protection

The Docker images are version-locked to the npm package:

  • Stable images are only published from vX.Y.Z git tags (via docker.yml triggered directly by the tag push), and the workflow refuses to build unless the tag exactly matches gitnexus/package.json's version. So ghcr.io/abhigyanpatwari/gitnexus:1.6.2 (and its Docker Hub mirror akonlabs/gitnexus:1.6.2) is byte-for-byte the same release as npm install gitnexus@1.6.2 — no drift, no floating builds from main. Both registries receive the same digest from a single build step, so you can pull from either and the signature verifies identically.
  • Release-candidate images (e.g. :1.7.0-rc.1) are published alongside each RC npm release. They are built by publish.yml calling docker.yml as a reusable workflow after the RC tag is created and pushed.
  • :latest is auto-promoted only from non-prerelease tags by the Docker metadata action, so it always points at a real, npm-published version.

Both images are signed with Cosign keyless signing using the workflow's GitHub OIDC identity, and shipped with build provenance and SBOM attestations. This is your protection against supply-chain attacks: even if an attacker republishes a same-named image elsewhere (or somehow pushes to a typo-squatted registry), they cannot forge a Cosign signature tied to abhigyanpatwari/GitNexus's docker.yml. Always verify before pulling into sensitive environments:

Stable releases — signed from the v* tag ref:

cosign verify ghcr.io/abhigyanpatwari/gitnexus:1.6.2 \
  --certificate-identity-regexp '^https://github\.com/abhigyanpatwari/GitNexus/\.github/workflows/docker\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

# Same signature verifies the Docker Hub mirror (identical digest):
cosign verify docker.io/akonlabs/gitnexus:1.6.2 \
  --certificate-identity-regexp '^https://github\.com/abhigyanpatwari/GitNexus/\.github/workflows/docker\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

The regex pins the certificate identity to this repo's docker.yml workflow run from a v* tag — rejecting unsigned images, images signed by other workflows, and images signed from unprotected refs. It is identical for both registries because both sets of tags were signed at the same digest in one workflow run.

Release candidates — signed from refs/heads/main (the caller's ref when publish.yml invokes docker.yml as a reusable workflow):

cosign verify ghcr.io/abhigyanpatwari/gitnexus:1.7.0-rc.1 \
  --certificate-identity 'https://github.com/abhigyanpatwari/GitNexus/.github/workflows/docker.yml@refs/heads/main' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

You can also inspect the build provenance and SBOM:

cosign download attestation ghcr.io/abhigyanpatwari/gitnexus:1.6.2 \
  --predicate-type https://slsa.dev/provenance/v1

Kubernetes: enforce signatures at admission

For Kubernetes deployments, ship the bundled ClusterImagePolicy so the Sigstore policy-controller rejects any GitNexus pod whose image is not signed by this repo's docker.yml running from a vX.Y.Z tag — the same identity the cosign verify snippet above pins.

# 1. Install the controller (one-time, cluster-wide)
helm repo add sigstore https://sigstore.github.io/helm-charts && helm repo update
helm install policy-controller -n cosign-system --create-namespace \
  sigstore/policy-controller

# 2. Opt your namespace in
kubectl label namespace <your-ns> policy.sigstore.dev/include=true

# 3. Apply the policy
kubectl apply -f deploy/kubernetes/cluster-image-policy.yaml

After this, attempting to deploy an unsigned image — or one signed by anything other than abhigyanpatwari/GitNexus's docker.yml at a v* tag — fails the admission webhook before a pod is ever created. This turns the verifiable signature into an enforced policy, which is the supply-chain control most clusters actually need.

Files

  • Dockerfile.web — builds gitnexus-shared and gitnexus-web, then serves the production frontend.
  • Dockerfile.cli — builds the CLI/server (with its native deps) and runs gitnexus serve --host 0.0.0.0.
  • docker-compose.yaml — starts both signed images side by side.
  • .env.example — overrides for image names, container names, ports, and the workspace mount.

The web UI uses the same indexing pipeline as the CLI but runs entirely in WebAssembly (Tree-sitter WASM, LadybugDB WASM, in-browser embeddings). It's great for quick exploration but limited by browser memory for larger repos.

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.


The Problem GitNexus Solves

Tools like Cursor, Claude Code, Codex, Cline, Roo Code, and Windsurf are powerful — but they don't truly know your codebase structure.

What happens:

  1. AI edits UserService.validate()
  2. Doesn't know 47 functions depend on its return type
  3. Breaking changes ship

Traditional Graph RAG vs GitNexus

Traditional approaches give the LLM raw graph edges and hope it explores enough. GitNexus precomputes structure at index time — clustering, tracing, scoring — so tools return complete context in one call:

flowchart TB
    subgraph Traditional["Traditional Graph RAG"]
        direction TB
        U1["User: What depends on UserService?"]
        U1 --> LLM1["LLM receives raw graph"]
        LLM1 --> Q1["Query 1: Find callers"]
        Q1 --> Q2["Query 2: What files?"]
        Q2 --> Q3["Query 3: Filter tests?"]
        Q3 --> Q4["Query 4: High-risk?"]
        Q4 --> OUT1["Answer after 4+ queries"]
    end

    subgraph GN["GitNexus Smart Tools"]
        direction TB
        U2["User: What depends on UserService?"]
        U2 --> TOOL["impact UserService upstream"]
        TOOL --> PRECOMP["Pre-structured response:
        8 callers, 3 clusters, all 90%+ confidence"]
        PRECOMP --> OUT2["Complete answer, 1 query"]
    end

Core innovation: Precomputed Relational Intelligence

  • Reliability — LLM can't miss context, it's already in the tool response
  • Token efficiency — No 10-query chains to understand one function
  • Model democratization — Smaller LLMs work because tools do the heavy lifting

How It Works

GitNexus builds a complete knowledge graph of your codebase through a multi-phase indexing pipeline:

  1. Structure — Walks the file tree and maps folder/file relationships
  2. Parsing — Extracts functions, classes, methods, and interfaces using Tree-sitter ASTs
  3. Resolution — Resolves imports, function calls, heritage, constructor inference, and self/this receiver types across files with language-aware logic
  4. Clustering — Groups related symbols into functional communities
  5. Processes — Traces execution flows from entry points through call chains
  6. Search — Builds hybrid search indexes for fast retrieval

Supported Languages

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++ — — ✓ ✓ ✓ ✓ — ✓ ✓
Dart ✓ — ✓ ✓ ✓ ✓ — ✓ ✓

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


Tool Examples

Impact Analysis

impact({target: "UserService", direction: "upstream", minConfidence: 0.8})

TARGET: Class UserService (src/services/user.ts)

UPSTREAM (what depends on this):
  Depth 1 (WILL BREAK):
    handleLogin [CALLS 90%] -> src/api/auth.ts:45
    handleRegister [CALLS 90%] -> src/api/auth.ts:78
    UserController [CALLS 85%] -> src/controllers/user.ts:12
  Depth 2 (LIKELY AFFECTED):
    authRouter [IMPORTS] -> src/routes/auth.ts

Options: maxDepth, minConfidence, relationTypes (CALLS, IMPORTS, EXTENDS, IMPLEMENTS), includeTests, limit (max symbols per depth, default 100), offset (pagination start per depth), summaryOnly (counts and risk only, omits symbol list)

Disambiguation — when several symbols share the target name, impact returns a ranked ambiguous candidate list instead of guessing. Narrow it with target_uid (exact, zero-ambiguity), file_path, or kind (Function, Class, Method, …). From the CLI these are --uid, --file, and --kind, matching gitnexus context:

gitnexus impact get_embeddings                       # → ambiguous: lists ranked candidates
gitnexus impact get_embeddings --file src/embed.py   # → resolves to the one in that file
gitnexus impact get_embeddings --uid "Function:src/embed.py:get_embeddings"  # exact
query({query: "authentication middleware"})

processes:
  - summary: "LoginFlow"
    priority: 0.042
    symbol_count: 4
    process_type: cross_community
    step_count: 7

process_symbols:
  - name: validateUser
    type: Function
    filePath: src/auth/validate.ts
    process_id: proc_login
    step_index: 2

definitions:
  - name: AuthConfig
    type: Interface
    filePath: src/types/auth.ts

Context (360-degree Symbol View)

context({name: "validateUser"})

symbol:
  uid: "Function:validateUser"
  kind: Function
  filePath: src/auth/validate.ts
  startLine: 15

incoming:
  calls: [handleLogin, handleRegister, UserController]
  imports: [authRouter]

outgoing:
  calls: [checkPassword, createSession]

processes:
  - name: LoginFlow (step 2/7)
  - name: RegistrationFlow (step 3/5)

Detect Changes (Pre-Commit)

detect_changes({scope: "all"})

summary:
  changed_count: 12
  affected_count: 3
  changed_files: 4
  risk_level: medium

changed_symbols: [validateUser, AuthService, ...]
affected_processes: [LoginFlow, RegistrationFlow, ...]

Rename (Multi-File)

rename({symbol_name: "validateUser", new_name: "verifyUser", dry_run: true})

status: success
files_affected: 5
total_edits: 8
graph_edits: 6     (high confidence)
text_search_edits: 2  (review carefully)
changes: [...]

Cypher Queries

-- Find what calls auth functions with high confidence
MATCH (c:Community {heuristicLabel: 'Authentication'})<-[:CodeRelation {type: 'MEMBER_OF'}]-(fn)
MATCH (caller)-[r:CodeRelation {type: 'CALLS'}]->(fn)
WHERE r.confidence > 0.8
RETURN caller.name, fn.name, r.confidence
ORDER BY r.confidence DESC

Wiki Generation

Generate LLM-powered documentation from your knowledge graph:

# Requires an LLM API key (OPENAI_API_KEY, etc.)
gitnexus wiki

# Use a custom model or provider
gitnexus wiki --model gpt-4o
gitnexus wiki --base-url https://api.anthropic.com/v1

# Force full regeneration
gitnexus wiki --force


# Increase the timeout or retries for large codebase or slow LLM providers
gitnexus wiki --timeout <seconds> # LLM request timeout in seconds (default: disabled)
gitnexus wiki --retries <n>      # Max LLM retry attempts per request (default: 3)

# Change the language generation for wiki
gitnexus wiki --lang <lang>  # Output language for generated documentation (e.g. english, chinese, spanish, japanese)

The wiki generator reads the indexed graph structure, groups files into modules via LLM, generates per-module documentation pages, and creates an overview page — all with cross-references to the knowledge graph.


Tech Stack

Layer CLI Web
Runtime Node.js (native) Browser (WASM)
Parsing Tree-sitter native bindings Tree-sitter WASM
Database LadybugDB native LadybugDB WASM
Embeddings HuggingFace transformers.js (GPU/CPU) transformers.js (WebGPU/WASM)
Search BM25 + semantic + RRF BM25 + semantic + RRF
Agent Interface MCP (stdio) LangChain ReAct agent
Visualization — Sigma.js + Graphology (WebGL)
Frontend — React 18, TypeScript, Vite, Tailwind v4
Clustering Graphology Graphology
Concurrency Worker threads + async Web Workers + Comlink

Roadmap

Actively Building

  • LLM Cluster Enrichment — Semantic cluster names via LLM API
  • AST Decorator Detection — Parse @Controller, @Get, etc.
  • Incremental Indexing — Only re-index changed files

Recently Completed

  • Constructor-Inferred Type Resolution, self/this Receiver Mapping
  • Wiki Generation, Multi-File Rename, Git-Diff Impact Analysis
  • Process-Grouped Search, 360-Degree Context, Claude Code Hooks
  • Multi-Repo MCP, Zero-Config Setup, 14 Language Support
  • Community Detection, Process Detection, Confidence Scoring
  • Hybrid Search, Vector Index

Security & Privacy

  • CLI: Everything runs locally on your machine. No network calls. Index stored in .gitnexus/ (gitignored). Global registry at ~/.gitnexus/ stores only paths and metadata.
  • Web: Everything runs in your browser. No code uploaded to any server. API keys stored in localStorage only.
  • Open source — audit the code yourself.

Acknowledgments