@veewo/gitnexus 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/README.md +234 -0
  2. package/dist/benchmark/agent-context/evaluators.d.ts +9 -0
  3. package/dist/benchmark/agent-context/evaluators.js +196 -0
  4. package/dist/benchmark/agent-context/evaluators.test.d.ts +1 -0
  5. package/dist/benchmark/agent-context/evaluators.test.js +39 -0
  6. package/dist/benchmark/agent-context/io.d.ts +2 -0
  7. package/dist/benchmark/agent-context/io.js +23 -0
  8. package/dist/benchmark/agent-context/io.test.d.ts +1 -0
  9. package/dist/benchmark/agent-context/io.test.js +19 -0
  10. package/dist/benchmark/agent-context/report.d.ts +2 -0
  11. package/dist/benchmark/agent-context/report.js +59 -0
  12. package/dist/benchmark/agent-context/report.test.d.ts +1 -0
  13. package/dist/benchmark/agent-context/report.test.js +85 -0
  14. package/dist/benchmark/agent-context/runner.d.ts +46 -0
  15. package/dist/benchmark/agent-context/runner.js +111 -0
  16. package/dist/benchmark/agent-context/runner.test.d.ts +1 -0
  17. package/dist/benchmark/agent-context/runner.test.js +79 -0
  18. package/dist/benchmark/agent-context/tool-runner.d.ts +7 -0
  19. package/dist/benchmark/agent-context/tool-runner.js +18 -0
  20. package/dist/benchmark/agent-context/tool-runner.test.d.ts +1 -0
  21. package/dist/benchmark/agent-context/tool-runner.test.js +11 -0
  22. package/dist/benchmark/agent-context/types.d.ts +40 -0
  23. package/dist/benchmark/agent-context/types.js +1 -0
  24. package/dist/benchmark/analyze-runner.d.ts +16 -0
  25. package/dist/benchmark/analyze-runner.js +51 -0
  26. package/dist/benchmark/analyze-runner.test.d.ts +1 -0
  27. package/dist/benchmark/analyze-runner.test.js +37 -0
  28. package/dist/benchmark/evaluators.d.ts +6 -0
  29. package/dist/benchmark/evaluators.js +10 -0
  30. package/dist/benchmark/evaluators.test.d.ts +1 -0
  31. package/dist/benchmark/evaluators.test.js +12 -0
  32. package/dist/benchmark/io.d.ts +7 -0
  33. package/dist/benchmark/io.js +25 -0
  34. package/dist/benchmark/io.test.d.ts +1 -0
  35. package/dist/benchmark/io.test.js +35 -0
  36. package/dist/benchmark/neonspark-candidates.d.ts +19 -0
  37. package/dist/benchmark/neonspark-candidates.js +94 -0
  38. package/dist/benchmark/neonspark-candidates.test.d.ts +1 -0
  39. package/dist/benchmark/neonspark-candidates.test.js +43 -0
  40. package/dist/benchmark/neonspark-materialize.d.ts +19 -0
  41. package/dist/benchmark/neonspark-materialize.js +111 -0
  42. package/dist/benchmark/neonspark-materialize.test.d.ts +1 -0
  43. package/dist/benchmark/neonspark-materialize.test.js +124 -0
  44. package/dist/benchmark/neonspark-sync.d.ts +3 -0
  45. package/dist/benchmark/neonspark-sync.js +53 -0
  46. package/dist/benchmark/neonspark-sync.test.d.ts +1 -0
  47. package/dist/benchmark/neonspark-sync.test.js +20 -0
  48. package/dist/benchmark/report.d.ts +1 -0
  49. package/dist/benchmark/report.js +7 -0
  50. package/dist/benchmark/runner.d.ts +48 -0
  51. package/dist/benchmark/runner.js +302 -0
  52. package/dist/benchmark/runner.test.d.ts +1 -0
  53. package/dist/benchmark/runner.test.js +50 -0
  54. package/dist/benchmark/scoring.d.ts +16 -0
  55. package/dist/benchmark/scoring.js +27 -0
  56. package/dist/benchmark/scoring.test.d.ts +1 -0
  57. package/dist/benchmark/scoring.test.js +24 -0
  58. package/dist/benchmark/tool-runner.d.ts +6 -0
  59. package/dist/benchmark/tool-runner.js +17 -0
  60. package/dist/benchmark/types.d.ts +36 -0
  61. package/dist/benchmark/types.js +1 -0
  62. package/dist/cli/ai-context.d.ts +22 -0
  63. package/dist/cli/ai-context.js +184 -0
  64. package/dist/cli/ai-context.test.d.ts +1 -0
  65. package/dist/cli/ai-context.test.js +30 -0
  66. package/dist/cli/analyze-multi-scope-regression.test.d.ts +1 -0
  67. package/dist/cli/analyze-multi-scope-regression.test.js +22 -0
  68. package/dist/cli/analyze-options.d.ts +7 -0
  69. package/dist/cli/analyze-options.js +56 -0
  70. package/dist/cli/analyze-options.test.d.ts +1 -0
  71. package/dist/cli/analyze-options.test.js +36 -0
  72. package/dist/cli/analyze.d.ts +14 -0
  73. package/dist/cli/analyze.js +384 -0
  74. package/dist/cli/augment.d.ts +13 -0
  75. package/dist/cli/augment.js +33 -0
  76. package/dist/cli/benchmark-agent-context.d.ts +29 -0
  77. package/dist/cli/benchmark-agent-context.js +61 -0
  78. package/dist/cli/benchmark-agent-context.test.d.ts +1 -0
  79. package/dist/cli/benchmark-agent-context.test.js +80 -0
  80. package/dist/cli/benchmark-unity.d.ts +15 -0
  81. package/dist/cli/benchmark-unity.js +31 -0
  82. package/dist/cli/benchmark-unity.test.d.ts +1 -0
  83. package/dist/cli/benchmark-unity.test.js +18 -0
  84. package/dist/cli/claude-hooks.d.ts +22 -0
  85. package/dist/cli/claude-hooks.js +97 -0
  86. package/dist/cli/clean.d.ts +10 -0
  87. package/dist/cli/clean.js +60 -0
  88. package/dist/cli/eval-server.d.ts +30 -0
  89. package/dist/cli/eval-server.js +372 -0
  90. package/dist/cli/index.d.ts +2 -0
  91. package/dist/cli/index.js +182 -0
  92. package/dist/cli/list.d.ts +6 -0
  93. package/dist/cli/list.js +33 -0
  94. package/dist/cli/mcp.d.ts +8 -0
  95. package/dist/cli/mcp.js +34 -0
  96. package/dist/cli/repo-manager-alias.test.d.ts +1 -0
  97. package/dist/cli/repo-manager-alias.test.js +40 -0
  98. package/dist/cli/scope-filter.test.d.ts +1 -0
  99. package/dist/cli/scope-filter.test.js +49 -0
  100. package/dist/cli/serve.d.ts +4 -0
  101. package/dist/cli/serve.js +6 -0
  102. package/dist/cli/setup.d.ts +8 -0
  103. package/dist/cli/setup.js +311 -0
  104. package/dist/cli/setup.test.d.ts +1 -0
  105. package/dist/cli/setup.test.js +31 -0
  106. package/dist/cli/status.d.ts +6 -0
  107. package/dist/cli/status.js +27 -0
  108. package/dist/cli/tool.d.ts +40 -0
  109. package/dist/cli/tool.js +94 -0
  110. package/dist/cli/version.test.d.ts +1 -0
  111. package/dist/cli/version.test.js +19 -0
  112. package/dist/cli/wiki.d.ts +15 -0
  113. package/dist/cli/wiki.js +361 -0
  114. package/dist/config/ignore-service.d.ts +1 -0
  115. package/dist/config/ignore-service.js +210 -0
  116. package/dist/config/supported-languages.d.ts +12 -0
  117. package/dist/config/supported-languages.js +15 -0
  118. package/dist/core/augmentation/engine.d.ts +26 -0
  119. package/dist/core/augmentation/engine.js +213 -0
  120. package/dist/core/embeddings/embedder.d.ts +60 -0
  121. package/dist/core/embeddings/embedder.js +251 -0
  122. package/dist/core/embeddings/embedding-pipeline.d.ts +51 -0
  123. package/dist/core/embeddings/embedding-pipeline.js +329 -0
  124. package/dist/core/embeddings/index.d.ts +9 -0
  125. package/dist/core/embeddings/index.js +9 -0
  126. package/dist/core/embeddings/text-generator.d.ts +24 -0
  127. package/dist/core/embeddings/text-generator.js +182 -0
  128. package/dist/core/embeddings/types.d.ts +87 -0
  129. package/dist/core/embeddings/types.js +32 -0
  130. package/dist/core/graph/graph.d.ts +2 -0
  131. package/dist/core/graph/graph.js +66 -0
  132. package/dist/core/graph/types.d.ts +61 -0
  133. package/dist/core/graph/types.js +1 -0
  134. package/dist/core/ingestion/ast-cache.d.ts +11 -0
  135. package/dist/core/ingestion/ast-cache.js +34 -0
  136. package/dist/core/ingestion/call-processor.d.ts +15 -0
  137. package/dist/core/ingestion/call-processor.js +327 -0
  138. package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
  139. package/dist/core/ingestion/cluster-enricher.js +170 -0
  140. package/dist/core/ingestion/community-processor.d.ts +39 -0
  141. package/dist/core/ingestion/community-processor.js +312 -0
  142. package/dist/core/ingestion/entry-point-scoring.d.ts +39 -0
  143. package/dist/core/ingestion/entry-point-scoring.js +260 -0
  144. package/dist/core/ingestion/filesystem-walker.d.ts +28 -0
  145. package/dist/core/ingestion/filesystem-walker.js +80 -0
  146. package/dist/core/ingestion/framework-detection.d.ts +39 -0
  147. package/dist/core/ingestion/framework-detection.js +235 -0
  148. package/dist/core/ingestion/heritage-processor.d.ts +20 -0
  149. package/dist/core/ingestion/heritage-processor.js +197 -0
  150. package/dist/core/ingestion/import-processor.d.ts +38 -0
  151. package/dist/core/ingestion/import-processor.js +778 -0
  152. package/dist/core/ingestion/parsing-processor.d.ts +15 -0
  153. package/dist/core/ingestion/parsing-processor.js +291 -0
  154. package/dist/core/ingestion/pipeline.d.ts +5 -0
  155. package/dist/core/ingestion/pipeline.js +323 -0
  156. package/dist/core/ingestion/process-processor.d.ts +51 -0
  157. package/dist/core/ingestion/process-processor.js +309 -0
  158. package/dist/core/ingestion/scope-filter.d.ts +25 -0
  159. package/dist/core/ingestion/scope-filter.js +100 -0
  160. package/dist/core/ingestion/structure-processor.d.ts +2 -0
  161. package/dist/core/ingestion/structure-processor.js +36 -0
  162. package/dist/core/ingestion/symbol-table.d.ts +33 -0
  163. package/dist/core/ingestion/symbol-table.js +38 -0
  164. package/dist/core/ingestion/tree-sitter-queries.d.ts +12 -0
  165. package/dist/core/ingestion/tree-sitter-queries.js +398 -0
  166. package/dist/core/ingestion/utils.d.ts +10 -0
  167. package/dist/core/ingestion/utils.js +50 -0
  168. package/dist/core/ingestion/workers/parse-worker.d.ts +59 -0
  169. package/dist/core/ingestion/workers/parse-worker.js +672 -0
  170. package/dist/core/ingestion/workers/worker-pool.d.ts +16 -0
  171. package/dist/core/ingestion/workers/worker-pool.js +120 -0
  172. package/dist/core/kuzu/csv-generator.d.ts +29 -0
  173. package/dist/core/kuzu/csv-generator.js +336 -0
  174. package/dist/core/kuzu/kuzu-adapter.d.ts +101 -0
  175. package/dist/core/kuzu/kuzu-adapter.js +753 -0
  176. package/dist/core/kuzu/schema.d.ts +53 -0
  177. package/dist/core/kuzu/schema.js +407 -0
  178. package/dist/core/search/bm25-index.d.ts +23 -0
  179. package/dist/core/search/bm25-index.js +95 -0
  180. package/dist/core/search/hybrid-search.d.ts +49 -0
  181. package/dist/core/search/hybrid-search.js +118 -0
  182. package/dist/core/tree-sitter/parser-loader.d.ts +4 -0
  183. package/dist/core/tree-sitter/parser-loader.js +44 -0
  184. package/dist/core/wiki/generator.d.ts +110 -0
  185. package/dist/core/wiki/generator.js +786 -0
  186. package/dist/core/wiki/graph-queries.d.ts +80 -0
  187. package/dist/core/wiki/graph-queries.js +238 -0
  188. package/dist/core/wiki/html-viewer.d.ts +10 -0
  189. package/dist/core/wiki/html-viewer.js +297 -0
  190. package/dist/core/wiki/llm-client.d.ts +40 -0
  191. package/dist/core/wiki/llm-client.js +162 -0
  192. package/dist/core/wiki/prompts.d.ts +53 -0
  193. package/dist/core/wiki/prompts.js +174 -0
  194. package/dist/lib/utils.d.ts +1 -0
  195. package/dist/lib/utils.js +3 -0
  196. package/dist/mcp/core/embedder.d.ts +27 -0
  197. package/dist/mcp/core/embedder.js +108 -0
  198. package/dist/mcp/core/kuzu-adapter.d.ts +34 -0
  199. package/dist/mcp/core/kuzu-adapter.js +231 -0
  200. package/dist/mcp/local/local-backend.d.ts +160 -0
  201. package/dist/mcp/local/local-backend.js +1646 -0
  202. package/dist/mcp/resources.d.ts +31 -0
  203. package/dist/mcp/resources.js +407 -0
  204. package/dist/mcp/server.d.ts +23 -0
  205. package/dist/mcp/server.js +251 -0
  206. package/dist/mcp/staleness.d.ts +15 -0
  207. package/dist/mcp/staleness.js +29 -0
  208. package/dist/mcp/tools.d.ts +24 -0
  209. package/dist/mcp/tools.js +195 -0
  210. package/dist/server/api.d.ts +10 -0
  211. package/dist/server/api.js +344 -0
  212. package/dist/server/mcp-http.d.ts +13 -0
  213. package/dist/server/mcp-http.js +100 -0
  214. package/dist/storage/git.d.ts +6 -0
  215. package/dist/storage/git.js +32 -0
  216. package/dist/storage/repo-manager.d.ts +125 -0
  217. package/dist/storage/repo-manager.js +257 -0
  218. package/dist/types/pipeline.d.ts +34 -0
  219. package/dist/types/pipeline.js +18 -0
  220. package/hooks/claude/gitnexus-hook.cjs +135 -0
  221. package/hooks/claude/pre-tool-use.sh +78 -0
  222. package/hooks/claude/session-start.sh +42 -0
  223. package/package.json +92 -0
  224. package/skills/gitnexus-cli.md +82 -0
  225. package/skills/gitnexus-debugging.md +89 -0
  226. package/skills/gitnexus-exploring.md +78 -0
  227. package/skills/gitnexus-guide.md +64 -0
  228. package/skills/gitnexus-impact-analysis.md +97 -0
  229. package/skills/gitnexus-refactoring.md +121 -0
  230. package/vendor/leiden/index.cjs +355 -0
  231. package/vendor/leiden/utils.cjs +392 -0
