gitnexus 1.4.9 → 1.5.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 +6 -5
- package/dist/cli/ai-context.d.ts +4 -1
- package/dist/cli/ai-context.js +19 -11
- package/dist/cli/analyze.d.ts +6 -0
- package/dist/cli/analyze.js +105 -251
- package/dist/cli/eval-server.js +20 -11
- package/dist/cli/index-repo.js +20 -22
- package/dist/cli/index.js +8 -7
- package/dist/cli/mcp.js +1 -1
- package/dist/cli/serve.js +29 -1
- package/dist/cli/setup.js +9 -9
- package/dist/cli/skill-gen.js +15 -9
- package/dist/cli/wiki.d.ts +2 -0
- package/dist/cli/wiki.js +141 -26
- package/dist/config/ignore-service.js +102 -22
- package/dist/config/supported-languages.d.ts +8 -42
- package/dist/config/supported-languages.js +8 -43
- package/dist/core/augmentation/engine.js +19 -7
- package/dist/core/embeddings/embedder.js +19 -15
- package/dist/core/embeddings/embedding-pipeline.js +6 -6
- package/dist/core/embeddings/http-client.js +3 -3
- package/dist/core/embeddings/text-generator.js +9 -24
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/embeddings/types.js +1 -7
- package/dist/core/graph/graph.js +6 -2
- package/dist/core/graph/types.d.ts +9 -59
- package/dist/core/ingestion/ast-cache.js +3 -3
- package/dist/core/ingestion/call-processor.d.ts +20 -2
- package/dist/core/ingestion/call-processor.js +347 -144
- package/dist/core/ingestion/call-routing.js +10 -4
- package/dist/core/ingestion/call-sites/extract-language-call-site.d.ts +10 -0
- package/dist/core/ingestion/call-sites/extract-language-call-site.js +22 -0
- package/dist/core/ingestion/call-sites/java.d.ts +9 -0
- package/dist/core/ingestion/call-sites/java.js +30 -0
- package/dist/core/ingestion/cluster-enricher.js +6 -8
- package/dist/core/ingestion/cobol/cobol-copy-expander.js +10 -3
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +287 -81
- package/dist/core/ingestion/cobol/jcl-parser.js +1 -1
- package/dist/core/ingestion/cobol/jcl-processor.js +1 -1
- package/dist/core/ingestion/cobol-processor.js +102 -56
- package/dist/core/ingestion/community-processor.js +21 -15
- package/dist/core/ingestion/entry-point-scoring.d.ts +1 -1
- package/dist/core/ingestion/entry-point-scoring.js +5 -6
- package/dist/core/ingestion/export-detection.js +32 -9
- package/dist/core/ingestion/field-extractor.d.ts +1 -1
- package/dist/core/ingestion/field-extractors/configs/c-cpp.js +8 -12
- package/dist/core/ingestion/field-extractors/configs/csharp.js +45 -2
- package/dist/core/ingestion/field-extractors/configs/dart.js +5 -3
- package/dist/core/ingestion/field-extractors/configs/go.js +3 -7
- package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +5 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.js +14 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.js +7 -7
- package/dist/core/ingestion/field-extractors/configs/php.js +9 -11
- package/dist/core/ingestion/field-extractors/configs/python.js +1 -1
- package/dist/core/ingestion/field-extractors/configs/ruby.js +4 -3
- package/dist/core/ingestion/field-extractors/configs/rust.js +2 -5
- package/dist/core/ingestion/field-extractors/configs/swift.js +9 -7
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +2 -6
- package/dist/core/ingestion/field-extractors/generic.d.ts +5 -2
- package/dist/core/ingestion/field-extractors/generic.js +6 -0
- package/dist/core/ingestion/field-extractors/typescript.d.ts +1 -1
- package/dist/core/ingestion/field-extractors/typescript.js +1 -1
- package/dist/core/ingestion/field-types.d.ts +4 -2
- package/dist/core/ingestion/filesystem-walker.js +3 -3
- package/dist/core/ingestion/framework-detection.d.ts +1 -1
- package/dist/core/ingestion/framework-detection.js +355 -85
- package/dist/core/ingestion/heritage-processor.d.ts +24 -0
- package/dist/core/ingestion/heritage-processor.js +99 -8
- package/dist/core/ingestion/import-processor.js +44 -15
- package/dist/core/ingestion/import-resolvers/csharp.js +7 -3
- package/dist/core/ingestion/import-resolvers/dart.js +1 -1
- package/dist/core/ingestion/import-resolvers/go.js +4 -2
- package/dist/core/ingestion/import-resolvers/jvm.js +4 -4
- package/dist/core/ingestion/import-resolvers/php.js +4 -4
- package/dist/core/ingestion/import-resolvers/python.js +1 -1
- package/dist/core/ingestion/import-resolvers/rust.js +9 -3
- package/dist/core/ingestion/import-resolvers/standard.d.ts +1 -1
- package/dist/core/ingestion/import-resolvers/standard.js +6 -5
- package/dist/core/ingestion/import-resolvers/swift.js +2 -1
- package/dist/core/ingestion/import-resolvers/utils.js +26 -7
- package/dist/core/ingestion/language-config.js +5 -4
- package/dist/core/ingestion/language-provider.d.ts +7 -2
- package/dist/core/ingestion/languages/c-cpp.js +106 -21
- package/dist/core/ingestion/languages/cobol.js +1 -1
- package/dist/core/ingestion/languages/csharp.js +96 -19
- package/dist/core/ingestion/languages/dart.js +23 -7
- package/dist/core/ingestion/languages/go.js +1 -1
- package/dist/core/ingestion/languages/index.d.ts +1 -1
- package/dist/core/ingestion/languages/index.js +2 -3
- package/dist/core/ingestion/languages/java.js +4 -1
- package/dist/core/ingestion/languages/kotlin.js +60 -13
- package/dist/core/ingestion/languages/php.js +102 -25
- package/dist/core/ingestion/languages/python.js +28 -5
- package/dist/core/ingestion/languages/ruby.js +56 -14
- package/dist/core/ingestion/languages/rust.js +55 -11
- package/dist/core/ingestion/languages/swift.js +112 -27
- package/dist/core/ingestion/languages/typescript.js +95 -19
- package/dist/core/ingestion/markdown-processor.js +5 -5
- package/dist/core/ingestion/method-extractors/configs/csharp.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/csharp.js +283 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.js +326 -0
- package/dist/core/ingestion/method-extractors/generic.d.ts +5 -0
- package/dist/core/ingestion/method-extractors/generic.js +137 -0
- package/dist/core/ingestion/method-types.d.ts +61 -0
- package/dist/core/ingestion/method-types.js +2 -0
- package/dist/core/ingestion/mro-processor.d.ts +1 -1
- package/dist/core/ingestion/mro-processor.js +12 -8
- package/dist/core/ingestion/named-binding-processor.js +2 -2
- package/dist/core/ingestion/named-bindings/rust.js +3 -1
- package/dist/core/ingestion/parsing-processor.js +74 -24
- package/dist/core/ingestion/pipeline.d.ts +2 -1
- package/dist/core/ingestion/pipeline.js +208 -102
- package/dist/core/ingestion/process-processor.js +12 -10
- package/dist/core/ingestion/resolution-context.js +3 -3
- package/dist/core/ingestion/route-extractors/middleware.js +31 -7
- package/dist/core/ingestion/route-extractors/php.js +2 -1
- package/dist/core/ingestion/route-extractors/response-shapes.js +8 -4
- package/dist/core/ingestion/structure-processor.d.ts +1 -1
- package/dist/core/ingestion/structure-processor.js +4 -4
- package/dist/core/ingestion/symbol-table.d.ts +1 -1
- package/dist/core/ingestion/symbol-table.js +22 -6
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
- package/dist/core/ingestion/tree-sitter-queries.js +1 -1
- package/dist/core/ingestion/type-env.d.ts +2 -2
- package/dist/core/ingestion/type-env.js +75 -50
- package/dist/core/ingestion/type-extractors/c-cpp.js +33 -30
- package/dist/core/ingestion/type-extractors/csharp.js +24 -14
- package/dist/core/ingestion/type-extractors/dart.js +6 -8
- package/dist/core/ingestion/type-extractors/go.js +7 -6
- package/dist/core/ingestion/type-extractors/jvm.js +10 -21
- package/dist/core/ingestion/type-extractors/php.js +26 -13
- package/dist/core/ingestion/type-extractors/python.js +11 -15
- package/dist/core/ingestion/type-extractors/ruby.js +8 -3
- package/dist/core/ingestion/type-extractors/rust.js +6 -8
- package/dist/core/ingestion/type-extractors/shared.js +134 -50
- package/dist/core/ingestion/type-extractors/swift.js +16 -13
- package/dist/core/ingestion/type-extractors/typescript.js +23 -15
- package/dist/core/ingestion/utils/ast-helpers.d.ts +8 -8
- package/dist/core/ingestion/utils/ast-helpers.js +72 -35
- package/dist/core/ingestion/utils/call-analysis.d.ts +2 -0
- package/dist/core/ingestion/utils/call-analysis.js +96 -49
- package/dist/core/ingestion/utils/event-loop.js +1 -1
- package/dist/core/ingestion/workers/parse-worker.d.ts +7 -2
- package/dist/core/ingestion/workers/parse-worker.js +364 -84
- package/dist/core/ingestion/workers/worker-pool.js +5 -10
- package/dist/core/lbug/csv-generator.js +54 -15
- package/dist/core/lbug/lbug-adapter.d.ts +5 -0
- package/dist/core/lbug/lbug-adapter.js +86 -23
- package/dist/core/lbug/schema.d.ts +3 -6
- package/dist/core/lbug/schema.js +6 -30
- package/dist/core/run-analyze.d.ts +49 -0
- package/dist/core/run-analyze.js +257 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +1 -1
- package/dist/core/tree-sitter/parser-loader.js +1 -1
- package/dist/core/wiki/cursor-client.js +2 -7
- package/dist/core/wiki/generator.js +38 -23
- package/dist/core/wiki/graph-queries.js +10 -10
- package/dist/core/wiki/html-viewer.js +7 -3
- package/dist/core/wiki/llm-client.d.ts +23 -2
- package/dist/core/wiki/llm-client.js +96 -26
- package/dist/core/wiki/prompts.js +7 -6
- package/dist/mcp/core/embedder.js +1 -1
- package/dist/mcp/core/lbug-adapter.d.ts +4 -1
- package/dist/mcp/core/lbug-adapter.js +17 -7
- package/dist/mcp/local/local-backend.js +247 -95
- package/dist/mcp/resources.js +14 -6
- package/dist/mcp/server.js +13 -5
- package/dist/mcp/staleness.js +5 -1
- package/dist/mcp/tools.js +100 -23
- package/dist/server/analyze-job.d.ts +53 -0
- package/dist/server/analyze-job.js +146 -0
- package/dist/server/analyze-worker.d.ts +13 -0
- package/dist/server/analyze-worker.js +59 -0
- package/dist/server/api.js +795 -44
- package/dist/server/git-clone.d.ts +25 -0
- package/dist/server/git-clone.js +91 -0
- package/dist/storage/git.js +1 -3
- package/dist/storage/repo-manager.d.ts +5 -2
- package/dist/storage/repo-manager.js +4 -4
- package/dist/types/pipeline.d.ts +1 -21
- package/dist/types/pipeline.js +1 -18
- package/hooks/claude/gitnexus-hook.cjs +52 -22
- package/package.json +13 -13
- package/dist/core/ingestion/utils/language-detection.d.ts +0 -9
- package/dist/core/ingestion/utils/language-detection.js +0 -70
|
@@ -15,27 +15,27 @@
|
|
|
15
15
|
export async function resolveLLMConfig(overrides) {
|
|
16
16
|
const { loadCLIConfig } = await import('../../storage/repo-manager.js');
|
|
17
17
|
const savedConfig = await loadCLIConfig();
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
||
|
|
22
|
-
|
|
23
|
-
|| '';
|
|
24
|
-
// For cursor provider, only use model if explicitly provided (default is 'auto' handled by CLI)
|
|
25
|
-
// For openai provider, use model with fallback to default
|
|
26
|
-
const model = provider === 'cursor'
|
|
27
|
-
? (overrides?.model || savedConfig.cursorModel || '')
|
|
28
|
-
: (overrides?.model || process.env.GITNEXUS_MODEL || savedConfig.model || 'minimax/minimax-m2.5');
|
|
18
|
+
const apiKey = overrides?.apiKey ||
|
|
19
|
+
process.env.GITNEXUS_API_KEY ||
|
|
20
|
+
process.env.OPENAI_API_KEY ||
|
|
21
|
+
savedConfig.apiKey ||
|
|
22
|
+
'';
|
|
29
23
|
return {
|
|
30
24
|
apiKey,
|
|
31
|
-
baseUrl: overrides?.baseUrl
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
model
|
|
25
|
+
baseUrl: overrides?.baseUrl ||
|
|
26
|
+
process.env.GITNEXUS_LLM_BASE_URL ||
|
|
27
|
+
savedConfig.baseUrl ||
|
|
28
|
+
'https://openrouter.ai/api/v1',
|
|
29
|
+
model: overrides?.model ||
|
|
30
|
+
process.env.GITNEXUS_MODEL ||
|
|
31
|
+
(savedConfig.provider === 'cursor' ? savedConfig.cursorModel : undefined) ||
|
|
32
|
+
savedConfig.model ||
|
|
33
|
+
'minimax/minimax-m2.5',
|
|
36
34
|
maxTokens: overrides?.maxTokens ?? 16_384,
|
|
37
35
|
temperature: overrides?.temperature ?? 0,
|
|
38
|
-
provider,
|
|
36
|
+
provider: overrides?.provider ?? savedConfig.provider ?? 'openai',
|
|
37
|
+
apiVersion: overrides?.apiVersion || process.env.GITNEXUS_AZURE_API_VERSION || savedConfig.apiVersion,
|
|
38
|
+
isReasoningModel: overrides?.isReasoningModel ?? savedConfig.isReasoningModel,
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
@@ -44,6 +44,39 @@ export async function resolveLLMConfig(overrides) {
|
|
|
44
44
|
export function estimateTokens(text) {
|
|
45
45
|
return Math.ceil(text.length / 4);
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Returns true if the given base URL is an Azure OpenAI endpoint.
|
|
49
|
+
* Uses proper hostname matching to avoid spoofed URLs like
|
|
50
|
+
* "https://myresource.openai.azure.com.evil.com/v1".
|
|
51
|
+
*/
|
|
52
|
+
export function isAzureProvider(baseUrl) {
|
|
53
|
+
try {
|
|
54
|
+
const { hostname } = new URL(baseUrl);
|
|
55
|
+
return hostname.endsWith('.openai.azure.com') || hostname.endsWith('.services.ai.azure.com');
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// If URL is malformed, fall back to substring check
|
|
59
|
+
return baseUrl.includes('.openai.azure.com') || baseUrl.includes('.services.ai.azure.com');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns true if the model name matches a known reasoning model pattern,
|
|
64
|
+
* or if the explicit override is true.
|
|
65
|
+
* Pass override=false to force non-reasoning even for o-series names.
|
|
66
|
+
*/
|
|
67
|
+
export function isReasoningModel(model, override) {
|
|
68
|
+
if (override !== undefined)
|
|
69
|
+
return override;
|
|
70
|
+
// Match known bare reasoning models (o1, o3) and any o-series with -mini/-preview suffix
|
|
71
|
+
return /^o[1-9]\d*(-mini|-preview)$|^o1$|^o3$/i.test(model);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Build the full chat completions URL, appending ?api-version when provided.
|
|
75
|
+
*/
|
|
76
|
+
export function buildRequestUrl(baseUrl, apiVersion) {
|
|
77
|
+
const base = `${baseUrl.replace(/\/+$/, '')}/chat/completions`;
|
|
78
|
+
return apiVersion ? `${base}?api-version=${encodeURIComponent(apiVersion)}` : base;
|
|
79
|
+
}
|
|
47
80
|
/**
|
|
48
81
|
* Call an OpenAI-compatible LLM API.
|
|
49
82
|
* Uses streaming when onChunk callback is provided for real-time progress.
|
|
@@ -55,16 +88,35 @@ export async function callLLM(prompt, config, systemPrompt, options) {
|
|
|
55
88
|
messages.push({ role: 'system', content: systemPrompt });
|
|
56
89
|
}
|
|
57
90
|
messages.push({ role: 'user', content: prompt });
|
|
58
|
-
|
|
91
|
+
// Detect Azure endpoint (by provider field or URL pattern)
|
|
92
|
+
const azure = config.provider === 'azure' || isAzureProvider(config.baseUrl);
|
|
93
|
+
// Warn when using Azure legacy deployment URL without api-version
|
|
94
|
+
if (azure && !config.apiVersion && config.baseUrl.includes('/deployments/')) {
|
|
95
|
+
console.warn('[gitnexus] Warning: Azure legacy deployment URL detected but no api-version set. Add --api-version 2024-10-21 or use the v1 API format.');
|
|
96
|
+
}
|
|
97
|
+
// Detect reasoning model (o1, o3, o4-mini etc.) or explicit override
|
|
98
|
+
const reasoning = isReasoningModel(config.model, config.isReasoningModel);
|
|
99
|
+
const url = buildRequestUrl(config.baseUrl, azure ? config.apiVersion : undefined);
|
|
59
100
|
const useStream = !!options?.onChunk;
|
|
101
|
+
// Build request body — reasoning models reject temperature and use max_completion_tokens
|
|
60
102
|
const body = {
|
|
61
103
|
model: config.model,
|
|
62
104
|
messages,
|
|
63
|
-
max_tokens: config.maxTokens,
|
|
64
|
-
temperature: config.temperature,
|
|
65
105
|
};
|
|
106
|
+
if (reasoning) {
|
|
107
|
+
body.max_completion_tokens = config.maxTokens;
|
|
108
|
+
// Do NOT include temperature, top_p, presence_penalty, frequency_penalty
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
body.max_tokens = config.maxTokens;
|
|
112
|
+
body.temperature = config.temperature;
|
|
113
|
+
}
|
|
66
114
|
if (useStream)
|
|
67
115
|
body.stream = true;
|
|
116
|
+
// Build auth headers — Azure uses api-key header, everyone else uses Authorization: Bearer
|
|
117
|
+
const authHeaders = azure
|
|
118
|
+
? { 'api-key': config.apiKey }
|
|
119
|
+
: { Authorization: `Bearer ${config.apiKey}` };
|
|
68
120
|
const MAX_RETRIES = 3;
|
|
69
121
|
let lastError = null;
|
|
70
122
|
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
@@ -73,16 +125,23 @@ export async function callLLM(prompt, config, systemPrompt, options) {
|
|
|
73
125
|
method: 'POST',
|
|
74
126
|
headers: {
|
|
75
127
|
'Content-Type': 'application/json',
|
|
76
|
-
|
|
128
|
+
...authHeaders,
|
|
77
129
|
},
|
|
78
130
|
body: JSON.stringify(body),
|
|
79
131
|
});
|
|
80
132
|
if (!response.ok) {
|
|
81
133
|
const errorText = await response.text().catch(() => 'unknown error');
|
|
134
|
+
// Azure content filter — surface a clear message instead of a generic API error
|
|
135
|
+
if (azure &&
|
|
136
|
+
response.status === 400 &&
|
|
137
|
+
(errorText.includes('content_filter') ||
|
|
138
|
+
errorText.includes('ResponsibleAIPolicyViolation'))) {
|
|
139
|
+
throw new Error(`Azure content filter blocked this request. The prompt triggered content policy. Details: ${errorText.slice(0, 300)}`);
|
|
140
|
+
}
|
|
82
141
|
// Rate limit — wait with exponential backoff and retry
|
|
83
142
|
if (response.status === 429 && attempt < MAX_RETRIES - 1) {
|
|
84
143
|
const retryAfter = parseInt(response.headers.get('retry-after') || '0', 10);
|
|
85
|
-
const delay = retryAfter > 0 ? retryAfter * 1000 :
|
|
144
|
+
const delay = retryAfter > 0 ? retryAfter * 1000 : 2 ** attempt * 3000;
|
|
86
145
|
await sleep(delay);
|
|
87
146
|
continue;
|
|
88
147
|
}
|
|
@@ -98,7 +157,7 @@ export async function callLLM(prompt, config, systemPrompt, options) {
|
|
|
98
157
|
return await readSSEStream(response.body, options.onChunk);
|
|
99
158
|
}
|
|
100
159
|
// Non-streaming path
|
|
101
|
-
const json = await response.json();
|
|
160
|
+
const json = (await response.json());
|
|
102
161
|
const choice = json.choices?.[0];
|
|
103
162
|
if (!choice?.message?.content) {
|
|
104
163
|
throw new Error('LLM returned empty response');
|
|
@@ -112,7 +171,8 @@ export async function callLLM(prompt, config, systemPrompt, options) {
|
|
|
112
171
|
catch (err) {
|
|
113
172
|
lastError = err;
|
|
114
173
|
// Network error — retry with backoff
|
|
115
|
-
if (attempt < MAX_RETRIES - 1 &&
|
|
174
|
+
if (attempt < MAX_RETRIES - 1 &&
|
|
175
|
+
(err.code === 'ECONNREFUSED' || err.code === 'ETIMEDOUT' || err.message?.includes('fetch'))) {
|
|
116
176
|
await sleep((attempt + 1) * 3000);
|
|
117
177
|
continue;
|
|
118
178
|
}
|
|
@@ -129,6 +189,7 @@ async function readSSEStream(body, onChunk) {
|
|
|
129
189
|
const reader = body.getReader();
|
|
130
190
|
let content = '';
|
|
131
191
|
let buffer = '';
|
|
192
|
+
let contentFilterTriggered = false;
|
|
132
193
|
while (true) {
|
|
133
194
|
const { done, value } = await reader.read();
|
|
134
195
|
if (done)
|
|
@@ -145,7 +206,13 @@ async function readSSEStream(body, onChunk) {
|
|
|
145
206
|
continue;
|
|
146
207
|
try {
|
|
147
208
|
const parsed = JSON.parse(data);
|
|
148
|
-
const
|
|
209
|
+
const choice = parsed.choices?.[0];
|
|
210
|
+
// Detect content filter finish reason — skip delta from this chunk
|
|
211
|
+
if (choice?.finish_reason === 'content_filter') {
|
|
212
|
+
contentFilterTriggered = true;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const delta = choice?.delta?.content;
|
|
149
216
|
if (delta) {
|
|
150
217
|
content += delta;
|
|
151
218
|
onChunk(content.length);
|
|
@@ -156,11 +223,14 @@ async function readSSEStream(body, onChunk) {
|
|
|
156
223
|
}
|
|
157
224
|
}
|
|
158
225
|
}
|
|
226
|
+
if (contentFilterTriggered) {
|
|
227
|
+
throw new Error('content filter triggered mid-stream. The generated content was blocked by content policy. Adjust your prompt and retry.');
|
|
228
|
+
}
|
|
159
229
|
if (!content) {
|
|
160
230
|
throw new Error('LLM returned empty streaming response');
|
|
161
231
|
}
|
|
162
232
|
return { content };
|
|
163
233
|
}
|
|
164
234
|
function sleep(ms) {
|
|
165
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
235
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
166
236
|
}
|
|
@@ -121,9 +121,9 @@ export function fillTemplate(template, vars) {
|
|
|
121
121
|
*/
|
|
122
122
|
export function formatFileListForGrouping(files) {
|
|
123
123
|
return files
|
|
124
|
-
.map(f => {
|
|
124
|
+
.map((f) => {
|
|
125
125
|
const exports = f.symbols.length > 0
|
|
126
|
-
? f.symbols.map(s => `${s.name} (${s.type})`).join(', ')
|
|
126
|
+
? f.symbols.map((s) => `${s.name} (${s.type})`).join(', ')
|
|
127
127
|
: 'no exports';
|
|
128
128
|
return `- ${f.filePath}: ${exports}`;
|
|
129
129
|
})
|
|
@@ -143,7 +143,8 @@ export function formatDirectoryTree(filePaths) {
|
|
|
143
143
|
const sorted = Array.from(dirs).sort();
|
|
144
144
|
if (sorted.length === 0)
|
|
145
145
|
return '(flat structure)';
|
|
146
|
-
return sorted.slice(0, 50).join('\n') +
|
|
146
|
+
return (sorted.slice(0, 50).join('\n') +
|
|
147
|
+
(sorted.length > 50 ? `\n... and ${sorted.length - 50} more directories` : ''));
|
|
147
148
|
}
|
|
148
149
|
/**
|
|
149
150
|
* Format call edges as readable text.
|
|
@@ -153,7 +154,7 @@ export function formatCallEdges(edges) {
|
|
|
153
154
|
return 'None';
|
|
154
155
|
return edges
|
|
155
156
|
.slice(0, 30)
|
|
156
|
-
.map(e => `${e.fromName} (${shortPath(e.fromFile)}) → ${e.toName} (${shortPath(e.toFile)})`)
|
|
157
|
+
.map((e) => `${e.fromName} (${shortPath(e.fromFile)}) → ${e.toName} (${shortPath(e.toFile)})`)
|
|
157
158
|
.join('\n');
|
|
158
159
|
}
|
|
159
160
|
/**
|
|
@@ -163,9 +164,9 @@ export function formatProcesses(processes) {
|
|
|
163
164
|
if (processes.length === 0)
|
|
164
165
|
return 'No execution flows detected for this module.';
|
|
165
166
|
return processes
|
|
166
|
-
.map(p => {
|
|
167
|
+
.map((p) => {
|
|
167
168
|
const stepsText = p.steps
|
|
168
|
-
.map(s => ` ${s.step}. ${s.name} (${shortPath(s.filePath)})`)
|
|
169
|
+
.map((s) => ` ${s.step}. ${s.name} (${shortPath(s.filePath)})`)
|
|
169
170
|
.join('\n');
|
|
170
171
|
return `**${p.label}** (${p.type}):\n${stepsText}`;
|
|
171
172
|
})
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* For MCP, we only need to compute query embeddings, not batch embed.
|
|
6
6
|
*/
|
|
7
7
|
import { pipeline, env } from '@huggingface/transformers';
|
|
8
|
-
import { isHttpMode, getHttpDimensions, httpEmbedQuery } from '../../core/embeddings/http-client.js';
|
|
8
|
+
import { isHttpMode, getHttpDimensions, httpEmbedQuery, } from '../../core/embeddings/http-client.js';
|
|
9
9
|
// Model config
|
|
10
10
|
const MODEL_ID = 'Snowflake/snowflake-arctic-embed-xs';
|
|
11
11
|
// Module-level state for singleton pattern
|
|
@@ -56,7 +56,10 @@ export declare const closeLbug: (repoId?: string) => Promise<void>;
|
|
|
56
56
|
* Check if a specific repo's pool is active
|
|
57
57
|
*/
|
|
58
58
|
export declare const isLbugReady: (repoId: string) => boolean;
|
|
59
|
-
/** Regex to detect write operations in user-supplied Cypher queries
|
|
59
|
+
/** Regex to detect write operations in user-supplied Cypher queries.
|
|
60
|
+
* Note: CALL is NOT blocked — it's used for read-only FTS (CALL QUERY_FTS_INDEX)
|
|
61
|
+
* and vector search (CALL QUERY_VECTOR_INDEX). The database is opened in
|
|
62
|
+
* read-only mode as defense-in-depth against write procedures. */
|
|
60
63
|
export declare const CYPHER_WRITE_RE: RegExp;
|
|
61
64
|
/** Check if a Cypher query contains write operations */
|
|
62
65
|
export declare function isWriteQuery(query: string): boolean;
|
|
@@ -222,11 +222,10 @@ async function doInitLbug(repoId, dbPath) {
|
|
|
222
222
|
catch (err) {
|
|
223
223
|
restoreStdout();
|
|
224
224
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
225
|
-
const isLockError = lastError.message.includes('Could not set lock')
|
|
226
|
-
|| lastError.message.includes('lock');
|
|
225
|
+
const isLockError = lastError.message.includes('Could not set lock') || lastError.message.includes('lock');
|
|
227
226
|
if (!isLockError || attempt === LOCK_RETRY_ATTEMPTS)
|
|
228
227
|
break;
|
|
229
|
-
await new Promise(resolve => setTimeout(resolve, LOCK_RETRY_DELAY_MS * attempt));
|
|
228
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_DELAY_MS * attempt));
|
|
230
229
|
}
|
|
231
230
|
}
|
|
232
231
|
if (!shared) {
|
|
@@ -264,7 +263,15 @@ async function doInitLbug(repoId, dbPath) {
|
|
|
264
263
|
// Register pool entry only after all connections are pre-warmed and FTS is
|
|
265
264
|
// loaded. Concurrent executeQuery calls see either "not initialized"
|
|
266
265
|
// (and throw cleanly) or a fully ready pool — never a half-built one.
|
|
267
|
-
pool.set(repoId, {
|
|
266
|
+
pool.set(repoId, {
|
|
267
|
+
db,
|
|
268
|
+
available,
|
|
269
|
+
checkedOut: 0,
|
|
270
|
+
waiters: [],
|
|
271
|
+
lastUsed: Date.now(),
|
|
272
|
+
dbPath,
|
|
273
|
+
closed: false,
|
|
274
|
+
});
|
|
268
275
|
ensureIdleTimer();
|
|
269
276
|
}
|
|
270
277
|
/**
|
|
@@ -318,7 +325,7 @@ export async function initLbugWithDb(repoId, existingDb, dbPath) {
|
|
|
318
325
|
waiters: [],
|
|
319
326
|
lastUsed: Date.now(),
|
|
320
327
|
dbPath,
|
|
321
|
-
closed: false
|
|
328
|
+
closed: false,
|
|
322
329
|
});
|
|
323
330
|
ensureIdleTimer();
|
|
324
331
|
}
|
|
@@ -468,8 +475,11 @@ export const closeLbug = async (repoId) => {
|
|
|
468
475
|
* Check if a specific repo's pool is active
|
|
469
476
|
*/
|
|
470
477
|
export const isLbugReady = (repoId) => pool.has(repoId);
|
|
471
|
-
/** Regex to detect write operations in user-supplied Cypher queries
|
|
472
|
-
|
|
478
|
+
/** Regex to detect write operations in user-supplied Cypher queries.
|
|
479
|
+
* Note: CALL is NOT blocked — it's used for read-only FTS (CALL QUERY_FTS_INDEX)
|
|
480
|
+
* and vector search (CALL QUERY_VECTOR_INDEX). The database is opened in
|
|
481
|
+
* read-only mode as defense-in-depth against write procedures. */
|
|
482
|
+
export const CYPHER_WRITE_RE = /(?<!:)\b(CREATE|DELETE|SET|MERGE|REMOVE|DROP|ALTER|COPY|DETACH|FOREACH|INSTALL|LOAD)\b/i;
|
|
473
483
|
/** Check if a Cypher query contains write operations */
|
|
474
484
|
export function isWriteQuery(query) {
|
|
475
485
|
return CYPHER_WRITE_RE.test(query);
|