gitnexus 1.1.9 → 1.2.0
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.
- package/README.md +50 -59
- package/dist/cli/analyze.js +114 -32
- package/dist/cli/eval-server.d.ts +30 -0
- package/dist/cli/eval-server.js +372 -0
- package/dist/cli/index.js +51 -1
- package/dist/cli/mcp.js +9 -0
- package/dist/cli/setup.js +44 -7
- package/dist/cli/tool.d.ts +37 -0
- package/dist/cli/tool.js +91 -0
- package/dist/cli/wiki.d.ts +13 -0
- package/dist/cli/wiki.js +199 -0
- package/dist/core/embeddings/embedder.d.ts +2 -2
- package/dist/core/embeddings/embedder.js +10 -10
- package/dist/core/embeddings/embedding-pipeline.d.ts +2 -1
- package/dist/core/embeddings/embedding-pipeline.js +12 -4
- package/dist/core/embeddings/types.d.ts +2 -2
- package/dist/core/ingestion/call-processor.d.ts +7 -0
- package/dist/core/ingestion/call-processor.js +61 -23
- package/dist/core/ingestion/community-processor.js +34 -26
- package/dist/core/ingestion/filesystem-walker.js +15 -10
- package/dist/core/ingestion/heritage-processor.d.ts +6 -0
- package/dist/core/ingestion/heritage-processor.js +68 -5
- package/dist/core/ingestion/import-processor.d.ts +22 -0
- package/dist/core/ingestion/import-processor.js +214 -19
- package/dist/core/ingestion/parsing-processor.d.ts +8 -1
- package/dist/core/ingestion/parsing-processor.js +66 -25
- package/dist/core/ingestion/pipeline.js +103 -39
- package/dist/core/ingestion/workers/parse-worker.d.ts +58 -0
- package/dist/core/ingestion/workers/parse-worker.js +451 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +22 -0
- package/dist/core/ingestion/workers/worker-pool.js +65 -0
- package/dist/core/kuzu/kuzu-adapter.d.ts +15 -1
- package/dist/core/kuzu/kuzu-adapter.js +177 -67
- package/dist/core/kuzu/schema.d.ts +1 -1
- package/dist/core/kuzu/schema.js +3 -0
- package/dist/core/wiki/generator.d.ts +96 -0
- package/dist/core/wiki/generator.js +674 -0
- package/dist/core/wiki/graph-queries.d.ts +80 -0
- package/dist/core/wiki/graph-queries.js +238 -0
- package/dist/core/wiki/html-viewer.d.ts +10 -0
- package/dist/core/wiki/html-viewer.js +297 -0
- package/dist/core/wiki/llm-client.d.ts +36 -0
- package/dist/core/wiki/llm-client.js +111 -0
- package/dist/core/wiki/prompts.d.ts +53 -0
- package/dist/core/wiki/prompts.js +174 -0
- package/dist/mcp/core/embedder.js +4 -2
- package/dist/mcp/core/kuzu-adapter.d.ts +2 -1
- package/dist/mcp/core/kuzu-adapter.js +35 -15
- package/dist/mcp/local/local-backend.js +9 -2
- package/dist/mcp/server.js +1 -1
- package/dist/storage/git.d.ts +0 -1
- package/dist/storage/git.js +1 -8
- package/dist/storage/repo-manager.d.ts +17 -0
- package/dist/storage/repo-manager.js +26 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,31 +18,37 @@ AI coding tools don't understand your codebase structure. They edit a function w
|
|
|
18
18
|
## Quick Start
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
#
|
|
22
|
-
|
|
21
|
+
# Index your repo (run from repo root)
|
|
22
|
+
npx gitnexus analyze
|
|
23
|
+
```
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
gitnexus setup
|
|
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
26
|
|
|
27
|
-
|
|
28
|
-
gitnexus analyze
|
|
27
|
+
To configure MCP for your editor, run `npx gitnexus setup` once — or set it up manually below.
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
```
|
|
29
|
+
`gitnexus setup` auto-detects your editors and writes the correct global MCP config. You only need to run it once.
|
|
32
30
|
|
|
33
|
-
|
|
31
|
+
### Editor Support
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
| Editor | MCP | Skills | Hooks (auto-augment) | Support |
|
|
34
|
+
|--------|-----|--------|---------------------|---------|
|
|
35
|
+
| **Claude Code** | Yes | Yes | Yes (PreToolUse) | **Full** |
|
|
36
|
+
| **Cursor** | Yes | Yes | — | MCP + Skills |
|
|
37
|
+
| **Windsurf** | Yes | — | — | MCP |
|
|
38
|
+
| **OpenCode** | Yes | Yes | — | MCP + Skills |
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
> **Claude Code** gets the deepest integration: MCP tools + agent skills + PreToolUse hooks that automatically enrich grep/glob/bash calls with knowledge graph context.
|
|
41
41
|
|
|
42
42
|
## MCP Setup (manual)
|
|
43
43
|
|
|
44
44
|
If you prefer to configure manually instead of using `gitnexus setup`:
|
|
45
45
|
|
|
46
|
+
### Claude Code (full support — MCP + skills + hooks)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
claude mcp add gitnexus -- npx -y gitnexus@latest mcp
|
|
50
|
+
```
|
|
51
|
+
|
|
46
52
|
### Cursor / Windsurf
|
|
47
53
|
|
|
48
54
|
Add to `~/.cursor/mcp.json` (global — works for all projects):
|
|
@@ -58,12 +64,6 @@ Add to `~/.cursor/mcp.json` (global — works for all projects):
|
|
|
58
64
|
}
|
|
59
65
|
```
|
|
60
66
|
|
|
61
|
-
### Claude Code
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
claude mcp add gitnexus -- npx -y gitnexus@latest mcp
|
|
65
|
-
```
|
|
66
|
-
|
|
67
67
|
### OpenCode
|
|
68
68
|
|
|
69
69
|
Add to `~/.config/opencode/config.json`:
|
|
@@ -100,68 +100,59 @@ Your AI agent gets these tools automatically:
|
|
|
100
100
|
| Tool | What It Does | `repo` Param |
|
|
101
101
|
|------|-------------|--------------|
|
|
102
102
|
| `list_repos` | Discover all indexed repositories | — |
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
103
|
+
| `query` | Process-grouped hybrid search (BM25 + semantic + RRF) | Optional |
|
|
104
|
+
| `context` | 360-degree symbol view — categorized refs, process participation | Optional |
|
|
105
|
+
| `impact` | Blast radius analysis with depth grouping and confidence | Optional |
|
|
106
|
+
| `detect_changes` | Git-diff impact — maps changed lines to affected processes | Optional |
|
|
107
|
+
| `rename` | Multi-file coordinated rename with graph + text search | Optional |
|
|
107
108
|
| `cypher` | Raw Cypher graph queries | Optional |
|
|
108
|
-
| `analyze` | Index or re-index a repository | Optional |
|
|
109
109
|
|
|
110
|
-
> With one indexed repo, the `repo` param is optional. With multiple, specify which: `
|
|
110
|
+
> With one indexed repo, the `repo` param is optional. With multiple, specify which: `query({query: "auth", repo: "my-app"})`.
|
|
111
111
|
|
|
112
112
|
## MCP Resources
|
|
113
113
|
|
|
114
114
|
| Resource | Purpose |
|
|
115
115
|
|----------|---------|
|
|
116
116
|
| `gitnexus://repos` | List all indexed repositories (read first) |
|
|
117
|
-
| `gitnexus://repo/{name}/context` | Codebase stats and
|
|
118
|
-
| `gitnexus://repo/{name}/clusters` | All functional clusters |
|
|
117
|
+
| `gitnexus://repo/{name}/context` | Codebase stats, staleness check, and available tools |
|
|
118
|
+
| `gitnexus://repo/{name}/clusters` | All functional clusters with cohesion scores |
|
|
119
119
|
| `gitnexus://repo/{name}/cluster/{name}` | Cluster members and details |
|
|
120
120
|
| `gitnexus://repo/{name}/processes` | All execution flows |
|
|
121
|
-
| `gitnexus://repo/{name}/process/{name}` | Full process trace |
|
|
121
|
+
| `gitnexus://repo/{name}/process/{name}` | Full process trace with steps |
|
|
122
122
|
| `gitnexus://repo/{name}/schema` | Graph schema for Cypher queries |
|
|
123
123
|
|
|
124
|
+
## MCP Prompts
|
|
125
|
+
|
|
126
|
+
| Prompt | What It Does |
|
|
127
|
+
|--------|-------------|
|
|
128
|
+
| `detect_impact` | Pre-commit change analysis — scope, affected processes, risk level |
|
|
129
|
+
| `generate_map` | Architecture documentation from the knowledge graph with mermaid diagrams |
|
|
130
|
+
|
|
124
131
|
## CLI Commands
|
|
125
132
|
|
|
126
133
|
```bash
|
|
127
|
-
gitnexus setup
|
|
128
|
-
gitnexus analyze [path]
|
|
129
|
-
gitnexus analyze --force
|
|
130
|
-
gitnexus
|
|
131
|
-
gitnexus
|
|
132
|
-
gitnexus
|
|
133
|
-
gitnexus
|
|
134
|
-
gitnexus
|
|
135
|
-
gitnexus clean
|
|
134
|
+
gitnexus setup # Configure MCP for your editors (one-time)
|
|
135
|
+
gitnexus analyze [path] # Index a repository (or update stale index)
|
|
136
|
+
gitnexus analyze --force # Force full re-index
|
|
137
|
+
gitnexus analyze --skip-embeddings # Skip embedding generation (faster)
|
|
138
|
+
gitnexus mcp # Start MCP server (stdio) — serves all indexed repos
|
|
139
|
+
gitnexus serve # Start HTTP server for web UI
|
|
140
|
+
gitnexus list # List all indexed repositories
|
|
141
|
+
gitnexus status # Show index status for current repo
|
|
142
|
+
gitnexus clean # Delete index for current repo
|
|
143
|
+
gitnexus clean --all --force # Delete all indexes
|
|
144
|
+
gitnexus wiki [path] # Generate LLM-powered docs from knowledge graph
|
|
145
|
+
gitnexus wiki --model <model> # Wiki with custom LLM model (default: gpt-4o-mini)
|
|
136
146
|
```
|
|
137
147
|
|
|
138
148
|
## Multi-Repo Support
|
|
139
149
|
|
|
140
|
-
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 with lazy KuzuDB connections.
|
|
150
|
+
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 with lazy KuzuDB connections (max 5 concurrent, evicted after 5 minutes idle).
|
|
141
151
|
|
|
142
152
|
## Supported Languages
|
|
143
153
|
|
|
144
154
|
TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust
|
|
145
155
|
|
|
146
|
-
## How Impact Analysis Works
|
|
147
|
-
|
|
148
|
-
```
|
|
149
|
-
gitnexus_impact({target: "UserService", direction: "upstream", repo: "my-app"})
|
|
150
|
-
|
|
151
|
-
TARGET: Class UserService (src/services/user.ts)
|
|
152
|
-
|
|
153
|
-
UPSTREAM (what depends on this):
|
|
154
|
-
Depth 1 (direct callers):
|
|
155
|
-
handleLogin [CALLS 90%] → src/api/auth.ts:45
|
|
156
|
-
handleRegister [CALLS 90%] → src/api/auth.ts:78
|
|
157
|
-
Depth 2:
|
|
158
|
-
authRouter [IMPORTS] → src/routes/auth.ts
|
|
159
|
-
|
|
160
|
-
8 files affected, 3 clusters touched
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
Options: `maxDepth`, `minConfidence`, `relationTypes`, `includeTests`
|
|
164
|
-
|
|
165
156
|
## Agent Skills
|
|
166
157
|
|
|
167
158
|
GitNexus ships with skill files that teach AI agents how to use the tools effectively:
|
|
@@ -171,7 +162,7 @@ GitNexus ships with skill files that teach AI agents how to use the tools effect
|
|
|
171
162
|
- **Impact Analysis** — Analyze blast radius before changes
|
|
172
163
|
- **Refactoring** — Plan safe refactors using dependency mapping
|
|
173
164
|
|
|
174
|
-
|
|
165
|
+
Installed automatically by both `gitnexus analyze` (per-repo) and `gitnexus setup` (global).
|
|
175
166
|
|
|
176
167
|
## Requirements
|
|
177
168
|
|
package/dist/cli/analyze.js
CHANGED
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import cliProgress from 'cli-progress';
|
|
8
8
|
import { runPipelineFromRepo } from '../core/ingestion/pipeline.js';
|
|
9
|
-
import { initKuzu, loadGraphToKuzu, getKuzuStats, executeQuery, executeWithReusedStatement, closeKuzu, createFTSIndex } from '../core/kuzu/kuzu-adapter.js';
|
|
9
|
+
import { initKuzu, loadGraphToKuzu, getKuzuStats, executeQuery, executeWithReusedStatement, closeKuzu, createFTSIndex, loadCachedEmbeddings } from '../core/kuzu/kuzu-adapter.js';
|
|
10
10
|
import { runEmbeddingPipeline } from '../core/embeddings/embedding-pipeline.js';
|
|
11
11
|
import { disposeEmbedder } from '../core/embeddings/embedder.js';
|
|
12
|
-
import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath
|
|
12
|
+
import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath } from '../storage/repo-manager.js';
|
|
13
13
|
import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js';
|
|
14
14
|
import { generateAIContextFiles } from './ai-context.js';
|
|
15
15
|
import fs from 'fs/promises';
|
|
16
16
|
import { registerClaudeHook } from './claude-hooks.js';
|
|
17
|
+
/** Threshold: auto-skip embeddings for repos with more nodes than this */
|
|
18
|
+
const EMBEDDING_NODE_LIMIT = 50_000;
|
|
17
19
|
const PHASE_LABELS = {
|
|
18
20
|
extracting: 'Scanning files',
|
|
19
21
|
structure: 'Building structure',
|
|
@@ -23,7 +25,11 @@ const PHASE_LABELS = {
|
|
|
23
25
|
heritage: 'Extracting inheritance',
|
|
24
26
|
communities: 'Detecting communities',
|
|
25
27
|
processes: 'Detecting processes',
|
|
26
|
-
complete: '
|
|
28
|
+
complete: 'Pipeline complete',
|
|
29
|
+
kuzu: 'Loading into KuzuDB',
|
|
30
|
+
fts: 'Creating search indexes',
|
|
31
|
+
embeddings: 'Generating embeddings',
|
|
32
|
+
done: 'Done',
|
|
27
33
|
};
|
|
28
34
|
export const analyzeCommand = async (inputPath, options) => {
|
|
29
35
|
console.log('\n GitNexus Analyzer\n');
|
|
@@ -34,14 +40,14 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
34
40
|
else {
|
|
35
41
|
const gitRoot = getGitRoot(process.cwd());
|
|
36
42
|
if (!gitRoot) {
|
|
37
|
-
console.log('
|
|
43
|
+
console.log(' Not inside a git repository\n');
|
|
38
44
|
process.exitCode = 1;
|
|
39
45
|
return;
|
|
40
46
|
}
|
|
41
47
|
repoPath = gitRoot;
|
|
42
48
|
}
|
|
43
49
|
if (!isGitRepo(repoPath)) {
|
|
44
|
-
console.log('
|
|
50
|
+
console.log(' Not a git repository\n');
|
|
45
51
|
process.exitCode = 1;
|
|
46
52
|
return;
|
|
47
53
|
}
|
|
@@ -49,35 +55,70 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
49
55
|
const currentCommit = getCurrentCommit(repoPath);
|
|
50
56
|
const existingMeta = await loadMeta(storagePath);
|
|
51
57
|
if (existingMeta && !options?.force && existingMeta.lastCommit === currentCommit) {
|
|
52
|
-
console.log('
|
|
58
|
+
console.log(' Already up to date\n');
|
|
53
59
|
return;
|
|
54
60
|
}
|
|
55
|
-
|
|
61
|
+
// Single progress bar for entire pipeline
|
|
62
|
+
const bar = new cliProgress.SingleBar({
|
|
56
63
|
format: ' {bar} {percentage}% | {phase}',
|
|
57
|
-
barCompleteChar: '
|
|
58
|
-
barIncompleteChar: '
|
|
64
|
+
barCompleteChar: '\u2588',
|
|
65
|
+
barIncompleteChar: '\u2591',
|
|
59
66
|
hideCursor: true,
|
|
60
67
|
barGlue: '',
|
|
61
68
|
autopadding: true,
|
|
69
|
+
clearOnComplete: false,
|
|
70
|
+
stopOnComplete: false,
|
|
62
71
|
}, cliProgress.Presets.shades_grey);
|
|
63
|
-
|
|
72
|
+
bar.start(100, 0, { phase: 'Initializing...' });
|
|
73
|
+
const t0Global = Date.now();
|
|
74
|
+
// ── Cache embeddings from existing index before rebuild ────────────
|
|
75
|
+
let cachedEmbeddingNodeIds = new Set();
|
|
76
|
+
let cachedEmbeddings = [];
|
|
77
|
+
if (existingMeta && !options?.force) {
|
|
78
|
+
try {
|
|
79
|
+
bar.update(0, { phase: 'Caching embeddings...' });
|
|
80
|
+
await initKuzu(kuzuPath);
|
|
81
|
+
const cached = await loadCachedEmbeddings();
|
|
82
|
+
cachedEmbeddingNodeIds = cached.embeddingNodeIds;
|
|
83
|
+
cachedEmbeddings = cached.embeddings;
|
|
84
|
+
await closeKuzu();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
try {
|
|
88
|
+
await closeKuzu();
|
|
89
|
+
}
|
|
90
|
+
catch { }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// ── Phase 1: Full Pipeline (0–60%) ─────────────────────────────────
|
|
64
94
|
const pipelineResult = await runPipelineFromRepo(repoPath, (progress) => {
|
|
65
95
|
const phaseLabel = PHASE_LABELS[progress.phase] || progress.phase;
|
|
66
|
-
|
|
96
|
+
const scaled = Math.round(progress.percent * 0.6);
|
|
97
|
+
bar.update(scaled, { phase: phaseLabel });
|
|
67
98
|
});
|
|
68
|
-
|
|
99
|
+
// ── Phase 2: KuzuDB (60–85%) ──────────────────────────────────────
|
|
100
|
+
bar.update(60, { phase: 'Loading into KuzuDB...' });
|
|
69
101
|
await closeKuzu();
|
|
70
|
-
const fsClean = await import('fs/promises');
|
|
71
102
|
const kuzuFiles = [kuzuPath, `${kuzuPath}.wal`, `${kuzuPath}.lock`];
|
|
72
103
|
for (const f of kuzuFiles) {
|
|
73
104
|
try {
|
|
74
|
-
await
|
|
105
|
+
await fs.rm(f, { recursive: true, force: true });
|
|
75
106
|
}
|
|
76
|
-
catch {
|
|
107
|
+
catch { }
|
|
77
108
|
}
|
|
109
|
+
const t0Kuzu = Date.now();
|
|
78
110
|
await initKuzu(kuzuPath);
|
|
79
|
-
|
|
80
|
-
|
|
111
|
+
let kuzuMsgCount = 0;
|
|
112
|
+
const kuzuResult = await loadGraphToKuzu(pipelineResult.graph, pipelineResult.fileContents, storagePath, (msg) => {
|
|
113
|
+
kuzuMsgCount++;
|
|
114
|
+
const progress = Math.min(84, 60 + Math.round((kuzuMsgCount / (kuzuMsgCount + 10)) * 24));
|
|
115
|
+
bar.update(progress, { phase: msg });
|
|
116
|
+
});
|
|
117
|
+
const kuzuTime = ((Date.now() - t0Kuzu) / 1000).toFixed(1);
|
|
118
|
+
const kuzuWarnings = kuzuResult.warnings;
|
|
119
|
+
// ── Phase 3: FTS (85–90%) ─────────────────────────────────────────
|
|
120
|
+
bar.update(85, { phase: 'Creating search indexes...' });
|
|
121
|
+
const t0Fts = Date.now();
|
|
81
122
|
try {
|
|
82
123
|
await createFTSIndex('File', 'file_fts', ['name', 'content']);
|
|
83
124
|
await createFTSIndex('Function', 'function_fts', ['name', 'content']);
|
|
@@ -86,15 +127,47 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
86
127
|
await createFTSIndex('Interface', 'interface_fts', ['name', 'content']);
|
|
87
128
|
}
|
|
88
129
|
catch (e) {
|
|
89
|
-
|
|
130
|
+
// Non-fatal — FTS is best-effort
|
|
90
131
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
132
|
+
const ftsTime = ((Date.now() - t0Fts) / 1000).toFixed(1);
|
|
133
|
+
// ── Phase 3.5: Re-insert cached embeddings ────────────────────────
|
|
134
|
+
if (cachedEmbeddings.length > 0) {
|
|
135
|
+
bar.update(88, { phase: `Restoring ${cachedEmbeddings.length} cached embeddings...` });
|
|
136
|
+
const EMBED_BATCH = 200;
|
|
137
|
+
for (let i = 0; i < cachedEmbeddings.length; i += EMBED_BATCH) {
|
|
138
|
+
const batch = cachedEmbeddings.slice(i, i + EMBED_BATCH);
|
|
139
|
+
const paramsList = batch.map(e => ({ nodeId: e.nodeId, embedding: e.embedding }));
|
|
140
|
+
try {
|
|
141
|
+
await executeWithReusedStatement(`CREATE (e:CodeEmbedding {nodeId: $nodeId, embedding: $embedding})`, paramsList);
|
|
142
|
+
}
|
|
143
|
+
catch { /* some may fail if node was removed, that's fine */ }
|
|
144
|
+
}
|
|
96
145
|
}
|
|
146
|
+
// ── Phase 4: Embeddings (90–98%) ──────────────────────────────────
|
|
97
147
|
const stats = await getKuzuStats();
|
|
148
|
+
let embeddingTime = '0.0';
|
|
149
|
+
let embeddingSkipped = false;
|
|
150
|
+
let embeddingSkipReason = '';
|
|
151
|
+
if (options?.skipEmbeddings) {
|
|
152
|
+
embeddingSkipped = true;
|
|
153
|
+
embeddingSkipReason = 'skipped (--skip-embeddings)';
|
|
154
|
+
}
|
|
155
|
+
else if (stats.nodes > EMBEDDING_NODE_LIMIT) {
|
|
156
|
+
embeddingSkipped = true;
|
|
157
|
+
embeddingSkipReason = `skipped (${stats.nodes.toLocaleString()} nodes > ${EMBEDDING_NODE_LIMIT.toLocaleString()} limit)`;
|
|
158
|
+
}
|
|
159
|
+
if (!embeddingSkipped) {
|
|
160
|
+
bar.update(90, { phase: 'Loading embedding model...' });
|
|
161
|
+
const t0Emb = Date.now();
|
|
162
|
+
await runEmbeddingPipeline(executeQuery, executeWithReusedStatement, (progress) => {
|
|
163
|
+
const scaled = 90 + Math.round((progress.percent / 100) * 8);
|
|
164
|
+
const label = progress.phase === 'loading-model' ? 'Loading embedding model...' : `Embedding ${progress.nodesProcessed || 0}/${progress.totalNodes || '?'}`;
|
|
165
|
+
bar.update(scaled, { phase: label });
|
|
166
|
+
}, {}, cachedEmbeddingNodeIds.size > 0 ? cachedEmbeddingNodeIds : undefined);
|
|
167
|
+
embeddingTime = ((Date.now() - t0Emb) / 1000).toFixed(1);
|
|
168
|
+
}
|
|
169
|
+
// ── Phase 5: Finalize (98–100%) ───────────────────────────────────
|
|
170
|
+
bar.update(98, { phase: 'Saving metadata...' });
|
|
98
171
|
const meta = {
|
|
99
172
|
repoPath,
|
|
100
173
|
lastCommit: currentCommit,
|
|
@@ -110,7 +183,6 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
110
183
|
await saveMeta(storagePath, meta);
|
|
111
184
|
await registerRepo(repoPath, meta);
|
|
112
185
|
await addToGitignore(repoPath);
|
|
113
|
-
// Auto-register Claude Code hook (idempotent)
|
|
114
186
|
const hookResult = await registerClaudeHook();
|
|
115
187
|
const projectName = path.basename(repoPath);
|
|
116
188
|
let aggregatedClusterCount = 0;
|
|
@@ -132,17 +204,27 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
132
204
|
});
|
|
133
205
|
await closeKuzu();
|
|
134
206
|
await disposeEmbedder();
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
console.log(
|
|
207
|
+
const totalTime = ((Date.now() - t0Global) / 1000).toFixed(1);
|
|
208
|
+
bar.update(100, { phase: 'Done' });
|
|
209
|
+
bar.stop();
|
|
210
|
+
// ── Summary ───────────────────────────────────────────────────────
|
|
211
|
+
const embeddingsCached = cachedEmbeddings.length > 0;
|
|
212
|
+
console.log(`\n Repository indexed successfully (${totalTime}s)${embeddingsCached ? ` [${cachedEmbeddings.length} embeddings cached]` : ''}\n`);
|
|
213
|
+
console.log(` ${stats.nodes.toLocaleString()} nodes | ${stats.edges.toLocaleString()} edges | ${pipelineResult.communityResult?.stats.totalCommunities || 0} clusters | ${pipelineResult.processResult?.stats.totalProcesses || 0} flows`);
|
|
214
|
+
console.log(` KuzuDB ${kuzuTime}s | FTS ${ftsTime}s | Embeddings ${embeddingSkipped ? embeddingSkipReason : embeddingTime + 's'}`);
|
|
215
|
+
console.log(` ${repoPath}`);
|
|
141
216
|
if (aiContext.files.length > 0) {
|
|
142
|
-
console.log(` Context:
|
|
217
|
+
console.log(` Context: ${aiContext.files.join(', ')}`);
|
|
143
218
|
}
|
|
144
219
|
if (hookResult.registered) {
|
|
145
|
-
console.log(` Hooks:
|
|
220
|
+
console.log(` Hooks: ${hookResult.message}`);
|
|
221
|
+
}
|
|
222
|
+
// Show warnings (missing schema pairs, etc.) after the clean output
|
|
223
|
+
if (kuzuWarnings.length > 0) {
|
|
224
|
+
console.log(`\n Warnings (${kuzuWarnings.length}):`);
|
|
225
|
+
for (const w of kuzuWarnings) {
|
|
226
|
+
console.log(` ${w}`);
|
|
227
|
+
}
|
|
146
228
|
}
|
|
147
229
|
try {
|
|
148
230
|
await fs.access(getGlobalRegistryPath());
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eval Server — Lightweight HTTP server for SWE-bench evaluation
|
|
3
|
+
*
|
|
4
|
+
* Keeps KuzuDB warm in memory so tool calls from the agent are near-instant.
|
|
5
|
+
* Designed to run inside Docker containers during SWE-bench evaluation.
|
|
6
|
+
*
|
|
7
|
+
* KEY DESIGN: Returns LLM-friendly text, not raw JSON.
|
|
8
|
+
* Raw JSON wastes tokens and is hard for models to parse. The text formatter
|
|
9
|
+
* converts structured results into compact, readable output that models
|
|
10
|
+
* can immediately act on. Next-step hints guide the agent through a
|
|
11
|
+
* productive tool-chaining workflow (query → context → impact → fix).
|
|
12
|
+
*
|
|
13
|
+
* Architecture:
|
|
14
|
+
* Agent bash cmd → curl localhost:PORT/tool/query → eval-server → LocalBackend → format → text
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* gitnexus eval-server # default port 4848
|
|
18
|
+
* gitnexus eval-server --port 4848 # explicit port
|
|
19
|
+
* gitnexus eval-server --idle-timeout 300 # auto-shutdown after 300s idle
|
|
20
|
+
*
|
|
21
|
+
* API:
|
|
22
|
+
* POST /tool/:name — Call a tool. Body is JSON arguments. Returns formatted text.
|
|
23
|
+
* GET /health — Health check. Returns {"status":"ok","repos":[...]}
|
|
24
|
+
* POST /shutdown — Graceful shutdown.
|
|
25
|
+
*/
|
|
26
|
+
export interface EvalServerOptions {
|
|
27
|
+
port?: string;
|
|
28
|
+
idleTimeout?: string;
|
|
29
|
+
}
|
|
30
|
+
export declare function evalServerCommand(options?: EvalServerOptions): Promise<void>;
|