* fix(impact): return structured error + partial results instead of crashing (#321)
- Wrap impact() in try-catch to return structured error JSON instead of
process crash (SIGSEGV/exit 139)
- Extract core logic to _impactImpl() for clean error boundary
- Break out of depth traversal loop on query failure, return partial
results collected so far (previously silently swallowed errors)
- Add 'partial' flag to response when traversal was interrupted
- Add try-catch in CLI impactCommand with structured error output
- Improve formatImpactResult to show suggestion text and partial warning
- Add 3 new unit tests for error/suggestion/partial scenarios
Fixes#321
* fix: address review feedback — 4 bugs from @claude review
Per @claude's review (requested by @magyargergo):
- [BUG 1] Consistent target field shape: error responses now return
{name: string} instead of raw string, matching success response schema
- [BUG 2] Remove misleading partial:true from total-failure responses
(partial is only meaningful when some depth levels succeeded)
- [BUG 3] Move getBackend() inside try-catch in impactCommand so
backend init failures return structured JSON instead of crashing
- [BUG 4] Safe error message extraction: use instanceof Error check
to handle thrown strings correctly (err?.message is undefined for
non-Error thrown values)
- [MINOR] Add radix argument to parseInt (10)
* test: add integration tests for impact error handling (#321)
Per @claude's recommendation (requested by @magyargergo):
- impact: structured error for unknown symbol (no crash)
- impact: error response has consistent {name: string} target shape
- impact: partial:true only set when some results were collected
Tests use existing withTestLbugDB + seeded graph fixture.
* feat(ingestion): respect .gitignore and .gitnexusignore during file discovery
Add support for excluding files from indexing based on .gitignore and
.gitnexusignore patterns. Previously, GitNexus used only a hardcoded
ignore list, causing significant index pollution in repositories with
git-ignored directories containing code (e.g., Docker-mounted volumes).
Changes:
- Add `ignore` package for gitignore-spec pattern matching
- Add `loadIgnoreRules()` to parse .gitignore + .gitnexusignore
- Add `createIgnoreFilter()` returning glob-compatible IgnoreLike object
- Integrate filter into glob's `ignore` option for directory-level pruning
- Remove post-glob `.filter()` call (now handled during traversal)
The hardcoded DEFAULT_IGNORE_LIST remains as fallback for non-git repos.
Closes#228
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ingestion): address review feedback on ignore filtering
- Distinguish ENOENT vs EACCES in loadIgnoreRules (warn on permission errors)
- Add GITNEXUS_NO_GITIGNORE env var to bypass .gitignore parsing
- Fix bare-name pattern matching in childrenIgnored (check both with/without trailing slash)
- Rename isIgnoredDirectory to isHardcodedIgnoredDirectory for clarity
- Add clarifying comments for design decisions (D2 negation, D3 dot:false redundancy)
- Add tests for bare-name patterns, file-glob patterns, EACCES handling, env var
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ingestion): address second round of review feedback
- G1: Document GITNEXUS_NO_GITIGNORE in `analyze --help` and log when active
- G2: Add comment clarifying path-scurry POSIX normalization contract
- G3: Add IgnoreOptions interface — env var now falls back, callers can
pass `noGitignore` explicitly for testability and future CLI flag
- G4: Add integration test verifying walkRepositoryPaths respects the env var
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(ingestion): gracefully skip files with unavailable tree-sitter grammars
Port unsupported language resilience from PR #301 by @jecanore.
- Make Kotlin import optional (like Swift) in parser-loader and parse-worker
- Add worker-local isLanguageAvailable() with filePath param for tsx distinction
- Track and log skipped files per language in both sequential and worker paths
- Add skippedLanguages to ParseWorkerResult for worker→main aggregation
- Add isLanguageAvailable unit tests
Refs: #301, #155, #228
Co-Authored-By: jecanore <juan@housingbase.io>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test(e2e): add ignore + language-skip end-to-end test with fixture repo
Add a fixture repo (test/fixtures/ignore-and-skip-repo/) with .gitignore,
.gitnexusignore, TypeScript source files, and a Swift file to exercise
all three features end-to-end:
- File discovery: verifies .gitignore excludes data/ and *.log,
.gitnexusignore excludes vendor/, source files are discovered
- Parsing: verifies TypeScript files produce Function nodes and DEFINES
relationships, Swift files are skipped gracefully when grammar is
unavailable
Add the test to the standalone group in ci-integration.yml and coverage job.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ci): move ignore-and-skip-e2e test to e2e group per review feedback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(test): use temp directory instead of fixture for e2e ignore test
The fixture's .gitignore prevented data/seed.json and debug.log from
being committed — these files would be missing after checkout in CI.
Switch to creating the entire test structure in a temp directory via
beforeAll (matching filesystem-walker.test.ts pattern). This ensures
all files exist regardless of git ignore rules.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(test): correct graph API usage in e2e ignore test
Use graph.nodes property getter instead of graph.getNodes(), and check
Function node filePath instead of non-existent File nodes (File nodes
are created by processStructure, not processParsing).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: add workflows permission to ci-integration.yml
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: change workflows permission to write per review
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: move workflows permission from ci-integration.yml to ci.yml caller
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ci): fix Claude workflows for fork PRs, remove misplaced workflows perm
Three issues prevented Claude from running on fork PRs:
1. claude-code-review.yml lacked workflows:write — push failed when
fork PRs modify .github/workflows/ files
2. claude.yml had no fork PR support — checked out main and couldn't
fetch the fork's branch from origin
3. Cleanup step unconditionally deleted branches even when push failed,
breaking the concurrent claude.yml workflow
Also removes workflows:write from ci.yml's integration job — CI tests
don't need that permission. The permission belongs on the claude
workflows that push fork branches.
Changes:
- Add workflows:write to both claude workflow permissions blocks
- Add fork PR detection + branch push/cleanup to claude.yml
- Add step id to push-fork; cleanup only runs if push succeeded
- Pass branch names via env vars to prevent shell injection (security)
- Add concurrency groups to prevent race conditions between workflows
- Remove misplaced workflows:write from ci.yml integration job
* fix(ci): use GitHub API for fork branch refs instead of git push
GITHUB_TOKEN cannot have 'workflows' permission — it's only valid for
PATs and GitHub Apps. This means git push fails whenever a fork PR
modifies .github/workflows/ files.
Replace git push with the GitHub REST API (POST/PATCH /git/refs) to
create temporary branch refs. The API creates a pointer to the
already-existing PR head commit without triggering the workflow file
push protection. Similarly, cleanup uses DELETE /git/refs instead of
git push --delete.
Also removes the invalid 'workflows: write' from permissions blocks.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: jecanore <juan@housingbase.io>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* refactor: migrate from KuzuDB to LadybugDB v0.15
KuzuDB was archived (Apple acquisition, Oct 2025). LadybugDB is the
community fork with full API compatibility.
- Package swap: kuzu → @ladybugdb/core, kuzu-wasm → @ladybugdb/wasm-core
- Rename all internal paths: kuzu → lbug (adapters, schema, storage)
- Storage path: .gitnexus/kuzu → .gitnexus/lbug (with auto-cleanup)
- Add explicit VECTOR extension loading (required in v0.15)
- Update CI workflow, documentation, and all tests
- 1151 unit + 27 integration tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review findings (P1-P3)
P1: Fix WASM adapter to use getAll() API, wire cleanupOldKuzuFiles
into analyze command, add symlink path traversal protection.
P2: Cache VECTOR extension load state, batch augmentation engine
queries (20→4), fix web getCopyQuery for multi-language tables,
fix stale KuzuDB references, correct brainstorm package names.
P3: Complete lbug-wasm.d.ts type declarations, batch semantic
search per-label, update stale BM25 comment.
* chore: remove outdated KuzuDB migration brainstorming document
* fix: load FTS extension in MCP pool adapter on init
The read-only pool adapter never loaded the FTS extension, so all
QUERY_FTS_INDEX calls failed silently. This broke search-pool and
augmentation integration tests, and caused empty results in the
web UI server mode.
* feat: implement shared Database caching and connection reference counting
* feat: enhance KuzuDB migration handling and status reporting
* fix: mock cleanupOldKuzuFiles in local backend callTool tests
* fix: update mock for cleanupOldKuzuFiles and adjust imports in callTool tests
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* calm fix 4 adding skills to repo [ISSUE #140]
* inspect
* unit and integration tests
* fixed hardcoded cohesion miss
* e2e tests for --skills flag for langauge/repo support
* Cohesion test e2e tests
Adds PostToolUse hook that detects stale GitNexus index after git mutations (commit, merge, rebase, cherry-pick, pull) and notifies the agent to reindex. Uses lightweight staleness check (git rev-parse HEAD vs meta.json) instead of running gitnexus analyze synchronously, avoiding KuzuDB corruption and 120s blocks. Security and cross-platform hardening: remove shell:true from all spawnSync calls, use .cmd extensions on Windows, add path.isAbsolute(cwd) guards, fix setup.ts path escaping with JSON.stringify, use sendHookResponse() consistently. Includes 73 regression tests.
* Fix MCP startup transport compatibility
* Preserve CLI flags in MCP startup fix
* Harden MCP transport error handling
* Harden transport security and improve type safety
Transport hardening:
- Add MAX_BUFFER_SIZE (10 MB) cap to prevent OOM from oversized
Content-Length or unbounded newline-delimited input
- Replace recursive readNewlineMessage with iterative loop to prevent
stack overflow from consecutive empty lines
- Tighten looksLikeContentLength to require 14+ bytes before matching
- Add closed-state guard and error handling to send()
- Simplify processReadBuffer loop to break on error
- Fix loose equality (==) to strict (===)
- Widen constructor param types to ReadableStream/WritableStream
Type safety:
- Constrain createLazyAction generics so export name is validated
against the module's actual exports at compile time
- Use proper type guard instead of lint suppression
- Fix test tsconfig type errors
Regression tests for all hardening fixes (13 tests passing).
---------
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
KuzuDB's native module holds open handles that prevent Node.js from
exiting cleanly. Previously only force-exited when embeddings were used
(for ONNX Runtime segfault workaround), but the same issue affects all
analyze runs. Now always calls process.exit(0) after completion.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
CLAUDE.md and AGENTS.md now contain direct enforcement instructions instead
of a passive skill router table. Based on Vercel eval data showing skills
are skipped 56% of the time, and industry research on effective AGENTS.md
patterns from 2,500+ repos.
Key changes:
- Always/When/Never three-tier boundary structure
- RFC 2119 language (MUST, NEVER) for critical rules
- Exact tool commands with parameters inline
- Self-check checklist forcing model to verify its own work
- ~77 lines, well within the <150 line adherence threshold
Skills are still installed as bonus depth for Claude Code's skill system.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Convert static imports of @huggingface/transformers (which triggers
onnxruntime-node native binary loading) to dynamic import() calls.
This prevents crashes on Node versions whose ABI isn't supported by
the prebuilt onnxruntime binaries (e.g. Node v24).
Affected entry points:
- cli/analyze.ts: embedding pipeline only loaded when --embeddings is passed
- mcp/local/local-backend.ts: embedder only loaded on first semantic search
- server/api.ts: embedder only loaded when search endpoint needs embeddings
Fixes#89
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The MCP server called process.exit(1) at startup when no repositories
were found in the registry. This prevented users from configuring the
MCP integration before running `gitnexus analyze`.
The server now starts gracefully with 0 repos and discovers newly
indexed repos lazily via refreshRepos() on each tool call.
Closes#91
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep main's barLog implementation, preserve both currentDbPath and
ftsLoaded reset in closeKuzu, take PR's new resolveRepo pattern
for /api/query. Path traversal guard confirmed intact.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Skill descriptions were too tool-centric ("using knowledge graph", "blast
radius") which prevented Claude Code from matching them to user intent.
Rewritten to user-intent-driven format with "Use when..." phrasing and
example trigger phrases so Claude can semantically match user requests.
Updated across all 3 sources: gitnexus/skills/, gitnexus-claude-plugin/skills/,
.claude/skills/, and the ai-context.ts fallback generator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address code review feedback on the server-mode PR:
Critical fixes:
- Restore CORS whitelist (localhost + gitnexus.vercel.app only)
- Bind to 127.0.0.1 by default; add --host CLI flag for opt-in remote access
- Restore path traversal guard on /api/file (resolve + startsWith check)
- Restore try/catch on all route handlers + global error middleware
- Restore SIGINT/SIGTERM graceful shutdown handlers
Bug fixes:
- Add mutex to core initKuzu to prevent race conditions on concurrent
DB switches (two requests for different repos no longer corrupt state)
- Track ftsLoaded flag and reset on DB switch / close so FTS extension
is reloaded for each new database connection
- Restore input validation on /api/query (cypher required) and
/api/search (query required)
Improvements:
- Add TTL-based cleanup for orphaned MCP sessions (30min idle eviction)
to prevent memory leaks from network drops that skip onclose
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Raise MAX_FILE_SIZE from 512KB to 2MB to capture more real source files
- Replace verbose per-warning output with single summary line
- Soften skip message wording ("likely generated/vendored")
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Skill folder names determine invocation paths in Claude Code plugins
(e.g. plugin:gitnexus:gitnexus-cli). Generic names like "cli" or
"debugging" could collide with other plugins, so prefix them all with
gitnexus- for clarity.
Updated across plugin dirs, main package source files, ai-context.ts
generator, setup.ts installer, and all CLAUDE.md/AGENTS.md routing tables.
- Impact tool now returns risk score, affected processes/modules, and summary
- Cypher tool formats results as markdown tables for LLM readability
- Context tool includes module (functional area) field
- Semantic search skips model init when embeddings are disabled
- Setup: wrap npx in cmd /c on Windows for .cmd script compatibility
- Embedder: silence stderr during ONNX model load to protect MCP stdio
- API: use executeCypher directly to avoid double formatting
- Add community integrations section to READMEs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bundle MCP server config (.mcp.json), fix hook to use spawnSync with npx
fallback and read stderr (KuzuDB stdout workaround), wire plugin.json with
hooks/mcpServers paths, add guide skill with tools/resources/schema reference,
add AMP-compatible mcp.json to all skill dirs, and slim CLAUDE.md generator
by moving reference content into the guide skill.
Replace the old local-only backend mode with a new server connection
flow that lets the web UI connect to any running GitNexus server,
download the pre-built knowledge graph, and explore it without WASM.
- Add server-connection service with streaming download and progress
- Replace DropZone backend tab with server connect UI (URL input, progress bar, cancel)
- Add repo switcher dropdown in Header when multiple repos are indexed
- Mount MCP server over StreamableHTTP at /api/mcp for remote AI tool access
- Refactor api.ts to query KuzuDB directly instead of routing through LocalBackend
- Fix KuzuDB adapter to support switching between databases (close old before opening new)
- Extract createMCPServer() from startMCPServer() for transport-agnostic reuse
- Support ?server= query param for bookmarkable auto-connect
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous usage was vulnerable to "poisoned" tags
which could enduce commands to be run when a
`detectChanges` command was hit. This was primarily
fixed in `local-backend.ts` however I changes the
`execSync` usages where any injection was potentially
able to be performed (e.g. staleness).
Skipped touching wiki and generator as those use static
input, though these should potentially be changed over
in the future.
SingleBar from cli-progress doesn't have a .log() method (that's
MultiBar-only), causing a crash when any code calls console.log
during the pipeline.
Fixes#53
Thanks to @TranDatk for reporting in #56.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Embeddings (ONNX Runtime) caused segfaults on macOS (#38) and CUDA
install failures on Linux (#40). Since embeddings only power semantic
vector search in query() and BM25 + graph traversal already provide
strong retrieval, disable embeddings by default with minimal quality
impact.
Changes:
- Replace --skip-embeddings with --embeddings opt-in flag
- Remove disposeEmbedder() call (ONNX native cleanup segfaults)
- Add process.exit(0) when embeddings are used to bypass ONNX atexit hooks
- Suppress ONNX session warnings via logSeverityLevel
- Fix progress bar duplication by routing console output through bar.log()
- Add elapsed time indicator for long-running phases
Fixes#38Fixes#40
listRepos() now re-reads ~/.gitnexus/registry.json on every call.
resolveRepo() retries with a registry refresh on cache miss.
Extracted refreshRepos() from init() for reuse. All callers updated
with await for the new async signatures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Hook config goes in ~/.claude/settings.json (not hooks.json)
- Matcher uses string format ("Grep|Glob|Bash") per new Claude Code schema
- Rename gitnexus-hook.js → gitnexus-hook.cjs for CommonJS compatibility
- Fix setup.ts: correct hook filename and timeout (8000ms instead of 10ms)
- Bump to v1.1.9 and publish to npm
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>