- Add 3 new test fixtures: swift-if-let-guard-let, swift-await-try, swift-for-loop-inference
- Add integration tests for if let/guard let binding resolution (4 assertions)
- Add integration tests for await/try expression unwrapping (3 assertions)
- Add for-loop-inference fixture (documented as known gap — type-env infrastructure
is in place but call-processor re-parse path doesn't propagate the binding yet)
- Fix cross-chunk Swift implicit imports: standard processImports path now passes
allFileList instead of chunk-only files to addSwiftImplicitImports, matching
the fast-path behavior
- Add Swift type_annotation fallback in type-env declarationTypeNodes population
(handles [User] array sugar where childForFieldName('type') returns null)
- Handle Swift 'pattern' node in extractVarName fallback (pattern wraps simple_identifier)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npm ci was failing with "Missing: hono@4.12.8" and
"Missing: graphology-types@0.24.8" because the lock file was
out of sync after rebase. Regenerated from clean state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pin exact versions (no ^) to prevent surprise upgrades:
- tree-sitter: "0.22.4" (was "^0.22.4")
- tree-sitter-swift: "0.7.1" (was "^0.7.1")
Add npm overrides to suppress peer dependency warnings from grammar
packages that declare ^0.21.x but work fine with 0.22.4.
Note: tree-sitter-swift 0.6.0 fails to build on current Node (needs
node-gyp + Swift toolchain). 0.7.1 with prebuilt binaries is required
for Swift support to work at all.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Export detection: exclude private(set)/fileprivate(set) from
unexported check. Only the setter is restricted — the symbol
itself is still readable cross-file.
2. For-loop binding: use extractVarName() instead of raw .text
to avoid polluting scopeEnv with non-identifier keys from
tuple destructuring patterns (e.g. `for (a, b) in ...`).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers all high and medium impact gaps from the Swift feature coverage
analysis:
1. if let / guard let bindings: add if_statement and guard_statement
to DECLARATION_NODE_TYPES, extract varName and value for Tier 2
return-type propagation (callResult, copy, fieldAccess, methodCallResult)
2. await / try expression unwrapping: add unwrapSwiftExpression() that
strips await_expression and try_expression wrappers before checking
for call_expression. Applied in extractPendingAssignment,
extractInitializer, and scanConstructorBinding.
3. for item in collection: add extractForLoopBinding for Swift with
extractSwiftElementTypeFromTypeNode that handles [User] array sugar
and Array<User> generic types. Registered in typeConfig.
4. Multiple inheritance specifiers: already working — tree-sitter
queries match all inheritance_specifier occurrences automatically.
Verified, no code changes needed.
5. Enum case extraction: add (enum_entry (simple_identifier) @name)
@definition.property query to SWIFT_QUERIES.
6. self/super resolution: unskipped both describe.skip test suites
(tree-sitter-swift 0.7.1 ships prebuilds, Node 22 build issue
resolved). Both pass — 5 previously-skipped tests now running.
7. Optional chaining obj?.method(): already working — tree-sitter-swift
parses the ? transparently. Verified, no code changes needed.
Tests: 3,603 → 3,608 (5 unskipped self/super tests)
Swift tests: 23 → 28 passing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses reviewer feedback: the new Swift behaviors (implicit imports,
constructor fallback, extension dedup, export detection) had no dedicated
integration tests. Adds 4 fixture directories and 11 new test assertions:
1. swift-implicit-imports: two files, no explicit import, cross-file
constructor + member call resolves via addSwiftImplicitImports
2. swift-extension-dedup: extension creates duplicate Class node,
constructor still resolves to primary definition
3. swift-constructor-fallback: ClassName() without `new` resolves as
constructor via free→constructor retry
4. swift-export-visibility: internal symbols visible cross-file,
public/open visible, private/fileprivate noted as Tier 3 limitation
All 3,603 tests pass (11 new, 0 regressions).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Swift was missing the extractPendingAssignment extractor, which meant
return-type-based variable bindings like `let user = getUser()` couldn't
propagate the return type of `getUser()` to `user`. This broke member
call resolution: `user.save()` couldn't resolve to `User.save()` when
there were competing methods (both User and Repo have save()).
Handles four Swift patterns:
- let user = getUser() → callResult (Tier 2 propagation)
- let result = user.save() → methodCallResult
- let name = user.name → fieldAccess
- let copy = user → copy
All 3,592 tests pass — including the 2 previously-failing Swift
return-type inference tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix assignment query: tree-sitter-swift 0.7.1 uses named fields
(target:/result:/suffix:) instead of positional children
- Update export detection tests: Swift `internal` (default) is now
correctly treated as exported (module-scoped visibility)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When Swift extensions create multiple Class nodes with the same name
(e.g. Product.swift + ProductMatchableConformance.swift), the call
resolver gets multiple candidates and refuses to emit a CALLS edge.
Add dedup: when all candidates share the same type (Class/Struct) and
differ only by file, prefer the primary definition (shortest filepath).
Note: This fix is partial — some constructor calls inside function
bodies may still be consumed by the type-env constructor binding
scanner before reaching resolveCallTarget. Filed as known limitation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three changes that together enable cross-file call resolution for Swift:
1. export-detection.ts: Treat internal (default) Swift symbols as exported.
Swift's default access level is `internal` (module-scoped, visible to
all files in the same target). Only private/fileprivate are file-scoped.
Previously all non-public/open symbols were marked unexported.
2. import-processor.ts: Add implicit import edges between all Swift files
in the same module/target. Swift has no file-level imports — all files
see each other automatically. Without these edges, the tiered resolver
can't find cross-file symbols at Tier 2a (import-scoped).
Supports SPM targets via Package.swift; falls back to single-module
for Xcode projects without SPM.
3. call-processor.ts: Add constructor fallback for free-form calls.
Swift constructors look like free function calls (no `new` keyword):
`let ocr = OCRService()`. The call form is inferred as `free`, which
filters out Class/Struct targets. Now retries with `constructor` form
when free-form finds no callable but the name resolves to a type.
Tested on 61-file iOS 26 project (PricePal):
- Before: 0 cross-file CALLS edges
- After: full cross-file resolution (OCRService traced from ScanViewModel)
- 3,099 nodes, 10,449 edges, 246 clusters, 243 flows
Related: #406, #407
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The patch script fails to parse tree-sitter-swift@0.6.0's binding.gyp
because the file contains both Python-style # comments AND trailing
commas in JSON arrays. The existing regex strips # comments but leaves
trailing commas, causing JSON.parse() to fail with:
"Unexpected token ']'"
This silently prevents tree-sitter-swift from building, which means
Swift files are skipped entirely during analysis.
Fix: add a second regex pass to strip trailing commas before ] or }
after comment removal.
Fixes#386, #406
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Allow RFC 1918 private network ranges in the CORS origin allowlist so
users running GitNexus on their home or office LAN can access the web UI
from another device on the same network.
Permitted private ranges:
10.0.0.0/8 (10.x.x.x)
172.16.0.0/12 (172.16.x.x – 172.31.x.x)
192.168.0.0/16 (192.168.x.x)
The origin check is extracted into an exported isAllowedOrigin() helper
so it can be unit-tested in isolation. A new test file covers:
- No origin (curl / server-to-server)
- localhost and 127.0.0.1 variants
- All three RFC 1918 ranges including boundary values
- The deployed gitnexus.vercel.app site
- Public / untrusted origins that must be rejected
The server bind address (127.0.0.1 by default) is unchanged; this PR
only affects which cross-origin browser requests are accepted.
Closes#390
Instead of hardcoding confidence: 1.0, compute it at ingestion time using
the same resolution tier system that CALLS edges already use.
Heritage edges (EXTENDS, IMPLEMENTS):
- resolveHeritageId now returns { id, confidence } using TIER_CONFIDENCE
- Same-file → 0.95, import-scoped → 0.9, global → 0.5
- Edge confidence = geometric mean of source and target confidence
(principled for partially-correlated cross-scope estimates, per
Dillig et al. POPL 2011 and Dempster-Shafer theory)
MRO edges (OVERRIDES):
- MRO-ordered → 0.9, class method wins → 0.95
- Single interface → 0.85, ambiguous/unresolved → 0.5
IMPORTS and CONTAINS intentionally keep 1.0 (deterministic).
Closes#412
When LadybugDB throws a BUSY/lock error (e.g. CLI and server running
concurrently), withLbugDb retries up to 3 times with linear backoff.
Addresses review feedback:
1. **Race condition fix**: Connection cleanup (close + state reset) now
runs inside runWithSessionLock, preventing another operation from
acquiring the lock between cleanup steps and having its connection
closed from under it.
2. **Tests call withLbugDb directly**: Replaced simulateWithRetry helper
with tests that invoke the real withLbugDb implementation, catching
regressions in retry count, backoff, and lock interaction.
Closes#325
Addresses all review items from @magyargergo and Copilot:
1. **Rename --no-git to --skip-git**: Commander.js treats --no-X flags
as negation of --X (stores as options.git = false, not options.noGit).
--skip-git maps correctly to options.skipGit.
2. **Fix false " Already up to date\ on non-git folders**: When
currentCommit is empty string, skip the cache check — we cannot
detect changes without git, so always rebuild.
3. **Replace isGitRepo() with hasGitDir()**: Use filesystem check
(statSync on .git) instead of shelling out to git CLI. Consistent,
faster, and works when git is not installed.
4. **Fix misleading warning**: Message now only fires when .git
directory is actually absent (not when git CLI fails).
5. **Add CLI integration tests**: Verify Commander maps --skip-git
correctly and that non-git folders are rejected without the flag.
- Replace require(" fs\) with ESM-compatible top-level import (statSync)
- Register --no-git option in Commander CLI definition
- Use hasGitDir() instead of isGitRepo() for .gitignore update guard
to match the PR intent (filesystem check vs git CLI invocation)
The cypher tool description and schema resource omit Community and Process
node properties, causing agents to write failing queries on first attempt.
Added property listings sourced from the actual LadybugDB schema definitions:
- Community: heuristicLabel, cohesion, symbolCount, keywords, description, enrichedBy
- Process: heuristicLabel, processType, stepCount, communities, entryPointId, terminalId
Closes#411
Previously gitnexus analyze exited with an error on any directory that
lacked a .git entry, making it impossible to index generated code,
vendored libraries, or monorepo sub-trees that are not git roots.
Changes:
storage/git.ts
- Add hasGitDir(dirPath): boolean — a lightweight synchronous check for
the presence of a .git file or directory. Works for git worktrees
(.git file pointing at the real repo) as well as standard repos.
cli/analyze.ts
- Add noGit?: boolean to AnalyzeOptions.
- When the explicit inputPath resolves to a non-git folder (or the cwd
is not inside any git repo), respect --no-git instead of hard-failing.
- Print an actionable tip pointing at --no-git when git is absent and the
flag was not supplied.
- currentCommit defaults to an empty string for non-git folders so the
up-to-date check still functions (empty string never matches a real
commit hash, so the index is always rebuilt).
- Skip addToGitignore() when no .git is present — there is nothing to
update and the function would create a stale .gitignore at the root.
Git-dependent features that remain disabled for non-git folders:
- Incremental update (always rebuilds from scratch)
- Commit tracking in metadata
- .gitignore update
Closes#384
ORT 1.24.x downloads CUDA provider .so from NuGet at postinstall,
but only for linux/x64. The process.arch guard correctly returns
false on arm64 (safe CPU fallback), but the prior comment implied
arm64 CUDA was supported. Clarify the actual state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address PR #300 review findings:
[CRITICAL] hasOrtCudaProvider() was checking the top-level
onnxruntime-node@1.24.3 but @huggingface/transformers loads its own
nested onnxruntime-node@1.21.0 at runtime. The guard inspected the
wrong binary, so the native crash was not prevented.
Fix: resolve onnxruntime-node from transformers' own module scope
(createRequire from transformers' package.json) so the guard always
checks the same binary that will be dlopen'd at runtime.
Also:
- Add npm overrides to force @huggingface/transformers to use our
onnxruntime-node@^1.24.0 (works for global installs where gitnexus
is the root package; npx installs get safety from the resolve fix)
- Replace hardcoded 'x64' with process.arch for arm64 support
- Remove dead napi-v3 path check (ORT 1.21.0 never shipped CUDA .so)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Enhance C++ tree-sitter queries to support inline class method declarations and return types.
- Introduce `importedRawReturnTypes` in `BuildTypeEnvOptions` for cross-file raw return type handling.
- Add `FileTypeEnvBindings` interface to capture file-scope type bindings for exported symbols.
- Implement logic in `parse-worker.ts` to extract and serialize file-scope type bindings for cross-file type resolution.
- Create test fixtures for C++, Go, Ruby, and Rust to validate cross-file binding propagation.
- Update integration tests to verify correct resolution of method calls across files for C++, Go, Ruby, and Rust.
- Document Phase 14: Cross-File Binding Propagation in the type resolution roadmap and system documentation.
Fix server/bridge mode leaving the web UI with 0 nodes and broken
Query/Processes/embeddings by hydrating the worker-side LadybugDB
and BM25 indexes after loading graph data from the backend.
Also fix LadybugDB QueryResult API mismatch where result.getAll()
does not exist in some @ladybugdb/wasm-core versions — falls back
to getAllObjects() or getAllRows().
* feat: add markdown file indexing (headings + cross-links)
Parse .md/.mdx files using regex (no tree-sitter dependency) to extract:
- Section nodes from headings (h1-h6) with hierarchy via CONTAINS edges
- Cross-file IMPORTS edges from markdown links to other repo files
Ported from #286 to resolve conflicts with kuzu→lbug rename.
Co-Authored-By: Dennis Palatov <dp-web4@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add Section to NODE_TABLES and NODE_SCHEMA_QUERIES
The Section schema was defined but not registered in NODE_TABLES or
NODE_SCHEMA_QUERIES, so the table was never created in the database.
Also adds missing FROM File TO Section relation entry.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update schema test counts for Section node type
NODE_TABLES: 27→28, NODE_SCHEMA_QUERIES: 27→28, SCHEMA_QUERIES: 29→30
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add diagnostic output to skills-e2e idempotency test
Show stdout/stderr in assertion message so CI failures reveal
why the second analyze --skills run exits with code 1.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add Section COPY query with level column in lbug-adapter
Section table has 8 columns (includes level) but getCopyQuery fell
through to the default 7-column multi-language path. Adds explicit
Section cases to getCopyQuery and insertNodeToLbug/upsertNodeToLbug.
Error was: COPY failed for Section: Number of columns mismatch. Expected 7 but got 8.
---------
Co-authored-by: Dennis Palatov <dp-web4@users.noreply.github.com>