package/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # GitNexus
2
+
3
+ **Graph-powered code intelligence for AI agents.** Index any codebase into a knowledge graph, then query it via MCP or CLI.
4
+
5
+ Works with **Cursor**, **Claude Code**, **Windsurf**, **Cline**, **OpenCode**, and any MCP-compatible tool.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/gitnexus.svg)](https://www.npmjs.com/package/gitnexus)
8
+ [![License: PolyForm Noncommercial](https://img.shields.io/badge/License-PolyForm%20Noncommercial-blue.svg)](https://polyformproject.org/licenses/noncommercial/1.0.0/)
9
+
10
+ ---
11
+
12
+ ## Why?
13
+
14
+ AI coding tools don't understand your codebase structure. They edit a function without knowing 47 other functions depend on it. GitNexus fixes this by **precomputing every dependency, call chain, and relationship** into a queryable graph.
15
+
16
+ **Three commands to give your AI agent full codebase awareness.**
17
+
18
+ ## Quick Start
19
+
20
+ ```bash
21
+ # Index your repo (run from repo root)
22
+ npx gitnexus analyze
23
+ ```
24
+
25
+ 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.
26
+
27
+ To configure MCP for your editor, run `npx gitnexus setup` once — or set it up manually below.
28
+
29
+ `gitnexus setup` auto-detects your editors and writes the correct global MCP config. You only need to run it once.
30
+
31
+ ## Team Deployment and Distribution
32
+
33
+ For small-team rollout (single stable channel only), follow:
34
+ - [CLI Deployment and Distribution](../docs/cli-release-distribution.md)
35
+
36
+ Key links:
37
+ - [npm publish workflow](../.github/workflows/publish.yml)
38
+ - [CLI package config](./package.json)
39
+
40
+ ### Editor Support
41
+
42
+ | Editor | MCP | Skills | Hooks (auto-augment) | Support |
43
+ |--------|-----|--------|---------------------|---------|
44
+ | **Claude Code** | Yes | Yes | Yes (PreToolUse) | **Full** |
45
+ | **Cursor** | Yes | Yes | — | MCP + Skills |
46
+ | **Windsurf** | Yes | — | — | MCP |
47
+ | **OpenCode** | Yes | Yes | — | MCP + Skills |
48
+
49
+ > **Claude Code** gets the deepest integration: MCP tools + agent skills + PreToolUse hooks that automatically enrich grep/glob/bash calls with knowledge graph context.
50
+
51
+ ### Community Integrations
52
+
53
+ | Agent | Install | Source |
54
+ |-------|---------|--------|
55
+ | [pi](https://pi.dev) | `pi install npm:pi-gitnexus` | [pi-gitnexus](https://github.com/tintinweb/pi-gitnexus) |
56
+
57
+ ## MCP Setup (manual)
58
+
59
+ If you prefer to configure manually instead of using `gitnexus setup`:
60
+
61
+ ### Claude Code (full support — MCP + skills + hooks)
62
+
63
+ ```bash
64
+ claude mcp add gitnexus -- npx -y gitnexus@latest mcp
65
+ ```
66
+
67
+ ### Cursor / Windsurf
68
+
69
+ Add to `~/.cursor/mcp.json` (global — works for all projects):
70
+
71
+ ```json
72
+ {
73
+ "mcpServers": {
74
+ "gitnexus": {
75
+ "command": "npx",
76
+ "args": ["-y", "gitnexus@latest", "mcp"]
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ ### OpenCode
83
+
84
+ Add to `~/.config/opencode/config.json`:
85
+
86
+ ```json
87
+ {
88
+ "mcp": {
89
+ "gitnexus": {
90
+ "command": "npx",
91
+ "args": ["-y", "gitnexus@latest", "mcp"]
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
97
+ ## How It Works
98
+
99
+ GitNexus builds a complete knowledge graph of your codebase through a multi-phase indexing pipeline:
100
+
101
+ 1. **Structure** — Walks the file tree and maps folder/file relationships
102
+ 2. **Parsing** — Extracts functions, classes, methods, and interfaces using Tree-sitter ASTs
103
+ 3. **Resolution** — Resolves imports and function calls across files with language-aware logic
104
+ 4. **Clustering** — Groups related symbols into functional communities
105
+ 5. **Processes** — Traces execution flows from entry points through call chains
106
+ 6. **Search** — Builds hybrid search indexes for fast retrieval
107
+
108
+ The result is a **KuzuDB graph database** stored locally in `.gitnexus/` with full-text search and semantic embeddings.
109
+
110
+ ## MCP Tools
111
+
112
+ Your AI agent gets these tools automatically:
113
+
114
+ | Tool | What It Does | `repo` Param |
115
+ |------|-------------|--------------|
116
+ | `list_repos` | Discover all indexed repositories | — |
117
+ | `query` | Process-grouped hybrid search (BM25 + semantic + RRF) | Optional |
118
+ | `context` | 360-degree symbol view — categorized refs, process participation | Optional |
119
+ | `impact` | Blast radius analysis with depth grouping and confidence | Optional |
120
+ | `detect_changes` | Git-diff impact — maps changed lines to affected processes | Optional |
121
+ | `rename` | Multi-file coordinated rename with graph + text search | Optional |
122
+ | `cypher` | Raw Cypher graph queries | Optional |
123
+
124
+ > With one indexed repo, the `repo` param is optional. With multiple, specify which: `query({query: "auth", repo: "my-app"})`.
125
+
126
+ ## MCP Resources
127
+
128
+ | Resource | Purpose |
129
+ |----------|---------|
130
+ | `gitnexus://repos` | List all indexed repositories (read first) |
131
+ | `gitnexus://repo/{name}/context` | Codebase stats, staleness check, and available tools |
132
+ | `gitnexus://repo/{name}/clusters` | All functional clusters with cohesion scores |
133
+ | `gitnexus://repo/{name}/cluster/{name}` | Cluster members and details |
134
+ | `gitnexus://repo/{name}/processes` | All execution flows |
135
+ | `gitnexus://repo/{name}/process/{name}` | Full process trace with steps |
136
+ | `gitnexus://repo/{name}/schema` | Graph schema for Cypher queries |
137
+
138
+ ## MCP Prompts
139
+
140
+ | Prompt | What It Does |
141
+ |--------|-------------|
142
+ | `detect_impact` | Pre-commit change analysis — scope, affected processes, risk level |
143
+ | `generate_map` | Architecture documentation from the knowledge graph with mermaid diagrams |
144
+
145
+ ## CLI Commands
146
+
147
+ ```bash
148
+ gitnexus setup # Configure MCP for your editors (one-time)
149
+ gitnexus analyze [path] # Index a repository (or update stale index)
150
+ gitnexus analyze --force # Force full re-index
151
+ gitnexus analyze --embeddings # Enable semantic embeddings (off by default)
152
+ gitnexus analyze --scope-prefix Assets/NEON/Code --scope-prefix Packages/com.veewo.* # Scoped multi-directory indexing
153
+ gitnexus analyze --scope-manifest ./scope.txt --repo-alias neonspark-v1-subset # Scoped indexing + stable repo alias
154
+ gitnexus mcp # Start MCP server (stdio) — serves all indexed repos
155
+ gitnexus serve # Start local HTTP server (multi-repo) for web UI
156
+ gitnexus list # List all indexed repositories
157
+ gitnexus status # Show index status for current repo
158
+ gitnexus clean # Delete index for current repo
159
+ gitnexus clean --all --force # Delete all indexes
160
+ gitnexus wiki [path] # Generate LLM-powered docs from knowledge graph
161
+ gitnexus wiki --model <model> # Wiki with custom LLM model (default: gpt-4o-mini)
162
+ gitnexus benchmark-unity ../benchmarks/unity-baseline/v1 --profile quick --target-path ../benchmarks/fixtures/unity-mini
163
+ gitnexus benchmark-unity ../benchmarks/unity-baseline/v1 --profile full --target-path ../benchmarks/fixtures/unity-mini
164
+ ```
165
+
166
+ For scoped indexing, `analyze` logs scope overlap dedupe counts and any normalized path collisions to help diagnose multi-directory merge safety.
167
+
168
+ ## Unity Benchmark
169
+
170
+ Run reproducible Unity/C# accuracy and regression checks:
171
+
172
+ ```bash
173
+ gitnexus benchmark-unity ../benchmarks/unity-baseline/v1 --profile quick --target-path ../benchmarks/fixtures/unity-mini
174
+ gitnexus benchmark-unity ../benchmarks/unity-baseline/v1 --profile full --target-path ../benchmarks/fixtures/unity-mini
175
+ ```
176
+
177
+ Reports are written to `.gitnexus/benchmark/benchmark-report.json` and `.gitnexus/benchmark/benchmark-summary.md`.
178
+
179
+ Hard gates:
180
+
181
+ | Metric | Threshold |
182
+ |--------|-----------|
183
+ | Query precision | `>= 0.90` |
184
+ | Query recall | `>= 0.85` |
185
+ | Context/impact F1 | `>= 0.80` |
186
+ | Smoke pass rate | `= 1.00` |
187
+ | Analyze time regression | `<= +15%` |
188
+
189
+ ## Multi-Repo Support
190
+
191
+ GitNexus supports indexing multiple repositories. Each `gitnexus analyze` registers the repo in a global registry (`~/.gitnexus/registry.json`). The MCP server serves all indexed repos automatically.
192
+
193
+ ## Supported Languages
194
+
195
+ TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust
196
+
197
+ ## Agent Skills
198
+
199
+ GitNexus ships with skill files that teach AI agents how to use the tools effectively:
200
+
201
+ - **Exploring** — Navigate unfamiliar code using the knowledge graph
202
+ - **Debugging** — Trace bugs through call chains
203
+ - **Impact Analysis** — Analyze blast radius before changes
204
+ - **Refactoring** — Plan safe refactors using dependency mapping
205
+
206
+ Installation rules:
207
+
208
+ - `gitnexus analyze` installs repo-local skills to `.agents/skills/gitnexus/` and updates `AGENTS.md` / `CLAUDE.md`.
209
+ - `gitnexus setup` installs global skills to `~/.agents/skills/gitnexus/`.
210
+ - If needed, create editor-specific symlinks yourself (for example map `.claude/skills/gitnexus` to `~/.agents/skills/gitnexus`).
211
+
212
+ ## Requirements
213
+
214
+ - Node.js >= 18
215
+ - Git repository (uses git for commit tracking)
216
+
217
+ ## Privacy
218
+
219
+ - All processing happens locally on your machine
220
+ - No code is sent to any server
221
+ - Index stored in `.gitnexus/` inside your repo (gitignored)
222
+ - Global registry at `~/.gitnexus/` stores only paths and metadata
223
+
224
+ ## Web UI
225
+
226
+ GitNexus also has a browser-based UI at [gitnexus.vercel.app](https://gitnexus.vercel.app) — 100% client-side, your code never leaves the browser.
227
+
228
+ **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.
229
+
230
+ ## License
231
+
232
+ [PolyForm Noncommercial 1.0.0](https://polyformproject.org/licenses/noncommercial/1.0.0/)
233
+
234
+ Free for non-commercial use. Contact for commercial licensing.
@@ -0,0 +1,9 @@
1
+ import type { AgentContextCheck, AgentContextCheckResult } from './types.js';
2
+ type StepOutput = Record<string, any>;
3
+ export declare function evaluateCheckT(stepOutputs: StepOutput[], expectedUid: string): AgentContextCheckResult;
4
+ export declare function evaluateCheckE(toolCalls: number, maxToolCalls: number): AgentContextCheckResult;
5
+ export declare function evaluateScenarioChecks(stepOutputs: StepOutput[], checks: AgentContextCheck[], options?: {
6
+ targetUid?: string;
7
+ toolCalls?: number;
8
+ }): AgentContextCheckResult[];
9
+ export {};
@@ -0,0 +1,196 @@
1
+ function normalize(value) {
2
+ return value.trim().toLowerCase();
3
+ }
4
+ function pushUid(into, uid) {
5
+ if (typeof uid === 'string' && uid.trim()) {
6
+ into.add(uid.trim());
7
+ }
8
+ }
9
+ function collectUids(stepOutputs) {
10
+ const hits = new Set();
11
+ for (const output of stepOutputs) {
12
+ pushUid(hits, output?.symbol?.uid);
13
+ pushUid(hits, output?.target?.id);
14
+ for (const row of output?.process_symbols || []) {
15
+ pushUid(hits, row?.id);
16
+ }
17
+ for (const row of output?.definitions || []) {
18
+ pushUid(hits, row?.id);
19
+ }
20
+ for (const row of output?.candidates || []) {
21
+ pushUid(hits, row?.uid);
22
+ }
23
+ for (const list of Object.values(output?.byDepth || {})) {
24
+ if (!Array.isArray(list)) {
25
+ continue;
26
+ }
27
+ for (const row of list) {
28
+ pushUid(hits, row?.id);
29
+ }
30
+ }
31
+ }
32
+ return [...hits];
33
+ }
34
+ function countIncoming(stepOutputs) {
35
+ let count = 0;
36
+ for (const output of stepOutputs) {
37
+ for (const rows of Object.values(output?.incoming || {})) {
38
+ if (Array.isArray(rows)) {
39
+ count += rows.length;
40
+ }
41
+ }
42
+ }
43
+ return count;
44
+ }
45
+ function countOutgoing(stepOutputs) {
46
+ let count = 0;
47
+ for (const output of stepOutputs) {
48
+ for (const rows of Object.values(output?.outgoing || {})) {
49
+ if (Array.isArray(rows)) {
50
+ count += rows.length;
51
+ }
52
+ }
53
+ }
54
+ return count;
55
+ }
56
+ function countImpacted(stepOutputs) {
57
+ let total = 0;
58
+ for (const output of stepOutputs) {
59
+ total += Number(output?.impactedCount || 0);
60
+ }
61
+ return total;
62
+ }
63
+ function collectNames(stepOutputs) {
64
+ const names = new Set();
65
+ const addName = (name) => {
66
+ if (typeof name === 'string' && name.trim()) {
67
+ names.add(name.trim());
68
+ }
69
+ };
70
+ for (const output of stepOutputs) {
71
+ addName(output?.symbol?.name);
72
+ addName(output?.target?.name);
73
+ for (const row of output?.process_symbols || []) {
74
+ addName(row?.name);
75
+ }
76
+ for (const row of output?.definitions || []) {
77
+ addName(row?.name);
78
+ }
79
+ for (const row of output?.candidates || []) {
80
+ addName(row?.name);
81
+ }
82
+ for (const rows of Object.values(output?.incoming || {})) {
83
+ if (!Array.isArray(rows)) {
84
+ continue;
85
+ }
86
+ for (const row of rows) {
87
+ addName(row?.name);
88
+ }
89
+ }
90
+ for (const rows of Object.values(output?.outgoing || {})) {
91
+ if (!Array.isArray(rows)) {
92
+ continue;
93
+ }
94
+ for (const row of rows) {
95
+ addName(row?.name);
96
+ }
97
+ }
98
+ for (const rows of Object.values(output?.byDepth || {})) {
99
+ if (!Array.isArray(rows)) {
100
+ continue;
101
+ }
102
+ for (const row of rows) {
103
+ addName(row?.name);
104
+ }
105
+ }
106
+ }
107
+ return [...names];
108
+ }
109
+ export function evaluateCheckT(stepOutputs, expectedUid) {
110
+ const expected = normalize(expectedUid);
111
+ const pass = collectUids(stepOutputs).some((uid) => {
112
+ const n = normalize(uid);
113
+ return n === expected || n.endsWith(expected) || expected.endsWith(n);
114
+ });
115
+ return {
116
+ id: 'T',
117
+ pass,
118
+ detail: pass ? undefined : `target uid not found: ${expectedUid}`,
119
+ };
120
+ }
121
+ export function evaluateCheckE(toolCalls, maxToolCalls) {
122
+ const pass = toolCalls <= maxToolCalls;
123
+ return {
124
+ id: 'E',
125
+ pass,
126
+ detail: pass ? undefined : `tool calls ${toolCalls} exceed max ${maxToolCalls}`,
127
+ };
128
+ }
129
+ function evaluateCheckU(stepOutputs, minIncoming) {
130
+ const incoming = countIncoming(stepOutputs);
131
+ return {
132
+ id: 'U',
133
+ pass: incoming >= minIncoming,
134
+ detail: incoming >= minIncoming ? undefined : `incoming refs ${incoming} < ${minIncoming}`,
135
+ };
136
+ }
137
+ function evaluateCheckD(stepOutputs, minOutgoing) {
138
+ const outgoing = countOutgoing(stepOutputs);
139
+ return {
140
+ id: 'D',
141
+ pass: outgoing >= minOutgoing,
142
+ detail: outgoing >= minOutgoing ? undefined : `outgoing refs ${outgoing} < ${minOutgoing}`,
143
+ };
144
+ }
145
+ function evaluateCheckB(stepOutputs, minImpacted) {
146
+ const impacted = countImpacted(stepOutputs);
147
+ return {
148
+ id: 'B',
149
+ pass: impacted >= minImpacted,
150
+ detail: impacted >= minImpacted ? undefined : `impacted count ${impacted} < ${minImpacted}`,
151
+ };
152
+ }
153
+ function evaluateCheckI(stepOutputs, anchors, minInternalHits) {
154
+ const loweredAnchors = anchors.map((anchor) => normalize(anchor));
155
+ const names = collectNames(stepOutputs).map((name) => normalize(name));
156
+ const matched = new Set();
157
+ for (const anchor of loweredAnchors) {
158
+ if (names.some((name) => name.includes(anchor))) {
159
+ matched.add(anchor);
160
+ }
161
+ }
162
+ return {
163
+ id: 'I',
164
+ pass: matched.size >= minInternalHits,
165
+ detail: matched.size >= minInternalHits ? undefined : `internal anchors matched ${matched.size} < ${minInternalHits}`,
166
+ };
167
+ }
168
+ export function evaluateScenarioChecks(stepOutputs, checks, options) {
169
+ const results = [];
170
+ for (const check of checks) {
171
+ switch (check.id) {
172
+ case 'T':
173
+ results.push(evaluateCheckT(stepOutputs, check.required_uid || options?.targetUid || ''));
174
+ break;
175
+ case 'U':
176
+ results.push(evaluateCheckU(stepOutputs, check.min_incoming ?? 0));
177
+ break;
178
+ case 'D':
179
+ results.push(evaluateCheckD(stepOutputs, check.min_outgoing ?? 0));
180
+ break;
181
+ case 'B':
182
+ results.push(evaluateCheckB(stepOutputs, check.min_impacted ?? 0));
183
+ break;
184
+ case 'I':
185
+ results.push(evaluateCheckI(stepOutputs, check.internal_anchors || [], check.min_internal_hits ?? 0));
186
+ break;
187
+ case 'E':
188
+ results.push(evaluateCheckE(options?.toolCalls ?? stepOutputs.length, check.max_tool_calls ?? Number.MAX_SAFE_INTEGER));
189
+ break;
190
+ default:
191
+ results.push({ id: check.id, pass: false, detail: `unsupported check id: ${check.id}` });
192
+ break;
193
+ }
194
+ }
195
+ return results;
196
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { evaluateCheckE, evaluateCheckT, evaluateScenarioChecks } from './evaluators.js';
4
+ test('evaluates mandatory target disambiguation check T', () => {
5
+ const stepOutputs = [
6
+ {
7
+ symbol: { uid: 'Class:Sample:Target' },
8
+ target: { id: 'Class:Sample:Target' },
9
+ process_symbols: [{ id: 'Class:Sample:Target', name: 'Target' }],
10
+ definitions: [],
11
+ },
12
+ ];
13
+ const result = evaluateCheckT(stepOutputs, 'Class:Sample:Target');
14
+ assert.equal(result.pass, true);
15
+ });
16
+ test('evaluates efficiency check E by tool call budget', () => {
17
+ const result = evaluateCheckE(3, 4);
18
+ assert.equal(result.pass, true);
19
+ });
20
+ test('evaluates internal coverage check I from context/impact result names', () => {
21
+ const stepOutputs = [
22
+ {
23
+ incoming: {
24
+ calls: [{ id: 'Method:Sample:RefreshScreen', name: 'RefreshScreen' }],
25
+ },
26
+ outgoing: {
27
+ calls: [{ id: 'Method:Sample:HidePanel', name: 'HidePanel' }],
28
+ },
29
+ byDepth: {
30
+ depth_1: [{ id: 'Method:Sample:SyncState', name: 'SyncState' }],
31
+ },
32
+ },
33
+ ];
34
+ const checks = [
35
+ { id: 'I', internal_anchors: ['Refresh', 'Sync'], min_internal_hits: 2 },
36
+ ];
37
+ const [result] = evaluateScenarioChecks(stepOutputs, checks);
38
+ assert.equal(result.pass, true);
39
+ });
@@ -0,0 +1,2 @@
1
+ import type { AgentContextDataset } from './types.js';
2
+ export declare function loadAgentContextDataset(root: string): Promise<AgentContextDataset>;
@@ -0,0 +1,23 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ export async function loadAgentContextDataset(root) {
4
+ const thresholds = JSON.parse(await fs.readFile(path.join(root, 'thresholds.json'), 'utf-8'));
5
+ const scenarios = await readJsonl(path.join(root, 'scenarios.jsonl'), ['scenario_id', 'target_uid', 'tool_plan', 'checks']);
6
+ return { thresholds, scenarios };
7
+ }
8
+ async function readJsonl(file, required) {
9
+ const raw = await fs.readFile(file, 'utf-8');
10
+ const rows = raw
11
+ .split('\n')
12
+ .map((line) => line.trim())
13
+ .filter(Boolean)
14
+ .map((line) => JSON.parse(line));
15
+ for (const row of rows) {
16
+ for (const key of required) {
17
+ if (!(key in row)) {
18
+ throw new Error(`missing required field: ${key}`);
19
+ }
20
+ }
21
+ }
22
+ return rows;
23
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import path from 'node:path';
4
+ import { loadAgentContextDataset } from './io.js';
5
+ test('loadAgentContextDataset validates required scenario fields', async () => {
6
+ const invalidRoot = path.resolve('src/benchmark/agent-context/__fixtures__/invalid/missing-checks');
7
+ await assert.rejects(() => loadAgentContextDataset(invalidRoot), /missing required field/i);
8
+ });
9
+ test('loadAgentContextDataset loads valid thresholds and scenarios', async () => {
10
+ const validRoot = path.resolve('src/benchmark/agent-context/__fixtures__/valid');
11
+ const ds = await loadAgentContextDataset(validRoot);
12
+ assert.equal(ds.scenarios.length, 1);
13
+ assert.ok(ds.thresholds.coverage.minPerScenario > 0);
14
+ });
15
+ test('v1 scenario dataset loads exactly 3 scenarios', async () => {
16
+ const v1Root = path.resolve('../benchmarks/agent-context/neonspark-refactor-v1');
17
+ const ds = await loadAgentContextDataset(v1Root);
18
+ assert.equal(ds.scenarios.length, 3);
19
+ });
@@ -0,0 +1,2 @@
1
+ import type { AgentContextBenchmarkResult } from './runner.js';
2
+ export declare function writeAgentContextReports(reportDir: string, result: AgentContextBenchmarkResult): Promise<void>;
@@ -0,0 +1,59 @@
1
+ import { writeReports } from '../report.js';
2
+ function buildFailureClassRows(result) {
3
+ const counts = new Map();
4
+ for (const scenario of result.scenarios) {
5
+ for (const check of scenario.checks) {
6
+ if (!check.pass) {
7
+ counts.set(check.id, (counts.get(check.id) || 0) + 1);
8
+ }
9
+ }
10
+ }
11
+ return [...counts.entries()]
12
+ .map(([id, count]) => ({ id, count }))
13
+ .sort((a, b) => b.count - a.count);
14
+ }
15
+ function buildTriageRows(result) {
16
+ const failing = result.scenarios
17
+ .map((scenario) => {
18
+ const failedChecks = scenario.checks.filter((check) => !check.pass).map((check) => check.id);
19
+ return {
20
+ scenarioId: scenario.scenarioId,
21
+ coverage: scenario.coverage,
22
+ failedChecks,
23
+ };
24
+ })
25
+ .filter((row) => row.failedChecks.length > 0)
26
+ .sort((a, b) => a.coverage - b.coverage || b.failedChecks.length - a.failedChecks.length);
27
+ return failing.map((row, index) => `${index + 1}. ${row.scenarioId} (coverage=${row.coverage.toFixed(3)}, failed_checks=${row.failedChecks.join(', ')})`);
28
+ }
29
+ export async function writeAgentContextReports(reportDir, result) {
30
+ const jsonReport = {
31
+ generatedAt: new Date().toISOString(),
32
+ pass: result.pass,
33
+ failures: result.failures,
34
+ metrics: result.metrics,
35
+ scenarios: result.scenarios,
36
+ };
37
+ const failureClasses = buildFailureClassRows(result);
38
+ const triageRows = buildTriageRows(result);
39
+ const markdown = [
40
+ '# Agent-Context Benchmark Summary',
41
+ '',
42
+ `- Pass: ${result.pass ? 'YES' : 'NO'}`,
43
+ `- Average Coverage: ${result.metrics.avgCoverage.toFixed(3)}`,
44
+ `- Average Tool Calls: ${result.metrics.avgToolCalls.toFixed(3)}`,
45
+ `- Mandatory Target Pass Rate: ${result.metrics.mandatoryTargetPassRate.toFixed(3)}`,
46
+ '',
47
+ '## Scenarios',
48
+ ...result.scenarios.map((scenario) => `- ${scenario.scenarioId}: coverage=${scenario.coverage.toFixed(3)}, calls=${scenario.toolCalls}, gate=${scenario.gatePass ? 'PASS' : 'FAIL'}`),
49
+ '',
50
+ '## Top Failure Classes',
51
+ ...(failureClasses.length > 0
52
+ ? failureClasses.map((row) => `- ${row.id}: ${row.count}`)
53
+ : ['- none']),
54
+ '',
55
+ '## Recommended Triage Order',
56
+ ...(triageRows.length > 0 ? triageRows : ['1. none']),
57
+ ].join('\n');
58
+ await writeReports(reportDir, jsonReport, markdown);
59
+ }
@@ -0,0 +1 @@
1
+ export {};