gitnexus 1.6.7 → 1.6.8-rc.10
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 +20 -0
- package/dist/cli/analyze-config.js +39 -0
- package/dist/cli/analyze.d.ts +16 -0
- package/dist/cli/analyze.js +34 -6
- package/dist/cli/clean.d.ts +1 -0
- package/dist/cli/clean.js +43 -1
- package/dist/cli/eval-server.js +43 -0
- package/dist/cli/help-i18n.js +6 -0
- package/dist/cli/i18n/en.d.ts +11 -0
- package/dist/cli/i18n/en.js +11 -0
- package/dist/cli/i18n/resources.d.ts +22 -0
- package/dist/cli/i18n/zh-CN.d.ts +11 -0
- package/dist/cli/i18n/zh-CN.js +11 -0
- package/dist/cli/index.js +9 -0
- package/dist/cli/list.js +12 -0
- package/dist/cli/optional-grammars.d.ts +6 -8
- package/dist/cli/optional-grammars.js +8 -11
- package/dist/cli/status.js +26 -5
- package/dist/cli/tool.d.ts +5 -0
- package/dist/cli/tool.js +5 -0
- package/dist/core/embeddings/embedder.js +4 -0
- package/dist/core/embeddings/embedding-pipeline.js +27 -16
- package/dist/core/embeddings/onnxruntime-common-resolver.d.ts +6 -0
- package/dist/core/embeddings/onnxruntime-common-resolver.js +130 -0
- package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
- package/dist/core/group/extractors/http-patterns/java.js +3 -50
- package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
- package/dist/core/group/extractors/include-extractor.js +7 -7
- package/dist/core/ingestion/language-provider.d.ts +25 -2
- package/dist/core/ingestion/languages/c-cpp.js +11 -4
- package/dist/core/ingestion/languages/java.js +3 -0
- package/dist/core/ingestion/languages/kotlin/query.js +3 -2
- package/dist/core/ingestion/languages/kotlin.js +5 -1
- package/dist/core/ingestion/parsing-processor.js +21 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +64 -14
- package/dist/core/ingestion/pipeline.d.ts +9 -0
- package/dist/core/ingestion/route-extractors/spring-shared.d.ts +50 -0
- package/dist/core/ingestion/route-extractors/spring-shared.js +80 -0
- package/dist/core/ingestion/route-extractors/spring.d.ts +35 -0
- package/dist/core/ingestion/route-extractors/spring.js +136 -0
- package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
- package/dist/core/ingestion/workers/clone-safety.js +465 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +10 -0
- package/dist/core/ingestion/workers/parse-worker.js +27 -52
- package/dist/core/ingestion/workers/post-result.d.ts +22 -0
- package/dist/core/ingestion/workers/post-result.js +87 -0
- package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
- package/dist/core/ingestion/workers/result-merge.js +43 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +6 -0
- package/dist/core/ingestion/workers/worker-pool.js +28 -14
- package/dist/core/lbug/lbug-adapter.d.ts +19 -0
- package/dist/core/lbug/lbug-adapter.js +56 -1
- package/dist/core/run-analyze.d.ts +45 -0
- package/dist/core/run-analyze.js +175 -24
- package/dist/core/tree-sitter/parser-loader.js +5 -4
- package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
- package/dist/core/tree-sitter/vendored-grammars.js +57 -0
- package/dist/mcp/core/embedder.js +4 -0
- package/dist/mcp/local/local-backend.d.ts +55 -2
- package/dist/mcp/local/local-backend.js +363 -25
- package/dist/mcp/tools.js +32 -0
- package/dist/server/analyze-worker-ipc.d.ts +58 -0
- package/dist/server/analyze-worker-ipc.js +16 -0
- package/dist/server/analyze-worker.js +7 -1
- package/dist/storage/branch-index.d.ts +52 -0
- package/dist/storage/branch-index.js +65 -0
- package/dist/storage/git.d.ts +11 -0
- package/dist/storage/git.js +28 -0
- package/dist/storage/parse-cache.js +3 -0
- package/dist/storage/repo-manager.d.ts +57 -2
- package/dist/storage/repo-manager.js +132 -18
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +24 -4
- package/hooks/claude/gitnexus-hook.cjs +18 -3
- package/package.json +2 -2
- package/scripts/assert-publish-grammar-coverage.cjs +31 -0
- package/scripts/build-tree-sitter-grammars.cjs +16 -10
- package/vendor/tree-sitter-c/package.json +1 -1
- package/vendor/tree-sitter-dart/package.json +1 -1
- package/vendor/tree-sitter-kotlin/package.json +1 -1
- package/vendor/tree-sitter-proto/package.json +1 -1
- package/vendor/tree-sitter-swift/package.json +1 -1
- package/scripts/materialize-vendor-grammars.cjs +0 -97
package/dist/cli/index.js
CHANGED
|
@@ -34,6 +34,9 @@ program
|
|
|
34
34
|
.option('--skip-agents-md', 'Skip updating the gitnexus section in AGENTS.md and CLAUDE.md')
|
|
35
35
|
.option('--default-branch <branch>', 'Default branch used in the generated regression-compare example (base_ref). ' +
|
|
36
36
|
'Falls back to .gitnexusrc, then auto-detected origin/HEAD, then "main".')
|
|
37
|
+
.option('--branch <name>', 'Index the working tree under a specific branch slot (multi-branch indexing). ' +
|
|
38
|
+
'Defaults to the checked-out branch; the primary/first-indexed branch keeps the ' +
|
|
39
|
+
'flat index and others get their own. Distinct from --default-branch (cosmetic base_ref).')
|
|
37
40
|
.option('--no-stats', 'Omit volatile file/symbol counts from AGENTS.md and CLAUDE.md')
|
|
38
41
|
.option('--skip-skills', 'Skip installing standard GitNexus skill files under .claude/skills/gitnexus/. ' +
|
|
39
42
|
'Does not suppress community skills from --skills (those use .claude/skills/generated/). ' +
|
|
@@ -89,6 +92,7 @@ program
|
|
|
89
92
|
.description('Delete GitNexus index for current repo')
|
|
90
93
|
.option('-f, --force', 'Skip confirmation prompt')
|
|
91
94
|
.option('--all', 'Clean all indexed repos')
|
|
95
|
+
.option('--branch <name>', 'Delete only the named branch index (not the primary)')
|
|
92
96
|
.option('--lbug-sidecars', 'Clean quarantined LadybugDB missing-shadow WAL sidecars')
|
|
93
97
|
.action(createLazyAction(() => import('./clean.js'), 'cleanCommand'));
|
|
94
98
|
program
|
|
@@ -135,6 +139,7 @@ program
|
|
|
135
139
|
.command('query <search_query>')
|
|
136
140
|
.description('Search the knowledge graph for execution flows related to a concept')
|
|
137
141
|
.option('-r, --repo <name>', 'Target repository (omit if only one indexed)')
|
|
142
|
+
.option('--branch <name>', 'Scope to a specific branch index (multi-branch repos)')
|
|
138
143
|
.option('-c, --context <text>', 'Task context to improve ranking')
|
|
139
144
|
.option('-g, --goal <text>', 'What you want to find')
|
|
140
145
|
.option('-l, --limit <n>', 'Max processes to return (default: 5)')
|
|
@@ -144,6 +149,7 @@ program
|
|
|
144
149
|
.command('context [name]')
|
|
145
150
|
.description('360-degree view of a code symbol: callers, callees, processes')
|
|
146
151
|
.option('-r, --repo <name>', 'Target repository')
|
|
152
|
+
.option('--branch <name>', 'Scope to a specific branch index (multi-branch repos)')
|
|
147
153
|
.option('-u, --uid <uid>', 'Direct symbol UID (zero-ambiguity lookup)')
|
|
148
154
|
.option('-f, --file <path>', 'File path to disambiguate common names')
|
|
149
155
|
.option('--content', 'Include full symbol source code')
|
|
@@ -153,6 +159,7 @@ program
|
|
|
153
159
|
.description('Blast radius analysis: what breaks if you change a symbol')
|
|
154
160
|
.option('-d, --direction <dir>', 'upstream (dependants) or downstream (dependencies)', 'upstream')
|
|
155
161
|
.option('-r, --repo <name>', 'Target repository')
|
|
162
|
+
.option('--branch <name>', 'Scope to a specific branch index (multi-branch repos)')
|
|
156
163
|
.option('-u, --uid <uid>', 'Direct symbol UID (zero-ambiguity lookup)')
|
|
157
164
|
.option('-f, --file <path>', 'File path to disambiguate common names')
|
|
158
165
|
.option('--kind <kind>', 'Kind filter to disambiguate common names (e.g. Function, Class, Method)')
|
|
@@ -166,6 +173,7 @@ program
|
|
|
166
173
|
.command('cypher <query>')
|
|
167
174
|
.description('Execute raw Cypher query against the knowledge graph')
|
|
168
175
|
.option('-r, --repo <name>', 'Target repository')
|
|
176
|
+
.option('--branch <name>', 'Scope to a specific branch index (multi-branch repos)')
|
|
169
177
|
.action(createLbugLazyAction(() => import('./tool.js'), 'cypherCommand'));
|
|
170
178
|
program
|
|
171
179
|
.command('detect-changes')
|
|
@@ -174,6 +182,7 @@ program
|
|
|
174
182
|
.option('-s, --scope <scope>', 'What to analyze: unstaged, staged, all, or compare', 'unstaged')
|
|
175
183
|
.option('-b, --base-ref <ref>', 'Branch/commit for compare scope (e.g. main)')
|
|
176
184
|
.option('-r, --repo <name>', 'Target repository')
|
|
185
|
+
.option('--branch <name>', 'Scope to a specific branch index (multi-branch repos)')
|
|
177
186
|
.action(createLbugLazyAction(() => import('./tool.js'), 'detectChangesCommand'));
|
|
178
187
|
// ─── Eval Server (persistent daemon for SWE-bench) ─────────────────
|
|
179
188
|
program
|
package/dist/cli/list.js
CHANGED
|
@@ -31,6 +31,8 @@ export const listCommand = async () => {
|
|
|
31
31
|
console.log(` ${t('common.path')}: ${entry.path}`);
|
|
32
32
|
console.log(` ${t('list.indexed')}: ${indexedDate}`);
|
|
33
33
|
console.log(` ${t('list.commit')}: ${commitShort}`);
|
|
34
|
+
if (entry.branch)
|
|
35
|
+
console.log(` ${t('list.branch')}: ${entry.branch}`);
|
|
34
36
|
console.log(` ${t('list.stats')}: ${t('list.statsValue', {
|
|
35
37
|
files: stats.files ?? 0,
|
|
36
38
|
symbols: stats.nodes ?? 0,
|
|
@@ -40,6 +42,16 @@ export const listCommand = async () => {
|
|
|
40
42
|
console.log(` ${t('list.clusters')}: ${stats.communities}`);
|
|
41
43
|
if (stats.processes)
|
|
42
44
|
console.log(` ${t('list.processes')}: ${stats.processes}`);
|
|
45
|
+
// Per-branch indexes (#2106). Only rendered when extra branches were
|
|
46
|
+
// indexed for this path, so single-branch output is unchanged.
|
|
47
|
+
if (entry.branches && entry.branches.length > 0) {
|
|
48
|
+
console.log(` ${t('list.branchIndexes')}:`);
|
|
49
|
+
for (const b of entry.branches) {
|
|
50
|
+
const bCommit = b.lastCommit?.slice(0, 7) || t('list.unknown');
|
|
51
|
+
const bIndexed = new Date(b.indexedAt).toLocaleString();
|
|
52
|
+
console.log(` ${t('list.branchLine', { branch: b.branch, commit: bCommit, indexed: bIndexed })}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
43
55
|
console.log('');
|
|
44
56
|
}
|
|
45
57
|
};
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Optional grammar availability check.
|
|
3
3
|
*
|
|
4
|
-
* tree-sitter-dart,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* prebuilds activated via node-gyp-build.
|
|
8
|
-
* optionalDependency (not vendored). All can be skipped via
|
|
4
|
+
* tree-sitter-dart, -proto, -swift, and -kotlin are vendored under vendor/ and
|
|
5
|
+
* loaded from there by absolute path (NEVER copied into node_modules — see
|
|
6
|
+
* core/tree-sitter/vendored-grammars.ts / #2111). Each ships committed platform
|
|
7
|
+
* prebuilds activated via node-gyp-build. All can be skipped via
|
|
9
8
|
* GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1 (postinstall scripts), or can silently
|
|
10
|
-
* soft-fail when the
|
|
11
|
-
*
|
|
12
|
-
* skipped or its native build failed (Kotlin).
|
|
9
|
+
* soft-fail when no prebuild matches the host platform (and a source build was
|
|
10
|
+
* unavailable / not attempted).
|
|
13
11
|
*
|
|
14
12
|
* Either path produces the same observable: the .node binding is absent
|
|
15
13
|
* at runtime. This helper detects that condition and surfaces a single
|
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Optional grammar availability check.
|
|
3
3
|
*
|
|
4
|
-
* tree-sitter-dart,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* prebuilds activated via node-gyp-build.
|
|
8
|
-
* optionalDependency (not vendored). All can be skipped via
|
|
4
|
+
* tree-sitter-dart, -proto, -swift, and -kotlin are vendored under vendor/ and
|
|
5
|
+
* loaded from there by absolute path (NEVER copied into node_modules — see
|
|
6
|
+
* core/tree-sitter/vendored-grammars.ts / #2111). Each ships committed platform
|
|
7
|
+
* prebuilds activated via node-gyp-build. All can be skipped via
|
|
9
8
|
* GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1 (postinstall scripts), or can silently
|
|
10
|
-
* soft-fail when the
|
|
11
|
-
*
|
|
12
|
-
* skipped or its native build failed (Kotlin).
|
|
9
|
+
* soft-fail when no prebuild matches the host platform (and a source build was
|
|
10
|
+
* unavailable / not attempted).
|
|
13
11
|
*
|
|
14
12
|
* Either path produces the same observable: the .node binding is absent
|
|
15
13
|
* at runtime. This helper detects that condition and surfaces a single
|
|
16
14
|
* stderr line per missing grammar so users learn why .dart/.proto/.swift/.kt
|
|
17
15
|
* support is unavailable instead of silently getting a degraded index.
|
|
18
16
|
*/
|
|
19
|
-
import { createRequire } from 'module';
|
|
20
17
|
import { SupportedLanguages } from '../_shared/index.js';
|
|
21
18
|
import { isGrammarRuntimeSkipped } from '../core/tree-sitter/parser-loader.js';
|
|
19
|
+
import { requireVendoredGrammar } from '../core/tree-sitter/vendored-grammars.js';
|
|
22
20
|
import { cliWarn } from './cli-message.js';
|
|
23
|
-
const _require = createRequire(import.meta.url);
|
|
24
21
|
const OPTIONAL_GRAMMARS = [
|
|
25
22
|
{
|
|
26
23
|
name: 'tree-sitter-dart',
|
|
@@ -76,7 +73,7 @@ export function detectMissingOptionalGrammars() {
|
|
|
76
73
|
continue;
|
|
77
74
|
}
|
|
78
75
|
try {
|
|
79
|
-
|
|
76
|
+
requireVendoredGrammar(g.pkg);
|
|
80
77
|
}
|
|
81
78
|
catch (err) {
|
|
82
79
|
const code = err?.code;
|
package/dist/cli/status.js
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Shows the indexing status of the current repository.
|
|
5
5
|
*/
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { findRepo, getStoragePaths, loadMeta, hasKuzuIndex } from '../storage/repo-manager.js';
|
|
8
|
+
import { getCurrentCommit, getCurrentBranch, isGitRepo, getGitRoot } from '../storage/git.js';
|
|
8
9
|
import { t } from './i18n/index.js';
|
|
9
10
|
export const statusCommand = async () => {
|
|
10
11
|
const cwd = process.cwd();
|
|
@@ -28,10 +29,30 @@ export const statusCommand = async () => {
|
|
|
28
29
|
return;
|
|
29
30
|
}
|
|
30
31
|
const currentCommit = getCurrentCommit(repo.repoPath);
|
|
31
|
-
const
|
|
32
|
+
const currentBranch = getCurrentBranch(repo.repoPath);
|
|
33
|
+
// Pick the index matching the checked-out branch (#2106). The flat index
|
|
34
|
+
// belongs to the primary branch (repo.meta.branch); when the current branch
|
|
35
|
+
// differs and has its own index, report that one. Legacy/no-branch metas and
|
|
36
|
+
// detached HEAD fall through to the flat index (unchanged behavior).
|
|
37
|
+
let activeMeta = repo.meta;
|
|
38
|
+
let currentBranchIndexed = true;
|
|
39
|
+
if (currentBranch && repo.meta.branch && currentBranch !== repo.meta.branch) {
|
|
40
|
+
const { metaPath } = getStoragePaths(repo.repoPath, currentBranch);
|
|
41
|
+
const branchMeta = await loadMeta(path.dirname(metaPath));
|
|
42
|
+
if (branchMeta)
|
|
43
|
+
activeMeta = branchMeta;
|
|
44
|
+
else
|
|
45
|
+
currentBranchIndexed = false;
|
|
46
|
+
}
|
|
32
47
|
console.log(`${t('status.repository')}: ${repo.repoPath}`);
|
|
33
|
-
console.log(`${t('status.
|
|
34
|
-
|
|
48
|
+
console.log(`${t('status.branch')}: ${currentBranch ?? t('status.detached')}`);
|
|
49
|
+
if (!currentBranchIndexed) {
|
|
50
|
+
console.log(`${t('status.status')}: ${t('status.branchNotIndexed', { primary: repo.meta.branch ?? '' })}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const isUpToDate = currentCommit === activeMeta.lastCommit;
|
|
54
|
+
console.log(`${t('status.indexed')}: ${new Date(activeMeta.indexedAt).toLocaleString()}`);
|
|
55
|
+
console.log(`${t('status.indexedCommit')}: ${activeMeta.lastCommit?.slice(0, 7)}`);
|
|
35
56
|
console.log(`${t('status.currentCommit')}: ${currentCommit?.slice(0, 7)}`);
|
|
36
57
|
console.log(`${t('status.status')}: ${isUpToDate ? t('status.upToDate') : t('status.stale')}`);
|
|
37
58
|
};
|
package/dist/cli/tool.d.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
export declare function queryCommand(queryText: string, options?: {
|
|
18
18
|
repo?: string;
|
|
19
|
+
branch?: string;
|
|
19
20
|
context?: string;
|
|
20
21
|
goal?: string;
|
|
21
22
|
limit?: string;
|
|
@@ -23,6 +24,7 @@ export declare function queryCommand(queryText: string, options?: {
|
|
|
23
24
|
}): Promise<void>;
|
|
24
25
|
export declare function contextCommand(name: string, options?: {
|
|
25
26
|
repo?: string;
|
|
27
|
+
branch?: string;
|
|
26
28
|
file?: string;
|
|
27
29
|
uid?: string;
|
|
28
30
|
content?: boolean;
|
|
@@ -30,6 +32,7 @@ export declare function contextCommand(name: string, options?: {
|
|
|
30
32
|
export declare function impactCommand(target?: string, options?: {
|
|
31
33
|
direction?: string;
|
|
32
34
|
repo?: string;
|
|
35
|
+
branch?: string;
|
|
33
36
|
uid?: string;
|
|
34
37
|
file?: string;
|
|
35
38
|
kind?: string;
|
|
@@ -41,9 +44,11 @@ export declare function impactCommand(target?: string, options?: {
|
|
|
41
44
|
}): Promise<void>;
|
|
42
45
|
export declare function cypherCommand(query: string, options?: {
|
|
43
46
|
repo?: string;
|
|
47
|
+
branch?: string;
|
|
44
48
|
}): Promise<void>;
|
|
45
49
|
export declare function detectChangesCommand(options?: {
|
|
46
50
|
scope?: string;
|
|
47
51
|
baseRef?: string;
|
|
48
52
|
repo?: string;
|
|
53
|
+
branch?: string;
|
|
49
54
|
}): Promise<void>;
|
package/dist/cli/tool.js
CHANGED
|
@@ -68,6 +68,7 @@ export async function queryCommand(queryText, options) {
|
|
|
68
68
|
limit: options?.limit ? parseInt(options.limit) : undefined,
|
|
69
69
|
include_content: options?.content ?? false,
|
|
70
70
|
repo: options?.repo,
|
|
71
|
+
branch: options?.branch,
|
|
71
72
|
});
|
|
72
73
|
output(result);
|
|
73
74
|
}
|
|
@@ -88,6 +89,7 @@ export async function contextCommand(name, options) {
|
|
|
88
89
|
file_path: options?.file,
|
|
89
90
|
include_content: options?.content ?? false,
|
|
90
91
|
repo: options?.repo,
|
|
92
|
+
branch: options?.branch,
|
|
91
93
|
});
|
|
92
94
|
output(result);
|
|
93
95
|
}
|
|
@@ -126,6 +128,7 @@ export async function impactCommand(target, options) {
|
|
|
126
128
|
maxDepth: options?.depth ? parseInt(options.depth, 10) : undefined,
|
|
127
129
|
includeTests: options?.includeTests ?? false,
|
|
128
130
|
repo: options?.repo,
|
|
131
|
+
branch: options?.branch,
|
|
129
132
|
limit: parsedLimit,
|
|
130
133
|
offset: parsedOffset,
|
|
131
134
|
summaryOnly: options?.summaryOnly ?? undefined,
|
|
@@ -153,6 +156,7 @@ export async function cypherCommand(query, options) {
|
|
|
153
156
|
const result = await backend.callTool('cypher', {
|
|
154
157
|
query,
|
|
155
158
|
repo: options?.repo,
|
|
159
|
+
branch: options?.branch,
|
|
156
160
|
});
|
|
157
161
|
output(result);
|
|
158
162
|
}
|
|
@@ -162,6 +166,7 @@ export async function detectChangesCommand(options) {
|
|
|
162
166
|
scope: options?.scope || 'unstaged',
|
|
163
167
|
base_ref: options?.baseRef,
|
|
164
168
|
repo: options?.repo,
|
|
169
|
+
branch: options?.branch,
|
|
165
170
|
});
|
|
166
171
|
output(formatDetectChangesResult(result));
|
|
167
172
|
}
|
|
@@ -21,6 +21,7 @@ import { isHttpMode, getHttpDimensions, httpEmbed } from './http-client.js';
|
|
|
21
21
|
import { resolveEmbeddingConfig } from './config.js';
|
|
22
22
|
import { applyHfEnvOverrides, isHfDownloadFailure, withHfDownloadRetry } from './hf-env.js';
|
|
23
23
|
import { getLocalEmbeddingRuntimeBlocker } from './runtime-support.js';
|
|
24
|
+
import { ensureOnnxRuntimeCommonResolvable } from './onnxruntime-common-resolver.js';
|
|
24
25
|
import { logger } from '../logger.js';
|
|
25
26
|
/**
|
|
26
27
|
* Check whether the onnxruntime-node package that @huggingface/transformers
|
|
@@ -147,6 +148,9 @@ export const initEmbedder = async (onProgress, config = {}, forceDevice) => {
|
|
|
147
148
|
try {
|
|
148
149
|
// Lazy-load transformers.js only after the runtime guard has passed, so
|
|
149
150
|
// unsupported platforms never reach the native ONNX import (#1515).
|
|
151
|
+
// Under pnpm-strict / `pnpm dlx`, transformers' phantom `onnxruntime-common`
|
|
152
|
+
// import is unresolvable; register the fallback resolver first (#307).
|
|
153
|
+
ensureOnnxRuntimeCommonResolvable();
|
|
150
154
|
const { pipeline, env } = await import('@huggingface/transformers');
|
|
151
155
|
// Configure transformers.js environment
|
|
152
156
|
env.allowLocalModels = false;
|
|
@@ -16,8 +16,8 @@ import { extractStructuralNames } from './structural-extractor.js';
|
|
|
16
16
|
import { EMBEDDABLE_LABELS, isShortLabel, LABEL_METHOD, LABELS_WITH_EXPORTED, STRUCTURAL_LABELS, collectBestChunks, } from './types.js';
|
|
17
17
|
import { resolveEmbeddingConfig } from './config.js';
|
|
18
18
|
import { rankExactEmbeddingRows } from './exact-search.js';
|
|
19
|
-
import { EMBEDDING_TABLE_NAME, EMBEDDING_INDEX_NAME,
|
|
20
|
-
import { loadVectorExtension } from '../lbug/lbug-adapter.js';
|
|
19
|
+
import { EMBEDDING_TABLE_NAME, EMBEDDING_INDEX_NAME, STALE_HASH_SENTINEL } from '../lbug/schema.js';
|
|
20
|
+
import { loadVectorExtension, createVectorIndex } from '../lbug/lbug-adapter.js';
|
|
21
21
|
import { getExactScanLimit } from '../platform/capabilities.js';
|
|
22
22
|
import { logger } from '../logger.js';
|
|
23
23
|
const isDev = process.env.NODE_ENV === 'development';
|
|
@@ -153,24 +153,35 @@ export const batchInsertEmbeddings = async (executeWithReusedStatement, updates)
|
|
|
153
153
|
await executeWithReusedStatement(cypher, paramsList);
|
|
154
154
|
};
|
|
155
155
|
/**
|
|
156
|
-
* Create the vector index for semantic search
|
|
157
|
-
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
|
|
156
|
+
* Create the vector index for semantic search (indexes the CodeEmbedding table).
|
|
157
|
+
*
|
|
158
|
+
* Keeps the embedding-specific extension-install policy gate here
|
|
159
|
+
* (ensureVectorExtensionAvailable → resolveEmbeddingInstallPolicy, default
|
|
160
|
+
* `auto` for the analyze write path), then delegates the actual
|
|
161
|
+
* `CALL CREATE_VECTOR_INDEX(...)` to the adapter, which runs it through the
|
|
162
|
+
* unprepared `conn.query()` path. It must NOT go through the injected
|
|
163
|
+
* `executeQuery` (prepared `conn.prepare()`): LadybugDB cannot prepare that
|
|
164
|
+
* procedure and fails with "We do not support prepare multiple statements" —
|
|
165
|
+
* the silent degrade in #2114.
|
|
162
166
|
*/
|
|
163
|
-
const
|
|
167
|
+
const buildVectorIndex = async () => {
|
|
168
|
+
// This pre-check applies the embedding-specific install policy
|
|
169
|
+
// (resolveEmbeddingInstallPolicy, default `auto` for analyze) before reaching
|
|
170
|
+
// the adapter. The adapter's createVectorIndex() calls loadVectorExtension()
|
|
171
|
+
// again, but that's a no-op here: once this gate loads VECTOR the module-level
|
|
172
|
+
// `vectorExtensionLoaded` flag is set, so the adapter's second call
|
|
173
|
+
// short-circuits without re-resolving the policy — no double install.
|
|
164
174
|
if (!(await ensureVectorExtensionAvailable()))
|
|
165
175
|
return false;
|
|
166
176
|
try {
|
|
167
|
-
await
|
|
168
|
-
return true;
|
|
177
|
+
return await createVectorIndex();
|
|
169
178
|
}
|
|
170
179
|
catch (error) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
180
|
+
// Surface this even outside dev: it silently downgrades a user-requested
|
|
181
|
+
// feature (semantic search) to exact scan. Log under `err` so pino's
|
|
182
|
+
// standard serializer captures the message/stack — logging under `error`
|
|
183
|
+
// serialized an Error to `{}` (the empty `{"error":{}}` reported in #2114).
|
|
184
|
+
logger.warn({ err: error }, 'Vector index creation failed; semantic search will use exact-scan fallback');
|
|
174
185
|
return false;
|
|
175
186
|
}
|
|
176
187
|
};
|
|
@@ -283,7 +294,7 @@ export const runEmbeddingPipeline = async (executeQuery, executeWithReusedStatem
|
|
|
283
294
|
// Ensure the vector index exists even when no new nodes need embedding.
|
|
284
295
|
// A prior crash or first-time incremental run may have left CodeEmbedding
|
|
285
296
|
// rows without ever reaching index creation.
|
|
286
|
-
const vectorIndexReady = await
|
|
297
|
+
const vectorIndexReady = await buildVectorIndex();
|
|
287
298
|
onProgress({
|
|
288
299
|
phase: 'ready',
|
|
289
300
|
percent: 100,
|
|
@@ -403,7 +414,7 @@ export const runEmbeddingPipeline = async (executeQuery, executeWithReusedStatem
|
|
|
403
414
|
if (isDev) {
|
|
404
415
|
logger.info('📇 Creating vector index...');
|
|
405
416
|
}
|
|
406
|
-
const vectorIndexReady = await
|
|
417
|
+
const vectorIndexReady = await buildVectorIndex();
|
|
407
418
|
onProgress({
|
|
408
419
|
phase: 'ready',
|
|
409
420
|
percent: 100,
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Make `@huggingface/transformers`' phantom `onnxruntime-common` import
|
|
3
|
+
* resolvable under strict package-manager layouts (#307, #2069).
|
|
4
|
+
*
|
|
5
|
+
* ## Why
|
|
6
|
+
* transformers' shipped `dist/transformers.node.mjs` does a bare
|
|
7
|
+
* `import 'onnxruntime-common'`, but transformers' `package.json` never declares
|
|
8
|
+
* onnxruntime-common (it lists onnxruntime-node / onnxruntime-web / sharp). With
|
|
9
|
+
* npm's flat `node_modules` — or pnpm with hoisting — the package is hoisted to
|
|
10
|
+
* a directory on transformers' resolution path and the import resolves by
|
|
11
|
+
* accident. Under pnpm's isolated store (and therefore `pnpm dlx` / `pnpx`), a
|
|
12
|
+
* package only sees its *declared* deps, so the import dies with
|
|
13
|
+
* `ERR_MODULE_NOT_FOUND` before `analyze --embeddings` can run.
|
|
14
|
+
*
|
|
15
|
+
* Declaring onnxruntime-common in gitnexus' own dependencies (#2074) does NOT
|
|
16
|
+
* fix this under pnpm: Node resolves the bare specifier from *transformers'*
|
|
17
|
+
* module scope, not ours, and overrides/resolutions can only re-version an
|
|
18
|
+
* existing edge, never add the missing one.
|
|
19
|
+
*
|
|
20
|
+
* ## What this does
|
|
21
|
+
* Install a synchronous, in-thread ESM resolution hook (`module.registerHooks`,
|
|
22
|
+
* Node >= 22.15) that redirects `onnxruntime-common` to a copy gitnexus can
|
|
23
|
+
* resolve — but only when the default resolver fails. The redirect target is
|
|
24
|
+
* preferentially the `onnxruntime-common` that `onnxruntime-node` (the native
|
|
25
|
+
* binding transformers actually loads) itself depends on, so the redirected copy
|
|
26
|
+
* is version-matched to that binding even under `pnpm dlx` — where gitnexus'
|
|
27
|
+
* npm-style `overrides` block does NOT apply, because it is honoured only from a
|
|
28
|
+
* root manifest and gitnexus is a transitive dependency there. It falls back to
|
|
29
|
+
* gitnexus' own direct `onnxruntime-common` dependency when that chain can't be
|
|
30
|
+
* walked. onnxruntime-common is a stable, pure-JS package whose `Tensor` surface
|
|
31
|
+
* is unchanged across 1.24–1.26, so either target is API-compatible. On working
|
|
32
|
+
* layouts the default resolver succeeds first and the hook never fires, so
|
|
33
|
+
* behaviour is unchanged.
|
|
34
|
+
*
|
|
35
|
+
* `registerHooks` (synchronous, in-thread) is preferred over the older
|
|
36
|
+
* `module.register` (async, off-thread, now deprecated — DEP0205, removed in
|
|
37
|
+
* Node 26): the redirect is a one-line conditional that needs no worker thread,
|
|
38
|
+
* no separate hook module, and no `data` marshalling.
|
|
39
|
+
*
|
|
40
|
+
* ## Safety
|
|
41
|
+
* Best-effort and idempotent. The hook is installed lazily, only on the
|
|
42
|
+
* local-embedding code path (after parsing), so it is never registered during
|
|
43
|
+
* analysis, in the parse workers, or in HTTP embedding mode. Once installed it
|
|
44
|
+
* is process-global: its resolve closure runs for every subsequent module
|
|
45
|
+
* resolution, but it passes all of them through untouched and only substitutes a
|
|
46
|
+
* result for the exact `onnxruntime-common` specifier when that specifier is
|
|
47
|
+
* genuinely absent — so it cannot mask an unrelated resolution error, and the
|
|
48
|
+
* per-resolution cost is a single string comparison.
|
|
49
|
+
*
|
|
50
|
+
* `module.registerHooks` is marked `@experimental` and requires Node >= 22.15
|
|
51
|
+
* (the gitnexus engines floor is >= 22.0.0). On older runtimes it is absent and
|
|
52
|
+
* this is a graceful no-op: embeddings then resolve onnxruntime-common exactly
|
|
53
|
+
* as before — fine on hoisted layouts. Any failure during installation is
|
|
54
|
+
* swallowed.
|
|
55
|
+
*/
|
|
56
|
+
import { registerHooks, createRequire } from 'node:module';
|
|
57
|
+
import { pathToFileURL } from 'node:url';
|
|
58
|
+
import { logger } from '../logger.js';
|
|
59
|
+
let attempted = false;
|
|
60
|
+
/**
|
|
61
|
+
* Compute the file: URL the hook redirects `onnxruntime-common` to.
|
|
62
|
+
*
|
|
63
|
+
* Prefer the copy `onnxruntime-node` (the native binding transformers loads)
|
|
64
|
+
* depends on, so the redirected module is version-matched to the binding even
|
|
65
|
+
* under `pnpm dlx`, where transformers keeps its own pinned onnxruntime-node.
|
|
66
|
+
* The walk resolves transformers' MAIN entry — NOT `@huggingface/transformers/
|
|
67
|
+
* package.json`, which transformers' `exports` map blocks
|
|
68
|
+
* (`ERR_PACKAGE_PATH_NOT_EXPORTED`) — then onnxruntime-node, then its
|
|
69
|
+
* onnxruntime-common. Falls back to gitnexus' own direct dependency (always
|
|
70
|
+
* resolvable from our scope) when any step fails.
|
|
71
|
+
*/
|
|
72
|
+
const resolveOnnxRuntimeCommonUrl = () => {
|
|
73
|
+
const require = createRequire(import.meta.url);
|
|
74
|
+
try {
|
|
75
|
+
const transformersMain = require.resolve('@huggingface/transformers');
|
|
76
|
+
const ortNodePkg = createRequire(transformersMain).resolve('onnxruntime-node/package.json');
|
|
77
|
+
const common = createRequire(ortNodePkg).resolve('onnxruntime-common');
|
|
78
|
+
return pathToFileURL(common).href;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return pathToFileURL(require.resolve('onnxruntime-common')).href;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Idempotently install the onnxruntime-common resolution fallback. Call once
|
|
86
|
+
* immediately before the dynamic `import('@huggingface/transformers')` on the
|
|
87
|
+
* local-embedding path.
|
|
88
|
+
*/
|
|
89
|
+
export const ensureOnnxRuntimeCommonResolvable = () => {
|
|
90
|
+
if (attempted)
|
|
91
|
+
return;
|
|
92
|
+
// Mark attempted up-front: a failed attempt must not retry on every
|
|
93
|
+
// initEmbedder() call, and the hook is process-global — once is enough.
|
|
94
|
+
attempted = true;
|
|
95
|
+
try {
|
|
96
|
+
// Node < 22.15 (the gitnexus engines floor is >= 22.0.0): no synchronous
|
|
97
|
+
// hooks API. Degrade gracefully — the import still works on hoisted layouts.
|
|
98
|
+
if (typeof registerHooks !== 'function')
|
|
99
|
+
return;
|
|
100
|
+
const redirectUrl = resolveOnnxRuntimeCommonUrl();
|
|
101
|
+
registerHooks({
|
|
102
|
+
resolve(specifier, context, nextResolve) {
|
|
103
|
+
if (specifier !== 'onnxruntime-common')
|
|
104
|
+
return nextResolve(specifier, context);
|
|
105
|
+
// Honour a real, package-manager-provided copy when one is on the path
|
|
106
|
+
// (npm / hoisted pnpm); only substitute ours when the specifier is
|
|
107
|
+
// genuinely absent.
|
|
108
|
+
try {
|
|
109
|
+
return nextResolve(specifier, context);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
// The phantom import surfaces as ERR_MODULE_NOT_FOUND (or, for a
|
|
113
|
+
// present-but-exports-broken copy, ERR_PACKAGE_PATH_NOT_EXPORTED).
|
|
114
|
+
// Rethrow anything else so a genuinely broken install is not masked.
|
|
115
|
+
const code = err?.code;
|
|
116
|
+
if (code === 'ERR_MODULE_NOT_FOUND' || code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
|
|
117
|
+
return { url: redirectUrl, shortCircuit: true };
|
|
118
|
+
}
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
logger.debug({ redirectUrl }, 'Installed onnxruntime-common resolution fallback (#307)');
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
// Never block embeddings on the fallback. On layouts where the package
|
|
127
|
+
// manager already resolves onnxruntime-common this is unnecessary anyway.
|
|
128
|
+
logger.debug({ err: err instanceof Error ? err.message : String(err) }, 'onnxruntime-common resolution fallback not installed');
|
|
129
|
+
}
|
|
130
|
+
};
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
+
import { requireVendoredGrammar } from '../../../tree-sitter/vendored-grammars.js';
|
|
2
3
|
import { compilePatterns, runCompiledPatterns, } from '../tree-sitter-scanner.js';
|
|
3
4
|
/**
|
|
4
5
|
* Protobuf (.proto) tree-sitter plugin for gRPC contract extraction.
|
|
5
6
|
*
|
|
6
|
-
* Uses `tree-sitter-proto` (coder3101/tree-sitter-proto)
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* string-sanitizing parser.
|
|
7
|
+
* Uses `tree-sitter-proto` (coder3101/tree-sitter-proto), loaded from
|
|
8
|
+
* `vendor/` by absolute path (NEVER copied into node_modules — see
|
|
9
|
+
* vendored-grammars.ts / #2111). If the grammar's binding cannot be loaded
|
|
10
|
+
* (e.g. no prebuild for an unusual platform), the plugin exports `null` and the
|
|
11
|
+
* orchestrator falls back to the existing manual string-sanitizing parser.
|
|
11
12
|
*
|
|
12
13
|
* The grammar is vendored in `vendor/tree-sitter-proto/` with
|
|
13
14
|
* parser.c regenerated against tree-sitter-cli 0.24 (ABI version 14)
|
|
14
15
|
* so it is compatible with the project's tree-sitter 0.21.1 runtime
|
|
15
16
|
* (which loads ABI 13–14).
|
|
16
17
|
*/
|
|
18
|
+
// Only for `tree-sitter` (a real npm dependency) in the smoke-test below;
|
|
19
|
+
// the vendored grammar goes through requireVendoredGrammar (never a bare
|
|
20
|
+
// `_require('tree-sitter-proto')`, which would force a node_modules copy — #2111).
|
|
17
21
|
const _require = createRequire(import.meta.url);
|
|
18
22
|
let ProtoGrammar = null;
|
|
19
23
|
try {
|
|
20
|
-
ProtoGrammar =
|
|
24
|
+
ProtoGrammar = requireVendoredGrammar('tree-sitter-proto');
|
|
21
25
|
}
|
|
22
26
|
catch {
|
|
23
27
|
// Grammar not installed — PROTO_GRPC_PLUGIN will be null.
|
|
@@ -1,31 +1,6 @@
|
|
|
1
1
|
import Java from 'tree-sitter-java';
|
|
2
2
|
import { compilePatterns, runCompiledPatterns, unquoteLiteral, } from '../tree-sitter-scanner.js';
|
|
3
|
-
|
|
4
|
-
* Java HTTP plugin. Handles:
|
|
5
|
-
* - Spring `@RequestMapping` class prefixes + `@(Get|Post|...)Mapping` method annotations
|
|
6
|
-
* - Spring `RestTemplate.getForObject/...`, `exchange(...)`
|
|
7
|
-
* - Spring `WebClient.method(HttpMethod.X, ...)`, `WebClient.get().uri(...)`
|
|
8
|
-
* - OkHttp `new Request.Builder().url("...")`
|
|
9
|
-
* - OpenFeign interfaces with Spring MVC method annotations or
|
|
10
|
-
* native `@RequestLine("METHOD /path")` annotations
|
|
11
|
-
* - Java / Apache HttpClient literal request construction
|
|
12
|
-
*
|
|
13
|
-
* Every route-defining annotation (class/interface `@RequestMapping`
|
|
14
|
-
* prefixes, `@FeignClient(path)` prefixes, `@(Get|...)Mapping` method
|
|
15
|
-
* routes and native `@RequestLine`s) is matched by a single consolidated
|
|
16
|
-
* query (`JAVA_ROUTE_ANNOTATION_PATTERNS`) in one pass via
|
|
17
|
-
* `scanRouteAnnotations`. The `scan` function then walks up from each
|
|
18
|
-
* matched method to its enclosing class/interface to combine the prefix
|
|
19
|
-
* with the method path. Call-site consumers (RestTemplate, WebClient,
|
|
20
|
-
* OkHttp, Java/Apache HttpClient) keep their own focused queries.
|
|
21
|
-
*/
|
|
22
|
-
const METHOD_ANNOTATION_TO_HTTP = {
|
|
23
|
-
GetMapping: 'GET',
|
|
24
|
-
PostMapping: 'POST',
|
|
25
|
-
PutMapping: 'PUT',
|
|
26
|
-
DeleteMapping: 'DELETE',
|
|
27
|
-
PatchMapping: 'PATCH',
|
|
28
|
-
};
|
|
3
|
+
import { METHOD_ANNOTATION_TO_HTTP, isRouteMemberKey, findEnclosingClass, } from '../../../ingestion/route-extractors/spring-shared.js';
|
|
29
4
|
// ─── Route-defining annotations (one generic query, one pass) ─────────
|
|
30
5
|
// Every Java route-mapper annotation shares one shape: an annotation carrying a
|
|
31
6
|
// single string argument — positional `"..."` or named `key = "..."` — on a
|
|
@@ -300,19 +275,9 @@ const APACHE_HTTP_CLIENT_PATTERNS = compilePatterns({
|
|
|
300
275
|
],
|
|
301
276
|
});
|
|
302
277
|
/**
|
|
303
|
-
* Find the nearest enclosing
|
|
304
|
-
*
|
|
305
|
-
* SyntaxNode.parent walks one level at a time.
|
|
278
|
+
* Find the nearest enclosing interface declaration ancestor for a node, or
|
|
279
|
+
* null if the node is top-level.
|
|
306
280
|
*/
|
|
307
|
-
function findEnclosingClass(node) {
|
|
308
|
-
let cur = node.parent;
|
|
309
|
-
while (cur) {
|
|
310
|
-
if (cur.type === 'class_declaration')
|
|
311
|
-
return cur;
|
|
312
|
-
cur = cur.parent;
|
|
313
|
-
}
|
|
314
|
-
return null;
|
|
315
|
-
}
|
|
316
281
|
function findEnclosingInterface(node) {
|
|
317
282
|
let cur = node.parent;
|
|
318
283
|
while (cur) {
|
|
@@ -369,18 +334,6 @@ function hasAnnotation(node, names) {
|
|
|
369
334
|
}
|
|
370
335
|
return false;
|
|
371
336
|
}
|
|
372
|
-
/**
|
|
373
|
-
* A named annotation argument contributes a route only when its member key is
|
|
374
|
-
* `path` or `value`; a positional argument (no key node) always qualifies.
|
|
375
|
-
* This is the JS-side replacement for the in-query `^(path|value)$` filter and
|
|
376
|
-
* drops Spring's non-route string attributes (`produces`, `consumes`,
|
|
377
|
-
* `headers`, `name`, `params`) that would otherwise be mis-read as routes.
|
|
378
|
-
*/
|
|
379
|
-
function isRouteMemberKey(keyNode) {
|
|
380
|
-
if (!keyNode)
|
|
381
|
-
return true;
|
|
382
|
-
return keyNode.text === 'path' || keyNode.text === 'value';
|
|
383
|
-
}
|
|
384
337
|
/**
|
|
385
338
|
* Resolve every Java route-defining annotation in a single tree-sitter pass.
|
|
386
339
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requireVendoredGrammar } from '../../../tree-sitter/vendored-grammars.js';
|
|
2
2
|
import { compilePatterns, runCompiledPatterns, unquoteLiteral, } from '../tree-sitter-scanner.js';
|
|
3
3
|
/**
|
|
4
4
|
* Kotlin HTTP plugin (Spring providers + consumers).
|
|
@@ -52,15 +52,15 @@ import { compilePatterns, runCompiledPatterns, unquoteLiteral, } from '../tree-s
|
|
|
52
52
|
* value_argument
|
|
53
53
|
* string_literal ← the path
|
|
54
54
|
*
|
|
55
|
-
* tree-sitter-kotlin is
|
|
56
|
-
*
|
|
57
|
-
*
|
|
55
|
+
* tree-sitter-kotlin is a vendored grammar loaded from `vendor/` by absolute
|
|
56
|
+
* path (NEVER copied into node_modules — see vendored-grammars.ts / #2111) —
|
|
57
|
+
* when its native binding is unavailable the plugin gracefully exports `null`
|
|
58
|
+
* and `http-patterns/index.ts` skips registration for `.kt`/`.kts` files.
|
|
58
59
|
*/
|
|
59
|
-
|
|
60
|
-
/** Loaded lazily; null when the grammar binding isn't installed. */
|
|
60
|
+
/** Loaded lazily; null when the grammar binding isn't available. */
|
|
61
61
|
let Kotlin = null;
|
|
62
62
|
try {
|
|
63
|
-
Kotlin =
|
|
63
|
+
Kotlin = requireVendoredGrammar('tree-sitter-kotlin');
|
|
64
64
|
}
|
|
65
65
|
catch {
|
|
66
66
|
Kotlin = null;
|