gitnexus 1.6.9 → 1.6.10-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 +126 -41
- package/dist/cli/ai-context.d.ts +1 -1
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.js +106 -3
- package/dist/cli/cli-message.d.ts +1 -1
- package/dist/cli/doctor.d.ts +5 -0
- package/dist/cli/doctor.js +41 -3
- package/dist/cli/editor-targets.d.ts +17 -2
- package/dist/cli/editor-targets.js +44 -0
- package/dist/cli/embeddings.d.ts +12 -0
- package/dist/cli/embeddings.js +49 -0
- package/dist/cli/help-i18n.js +4 -0
- package/dist/cli/i18n/en.d.ts +5 -1
- package/dist/cli/i18n/en.js +5 -1
- package/dist/cli/i18n/resources.d.ts +9 -1
- package/dist/cli/i18n/zh-CN.d.ts +4 -0
- package/dist/cli/i18n/zh-CN.js +5 -1
- package/dist/cli/index.js +12 -1
- package/dist/cli/setup.js +204 -28
- package/dist/cli/uninstall.js +70 -16
- package/dist/core/embeddings/embedder.js +16 -2
- package/dist/core/embeddings/http-client.d.ts +28 -1
- package/dist/core/embeddings/http-client.js +107 -15
- package/dist/core/embeddings/node-module-compat.d.ts +23 -0
- package/dist/core/embeddings/node-module-compat.js +23 -0
- package/dist/core/embeddings/onnxruntime-common-resolver.js +6 -3
- package/dist/core/embeddings/onnxruntime-node-resolver.js +31 -5
- package/dist/core/embeddings/runtime-install.d.ts +119 -0
- package/dist/core/embeddings/runtime-install.js +372 -0
- package/dist/core/embeddings/runtime-support.d.ts +48 -17
- package/dist/core/embeddings/runtime-support.js +96 -0
- package/dist/core/group/extractors/http-patterns/python.js +246 -108
- package/dist/core/group/extractors/manifest-extractor.js +4 -0
- package/dist/core/ingestion/cobol/jcl-processor.js +9 -8
- package/dist/core/ingestion/cobol-processor.js +27 -26
- package/dist/core/ingestion/community-processor.d.ts +51 -1
- package/dist/core/ingestion/community-processor.js +316 -63
- package/dist/core/ingestion/emit-references.js +3 -2
- package/dist/core/ingestion/markdown-processor.js +3 -2
- package/dist/core/ingestion/parsing-processor.d.ts +3 -1
- package/dist/core/ingestion/parsing-processor.js +4 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +42 -0
- package/dist/core/ingestion/route-extractors/constant-resolver.d.ts +83 -0
- package/dist/core/ingestion/route-extractors/constant-resolver.js +131 -0
- package/dist/core/ingestion/route-extractors/python-const-resolver.d.ts +75 -0
- package/dist/core/ingestion/route-extractors/python-const-resolver.js +299 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
- package/dist/core/ingestion/tree-sitter-queries.js +12 -1
- package/dist/core/ingestion/utils/line-base.d.ts +20 -0
- package/dist/core/ingestion/utils/line-base.js +20 -0
- package/dist/core/ingestion/utils/symbol-labels.d.ts +21 -0
- package/dist/core/ingestion/utils/symbol-labels.js +45 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +34 -0
- package/dist/core/ingestion/workers/parse-worker.js +39 -4
- package/dist/core/ingestion/workers/result-merge.js +4 -0
- package/dist/core/lbug/csv-generator.js +8 -2
- package/dist/core/lbug/extension-load-error.d.ts +67 -0
- package/dist/core/lbug/extension-load-error.js +320 -0
- package/dist/core/lbug/extension-loader.d.ts +17 -2
- package/dist/core/lbug/extension-loader.js +38 -13
- package/dist/core/lbug/lbug-adapter.js +7 -16
- package/dist/core/lbug/native-check.d.ts +22 -0
- package/dist/core/lbug/native-check.js +66 -0
- package/dist/core/lbug/pool-adapter.js +10 -1
- package/dist/core/lbug/sidecar-recovery.d.ts +32 -0
- package/dist/core/lbug/sidecar-recovery.js +91 -7
- package/dist/core/platform/capabilities.js +27 -1
- package/dist/core/run-analyze.js +33 -6
- package/dist/core/search/fts-indexes.d.ts +7 -0
- package/dist/core/search/fts-indexes.js +37 -0
- package/dist/mcp/core/embedder.js +16 -2
- package/dist/mcp/local/line-display.d.ts +22 -0
- package/dist/mcp/local/line-display.js +3 -0
- package/dist/mcp/local/local-backend.d.ts +14 -0
- package/dist/mcp/local/local-backend.js +63 -19
- package/dist/mcp/local/pdg-impact.d.ts +5 -3
- package/dist/mcp/local/pdg-impact.js +5 -2
- package/dist/mcp/resources.js +1 -0
- package/dist/mcp/tools.js +1 -1
- package/dist/server/api.js +2 -2
- package/dist/storage/parse-cache.js +1 -1
- package/dist/storage/repo-manager.d.ts +6 -1
- package/dist/storage/repo-manager.js +6 -1
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +50 -3
- package/hooks/claude/gitnexus-hook.cjs +59 -9
- package/package.json +5 -3
- package/scripts/cross-platform-tests.ts +26 -0
- package/scripts/ensure-fts.ts +32 -0
- package/scripts/install-duckdb-extension.mjs +85 -24
- package/scripts/run-cross-platform.ts +46 -6
- package/scripts/shard-arg.ts +30 -0
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Warning attached to search responses when BM25/FTS is degraded. Prefers the
|
|
3
|
+
* live extension-load failure (with LadybugDB's real reason, #2374) over the
|
|
4
|
+
* generic indexes-missing message, so "indexes exist but the extension broke"
|
|
5
|
+
* is not misreported as missing indexes.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ftsDegradedWarning: () => string;
|
|
1
8
|
export declare const SUPPORTED_FTS_STEMMERS: ReadonlySet<string>;
|
|
2
9
|
export interface CreateSearchFTSIndexesOptions {
|
|
3
10
|
onIndexStart?: (table: string, indexName: string) => void;
|
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
import { createFTSIndex, dropFTSIndex, DEFAULT_FTS_STEMMER } from '../lbug/lbug-adapter.js';
|
|
2
|
+
import { getExtensionCapabilities } from '../lbug/extension-loader.js';
|
|
3
|
+
import { classifyExtensionLoadError } from '../lbug/extension-load-error.js';
|
|
2
4
|
import { FTS_INDEXES } from './fts-schema.js';
|
|
5
|
+
/**
|
|
6
|
+
* Strip filesystem paths from a LadybugDB error before it reaches the HTTP
|
|
7
|
+
* `/api/search` and MCP query surfaces (#2374, PR #2375): the raw LOAD error
|
|
8
|
+
* embeds the absolute extension path (username, home dir) which must not leak to
|
|
9
|
+
* a network client. The error class words ("Failed to load library", "invalid
|
|
10
|
+
* ELF header", "has not been installed") have no leading path separator and
|
|
11
|
+
* survive. CLI/doctor/log surfaces keep the full path (they read the reason
|
|
12
|
+
* directly, not through this function).
|
|
13
|
+
*/
|
|
14
|
+
const redactPaths = (reason) => reason.replace(/(?:[A-Za-z]:\\|\/)[^\s'"]+/g, '<path>');
|
|
15
|
+
/**
|
|
16
|
+
* Warning attached to search responses when BM25/FTS is degraded. Prefers the
|
|
17
|
+
* live extension-load failure (with LadybugDB's real reason, #2374) over the
|
|
18
|
+
* generic indexes-missing message, so "indexes exist but the extension broke"
|
|
19
|
+
* is not misreported as missing indexes.
|
|
20
|
+
*/
|
|
21
|
+
export const ftsDegradedWarning = () => {
|
|
22
|
+
const fts = getExtensionCapabilities().find((c) => c.name === 'fts');
|
|
23
|
+
if (fts && !fts.loaded) {
|
|
24
|
+
const reason = fts.reason ? redactPaths(fts.reason).replace(/\.$/, '') : undefined;
|
|
25
|
+
// A missing *runtime dependency* (Windows error 126, etc.) is not healed by
|
|
26
|
+
// reinstalling (#2374) — surface the classified remedy instead of the generic
|
|
27
|
+
// reinstall tail. Read the diagnosis cached at mark-unavailable time so this
|
|
28
|
+
// per-request path (HTTP /api/search + MCP query) does NO file I/O (#2383 F3);
|
|
29
|
+
// fall back to the pure, no-I/O string classifier if it is somehow absent.
|
|
30
|
+
const { kind, remedy } = fts.diagnosis ?? classifyExtensionLoadError(fts.reason);
|
|
31
|
+
const tail = kind === 'missing_dependency'
|
|
32
|
+
? ` ${remedy}`
|
|
33
|
+
: '. Run `gitnexus doctor` for details, then `gitnexus analyze --repair-fts` with network access to reinstall.';
|
|
34
|
+
return ('FTS extension failed to load — keyword search degraded' +
|
|
35
|
+
(reason ? ` (${reason})` : '') +
|
|
36
|
+
tail);
|
|
37
|
+
}
|
|
38
|
+
return 'FTS indexes missing — keyword search degraded. Run: gitnexus analyze --repair-fts (or gitnexus analyze --force) to rebuild indexes.';
|
|
39
|
+
};
|
|
3
40
|
// Stemmers shipped by the LadybugDB FTS extension. Mirrors the lowercase token
|
|
4
41
|
// set in the extension bundled with @ladybugdb/core 0.18.x (see package.json).
|
|
5
42
|
// Keep in sync on a LadybugDB minor bump — a value here that the installed
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
import { isHttpMode, getHttpDimensions, httpEmbedQuery, } from '../../core/embeddings/http-client.js';
|
|
8
8
|
import { resolveEmbeddingConfig } from '../../core/embeddings/config.js';
|
|
9
9
|
import { applyHfEnvOverrides, isHfDownloadFailure, withHfDownloadRetry, } from '../../core/embeddings/hf-env.js';
|
|
10
|
-
import { getLocalEmbeddingRuntimeBlocker } from '../../core/embeddings/runtime-support.js';
|
|
10
|
+
import { getLocalEmbeddingRuntimeBlocker, getMissingLocalEmbeddingStackMessage, } from '../../core/embeddings/runtime-support.js';
|
|
11
11
|
import { ensureOnnxRuntimeCommonResolvable } from '../../core/embeddings/onnxruntime-common-resolver.js';
|
|
12
|
+
import { ensureEmbeddingStackResolvable } from '../../core/embeddings/runtime-install.js';
|
|
12
13
|
import { ensureOnnxRuntimeNodeMatchesSystem } from '../../core/embeddings/onnxruntime-node-resolver.js';
|
|
13
14
|
import { silenceStdout, restoreStdout, realStderrWrite } from '../../core/lbug/pool-adapter.js';
|
|
14
15
|
import { logger } from '../../core/logger.js';
|
|
@@ -45,6 +46,11 @@ export const initEmbedder = async () => {
|
|
|
45
46
|
try {
|
|
46
47
|
// Lazy-load transformers.js only after the runtime guard has passed, so
|
|
47
48
|
// unsupported platforms never reach the native ONNX import (#1515).
|
|
49
|
+
// Registered FIRST so it sits last in the hook chain (registerHooks runs
|
|
50
|
+
// the most recent hook first): when the optional stack was pruned at
|
|
51
|
+
// install time (#2370), its bare specifiers fall back to the on-demand
|
|
52
|
+
// runtime prefix.
|
|
53
|
+
ensureEmbeddingStackResolvable();
|
|
48
54
|
// Under pnpm-strict / `pnpm dlx`, transformers' phantom `onnxruntime-common`
|
|
49
55
|
// import is unresolvable; register the fallback resolver first (#307).
|
|
50
56
|
ensureOnnxRuntimeCommonResolvable();
|
|
@@ -55,7 +61,15 @@ export const initEmbedder = async () => {
|
|
|
55
61
|
// Windows/DirectML, and macOS. Mirrors the core embedder's call site so
|
|
56
62
|
// MCP query-time embedding gets the same CUDA-13 fix.
|
|
57
63
|
ensureOnnxRuntimeNodeMatchesSystem();
|
|
58
|
-
|
|
64
|
+
// The stack is an optionalDependency: npm prunes it when onnxruntime-node's
|
|
65
|
+
// postinstall can't reach api.nuget.org (#2370). Rethrow with actionable
|
|
66
|
+
// reinstall guidance instead of a raw ERR_MODULE_NOT_FOUND.
|
|
67
|
+
const { pipeline, env } = await import('@huggingface/transformers').catch((err) => {
|
|
68
|
+
const missing = getMissingLocalEmbeddingStackMessage(err);
|
|
69
|
+
if (missing)
|
|
70
|
+
throw new Error(missing);
|
|
71
|
+
throw err;
|
|
72
|
+
});
|
|
59
73
|
env.allowLocalModels = false;
|
|
60
74
|
// Bridge user-controlled env vars to transformers.js: HF_HOME →
|
|
61
75
|
// env.cacheDir, HF_ENDPOINT → env.remoteHost (#1205). Centralised in
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a 0-based GraphNode `startLine`/`endLine` to the 1-based line number
|
|
3
|
+
* shown to humans and LLMs in MCP tool output.
|
|
4
|
+
*
|
|
5
|
+
* Storage is 0-based (tree-sitter `startPosition.row`; see
|
|
6
|
+
* `ingestion/utils/line-base.ts`), which matches editors/`sed`/`less -N` only
|
|
7
|
+
* after `+ 1`. The `context`, `query`, and `impact` tools present line numbers a
|
|
8
|
+
* user cross-references against source, so they convert here at the response
|
|
9
|
+
* boundary (#2377).
|
|
10
|
+
*
|
|
11
|
+
* Apply ONLY to a symbol node's 0-based `startLine`/`endLine`. Do NOT apply to:
|
|
12
|
+
* - BasicBlock / CFG `functionStartLine` and PDG statement lines — already
|
|
13
|
+
* 1-based (they use `startPosition.row + 1`);
|
|
14
|
+
* - the internal `sym.startLine + 1` join params that target the 1-based
|
|
15
|
+
* BasicBlock id space;
|
|
16
|
+
* - raw `cypher` results, which pass LadybugDB columns through verbatim and
|
|
17
|
+
* stay 0-based (documented).
|
|
18
|
+
*
|
|
19
|
+
* `undefined`/`null` pass through so optional line fields stay absent.
|
|
20
|
+
*/
|
|
21
|
+
export declare function toDisplayLine(zeroBasedLine: number): number;
|
|
22
|
+
export declare function toDisplayLine(zeroBasedLine: number | null | undefined): number | undefined;
|
|
@@ -162,10 +162,24 @@ export declare class LocalBackend {
|
|
|
162
162
|
* stderr noisy per DoD §2.8.
|
|
163
163
|
*/
|
|
164
164
|
private warnedVectorUnsupported;
|
|
165
|
+
/**
|
|
166
|
+
* One-shot warning when a pruned or Node-unloadable optional embedding stack
|
|
167
|
+
* (#2370/#2372) forces semantic search to fall back to BM25 — so the
|
|
168
|
+
* degradation is visible once instead of silent.
|
|
169
|
+
*/
|
|
170
|
+
private warnedMissingEmbeddingStack;
|
|
165
171
|
/**
|
|
166
172
|
* Cross-repo group tools (CLI). Shares logic with MCP `group_*` handlers.
|
|
167
173
|
*/
|
|
168
174
|
getGroupService(): GroupService;
|
|
175
|
+
/**
|
|
176
|
+
* Adapt local `trace` to the group port. The assembled group/cross-repo trace
|
|
177
|
+
* presents 1-based endpoints (via resolveSymbolForGroup), so convert the hop
|
|
178
|
+
* lines here too — otherwise one response mixes 1-based endpoints with 0-based
|
|
179
|
+
* hops (#2380). Single-repo `trace` dispatches directly (not through this
|
|
180
|
+
* port) and stays 0-based (documented full-parity follow-up).
|
|
181
|
+
*/
|
|
182
|
+
private traceForGroup;
|
|
169
183
|
/**
|
|
170
184
|
* Adapt the shared symbol resolver to the GroupToolPort contract. Used by the
|
|
171
185
|
* cross-repo trace path to locate which member repo an endpoint lives in and
|
|
@@ -10,6 +10,7 @@ import path from 'path';
|
|
|
10
10
|
import { createHash } from 'crypto';
|
|
11
11
|
import { initLbug, executeQuery, executeParameterized, closeLbug, isLbugReady, } from '../../core/lbug/pool-adapter.js';
|
|
12
12
|
import { isValidQueryParams } from '../../core/lbug/query-params.js';
|
|
13
|
+
import { toDisplayLine } from './line-display.js';
|
|
13
14
|
import { isWalCorruptionError, WAL_RECOVERY_SUGGESTION } from '../../core/lbug/lbug-config.js';
|
|
14
15
|
// Embedding imports are lazy (dynamic import) to avoid loading onnxruntime-node
|
|
15
16
|
// at MCP server startup — crashes on unsupported Node ABI versions (#89)
|
|
@@ -26,9 +27,11 @@ import { rankExactEmbeddingRows, } from '../../core/embeddings/exact-search.js';
|
|
|
26
27
|
import { EMBEDDING_TABLE_NAME, EMBEDDING_INDEX_NAME } from '../../core/lbug/schema.js';
|
|
27
28
|
import { getExactScanLimit, isVectorExtensionSupportedByPlatform, } from '../../core/platform/capabilities.js';
|
|
28
29
|
import { PhaseTimer } from '../../core/search/phase-timer.js';
|
|
30
|
+
import { ftsDegradedWarning } from '../../core/search/fts-indexes.js';
|
|
29
31
|
import { cjkSegmentationModeMismatch, containsSegmentableCjkRun, getSearchFTSCjkSegmentation, isSupportedCjkSegmentationMode, MAX_CJK_SEGMENTATION_QUERY_LENGTH, } from '../../core/search/cjk-segmentation.js';
|
|
30
32
|
import { checkStalenessAsync, checkCwdMatch } from '../../core/git-staleness.js';
|
|
31
33
|
import { logger } from '../../core/logger.js';
|
|
34
|
+
import { isLocalEmbeddingRuntimeBlockerMessage, isMissingLocalEmbeddingStackMessage, } from '../../core/embeddings/runtime-support.js';
|
|
32
35
|
import { LIST_REPOS_DEFAULT_LIMIT, LIST_REPOS_MAX_LIMIT, EXPLAIN_DEFAULT_LIMIT, EXPLAIN_MAX_LIMIT, PDG_QUERY_DEFAULT_LIMIT, PDG_QUERY_MAX_LIMIT, } from '../tools.js';
|
|
33
36
|
import { findImportCycles } from '../../core/graph/import-cycles.js';
|
|
34
37
|
import { decodeTaintPath } from '../../core/ingestion/taint/path-codec.js';
|
|
@@ -434,6 +437,12 @@ export class LocalBackend {
|
|
|
434
437
|
* stderr noisy per DoD §2.8.
|
|
435
438
|
*/
|
|
436
439
|
warnedVectorUnsupported = false;
|
|
440
|
+
/**
|
|
441
|
+
* One-shot warning when a pruned or Node-unloadable optional embedding stack
|
|
442
|
+
* (#2370/#2372) forces semantic search to fall back to BM25 — so the
|
|
443
|
+
* degradation is visible once instead of silent.
|
|
444
|
+
*/
|
|
445
|
+
warnedMissingEmbeddingStack = false;
|
|
437
446
|
/**
|
|
438
447
|
* Cross-repo group tools (CLI). Shares logic with MCP `group_*` handlers.
|
|
439
448
|
*/
|
|
@@ -445,7 +454,7 @@ export class LocalBackend {
|
|
|
445
454
|
query: (r, p) => this.query(r, p),
|
|
446
455
|
impactByUid: (id, uid, d, o) => this.impactByUid(id, uid, d, o),
|
|
447
456
|
context: (r, p) => this.context(r, p),
|
|
448
|
-
trace: (r, p) => this.
|
|
457
|
+
trace: (r, p) => this.traceForGroup(r, p),
|
|
449
458
|
resolveSymbol: (r, q) => this.resolveSymbolForGroup(r, q),
|
|
450
459
|
pdgFlows: (r, anchor, opts) => this.pdgFlowsForGroup(r, anchor, opts),
|
|
451
460
|
};
|
|
@@ -453,6 +462,23 @@ export class LocalBackend {
|
|
|
453
462
|
}
|
|
454
463
|
return this.groupToolSvc;
|
|
455
464
|
}
|
|
465
|
+
/**
|
|
466
|
+
* Adapt local `trace` to the group port. The assembled group/cross-repo trace
|
|
467
|
+
* presents 1-based endpoints (via resolveSymbolForGroup), so convert the hop
|
|
468
|
+
* lines here too — otherwise one response mixes 1-based endpoints with 0-based
|
|
469
|
+
* hops (#2380). Single-repo `trace` dispatches directly (not through this
|
|
470
|
+
* port) and stays 0-based (documented full-parity follow-up).
|
|
471
|
+
*/
|
|
472
|
+
async traceForGroup(repo, params) {
|
|
473
|
+
const result = await this.trace(repo, params);
|
|
474
|
+
const hops = result.hops;
|
|
475
|
+
if (Array.isArray(hops)) {
|
|
476
|
+
for (const hop of hops) {
|
|
477
|
+
hop.startLine = toDisplayLine(hop.startLine);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return result;
|
|
481
|
+
}
|
|
456
482
|
/**
|
|
457
483
|
* Adapt the shared symbol resolver to the GroupToolPort contract. Used by the
|
|
458
484
|
* cross-repo trace path to locate which member repo an endpoint lives in and
|
|
@@ -470,8 +496,8 @@ export class LocalBackend {
|
|
|
470
496
|
name: s.name,
|
|
471
497
|
type: s.type,
|
|
472
498
|
filePath: s.filePath,
|
|
473
|
-
startLine: s.startLine,
|
|
474
|
-
endLine: s.endLine,
|
|
499
|
+
startLine: toDisplayLine(s.startLine),
|
|
500
|
+
endLine: toDisplayLine(s.endLine),
|
|
475
501
|
},
|
|
476
502
|
};
|
|
477
503
|
}
|
|
@@ -483,7 +509,7 @@ export class LocalBackend {
|
|
|
483
509
|
name: c.name,
|
|
484
510
|
type: c.type,
|
|
485
511
|
filePath: c.filePath,
|
|
486
|
-
startLine: c.startLine,
|
|
512
|
+
startLine: toDisplayLine(c.startLine),
|
|
487
513
|
})),
|
|
488
514
|
};
|
|
489
515
|
}
|
|
@@ -1564,8 +1590,8 @@ export class LocalBackend {
|
|
|
1564
1590
|
name: sym.name,
|
|
1565
1591
|
type: sym.type,
|
|
1566
1592
|
filePath: sym.filePath,
|
|
1567
|
-
startLine: sym.startLine,
|
|
1568
|
-
endLine: sym.endLine,
|
|
1593
|
+
startLine: toDisplayLine(sym.startLine),
|
|
1594
|
+
endLine: toDisplayLine(sym.endLine),
|
|
1569
1595
|
...(module ? { module } : {}),
|
|
1570
1596
|
...(includeContent && content ? { content } : {}),
|
|
1571
1597
|
};
|
|
@@ -1652,7 +1678,7 @@ export class LocalBackend {
|
|
|
1652
1678
|
// path, leaving the success-path response shape byte-identical.
|
|
1653
1679
|
const warnings = [];
|
|
1654
1680
|
if (!ftsUsed) {
|
|
1655
|
-
warnings.push(
|
|
1681
|
+
warnings.push(ftsDegradedWarning());
|
|
1656
1682
|
}
|
|
1657
1683
|
// #2331: a CJK query against a server process resolving
|
|
1658
1684
|
// GITNEXUS_FTS_CJK_SEGMENTATION to 'none' silently misses sub-phrase
|
|
@@ -1791,8 +1817,11 @@ export class LocalBackend {
|
|
|
1791
1817
|
name: sym.name || sym[1],
|
|
1792
1818
|
type: sym.type || sym[2],
|
|
1793
1819
|
filePath: sym.filePath || sym[3],
|
|
1794
|
-
|
|
1795
|
-
|
|
1820
|
+
// Raw 0-based here — `bm25Search` is only called from `query()`,
|
|
1821
|
+
// whose aggregation loop applies `toDisplayLine` once (see below).
|
|
1822
|
+
// Converting here too would double-shift BM25-matched lines (#2380).
|
|
1823
|
+
startLine: sym.startLine ?? sym[4],
|
|
1824
|
+
endLine: sym.endLine ?? sym[5],
|
|
1796
1825
|
bm25Score: bm25Result.score,
|
|
1797
1826
|
});
|
|
1798
1827
|
}
|
|
@@ -1927,8 +1956,20 @@ export class LocalBackend {
|
|
|
1927
1956
|
}
|
|
1928
1957
|
return results;
|
|
1929
1958
|
}
|
|
1930
|
-
catch {
|
|
1931
|
-
//
|
|
1959
|
+
catch (err) {
|
|
1960
|
+
// Embeddings disabled is the common, silent case. But a pruned or
|
|
1961
|
+
// Node-unloadable optional stack (#2370/#2372) also lands here — surface it
|
|
1962
|
+
// once so semantic search doesn't silently degrade to BM25 with no hint
|
|
1963
|
+
// (the exact silent-degradation mode #2370 exists to fix). Emitted once per
|
|
1964
|
+
// LocalBackend instance to keep stderr quiet on hot paths (like the VECTOR
|
|
1965
|
+
// fallback above). All other errors stay silent, as before.
|
|
1966
|
+
const message = err instanceof Error ? err.message : '';
|
|
1967
|
+
if (!this.warnedMissingEmbeddingStack &&
|
|
1968
|
+
(isMissingLocalEmbeddingStackMessage(message) ||
|
|
1969
|
+
isLocalEmbeddingRuntimeBlockerMessage(message))) {
|
|
1970
|
+
this.warnedMissingEmbeddingStack = true;
|
|
1971
|
+
logger.warn(`GitNexus [query:vector]: ${message}`);
|
|
1972
|
+
}
|
|
1932
1973
|
return [];
|
|
1933
1974
|
}
|
|
1934
1975
|
}
|
|
@@ -2376,7 +2417,7 @@ export class LocalBackend {
|
|
|
2376
2417
|
name: c.name,
|
|
2377
2418
|
kind: c.type,
|
|
2378
2419
|
filePath: c.filePath,
|
|
2379
|
-
line: c.startLine,
|
|
2420
|
+
line: toDisplayLine(c.startLine),
|
|
2380
2421
|
score: Number(c.score.toFixed(2)),
|
|
2381
2422
|
})),
|
|
2382
2423
|
};
|
|
@@ -2583,8 +2624,8 @@ export class LocalBackend {
|
|
|
2583
2624
|
name: sym.name || sym[1],
|
|
2584
2625
|
kind: symKind,
|
|
2585
2626
|
filePath: sym.filePath || sym[3],
|
|
2586
|
-
startLine: sym.startLine
|
|
2587
|
-
endLine: sym.endLine
|
|
2627
|
+
startLine: toDisplayLine(sym.startLine ?? sym[4]),
|
|
2628
|
+
endLine: toDisplayLine(sym.endLine ?? sym[5]),
|
|
2588
2629
|
...(include_content && (sym.content || sym[6]) ? { content: sym.content || sym[6] } : {}),
|
|
2589
2630
|
...(methodMetadata ? { methodMetadata } : {}),
|
|
2590
2631
|
},
|
|
@@ -2660,7 +2701,7 @@ export class LocalBackend {
|
|
|
2660
2701
|
name: c.name,
|
|
2661
2702
|
kind: c.type,
|
|
2662
2703
|
filePath: c.filePath,
|
|
2663
|
-
line: c.startLine,
|
|
2704
|
+
line: toDisplayLine(c.startLine),
|
|
2664
2705
|
score: Number(c.score.toFixed(2)),
|
|
2665
2706
|
})),
|
|
2666
2707
|
},
|
|
@@ -2678,11 +2719,14 @@ export class LocalBackend {
|
|
|
2678
2719
|
return {
|
|
2679
2720
|
anchorClause: 'a.id STARTS WITH $idPrefix AND a.startLine >= $symStart AND a.startLine <= $symEnd',
|
|
2680
2721
|
queryParams: { idPrefix, symStart: sym.startLine + 1, symEnd: sym.endLine + 1 },
|
|
2722
|
+
// Display anchor is 1-based, matching the ambiguous-candidate branch and
|
|
2723
|
+
// the context/query/impact tools (#2380). This is display-only — the
|
|
2724
|
+
// BasicBlock join above uses the raw `sym.startLine + 1` in `symStart`.
|
|
2681
2725
|
anchor: {
|
|
2682
2726
|
file: sym.filePath,
|
|
2683
2727
|
symbol: sym.name,
|
|
2684
|
-
startLine: sym.startLine,
|
|
2685
|
-
endLine: sym.endLine,
|
|
2728
|
+
startLine: toDisplayLine(sym.startLine),
|
|
2729
|
+
endLine: toDisplayLine(sym.endLine),
|
|
2686
2730
|
},
|
|
2687
2731
|
};
|
|
2688
2732
|
}
|
|
@@ -4016,7 +4060,7 @@ export class LocalBackend {
|
|
|
4016
4060
|
name: c.name,
|
|
4017
4061
|
kind: c.type,
|
|
4018
4062
|
filePath: c.filePath,
|
|
4019
|
-
line: c.startLine,
|
|
4063
|
+
line: toDisplayLine(c.startLine),
|
|
4020
4064
|
score: Number(c.score.toFixed(2)),
|
|
4021
4065
|
})),
|
|
4022
4066
|
};
|
|
@@ -4069,7 +4113,7 @@ export class LocalBackend {
|
|
|
4069
4113
|
name: c.name,
|
|
4070
4114
|
kind: c.type,
|
|
4071
4115
|
filePath: c.filePath,
|
|
4072
|
-
line: c.startLine,
|
|
4116
|
+
line: toDisplayLine(c.startLine),
|
|
4073
4117
|
score: Number(c.score.toFixed(2)),
|
|
4074
4118
|
impactedCount: summary?.impactedCount ?? 0,
|
|
4075
4119
|
risk: summary?.risk ?? 'UNKNOWN',
|
|
@@ -34,8 +34,10 @@ export declare function splitCalleeIds(raw: unknown): string[];
|
|
|
34
34
|
* Contract version of the mode:'pdg' impact result shape. A stable discriminator
|
|
35
35
|
* for external MCP/agent consumers — distinct from the DB INCREMENTAL_SCHEMA_VERSION.
|
|
36
36
|
* Bump on any breaking change to the PDG result fields.
|
|
37
|
+
* v2: `startLine` in the result is now 1-based display (#2380), matching the
|
|
38
|
+
* context/query/impact tools (was 0-based).
|
|
37
39
|
*/
|
|
38
|
-
export declare const PDG_RESULT_VERSION:
|
|
40
|
+
export declare const PDG_RESULT_VERSION: 2;
|
|
39
41
|
/** A reachable dependence block resolved to its source statement. */
|
|
40
42
|
export interface PdgStatement {
|
|
41
43
|
/** 1-based source line where the statement's block starts. */
|
|
@@ -105,7 +107,7 @@ export interface PdgInterproceduralImpact {
|
|
|
105
107
|
export interface PdgImpactBaseResult extends PdgImpactParityFields {
|
|
106
108
|
mode: 'pdg';
|
|
107
109
|
/** Contract version of the mode:'pdg' impact result shape; bump on any breaking change to the PDG result fields. */
|
|
108
|
-
pdgResultVersion:
|
|
110
|
+
pdgResultVersion: 2;
|
|
109
111
|
target: PdgImpactTarget;
|
|
110
112
|
direction: 'upstream' | 'downstream';
|
|
111
113
|
impactedCount: number;
|
|
@@ -172,7 +174,7 @@ export interface PdgImpactDegradedResult extends PdgImpactBaseResult {
|
|
|
172
174
|
export interface PdgImpactErrorResult {
|
|
173
175
|
mode?: 'pdg';
|
|
174
176
|
/** Contract version of the mode:'pdg' impact result shape; bump on any breaking change to the PDG result fields. */
|
|
175
|
-
pdgResultVersion:
|
|
177
|
+
pdgResultVersion: 2;
|
|
176
178
|
error: string;
|
|
177
179
|
target: PdgImpactTarget;
|
|
178
180
|
direction: 'upstream' | 'downstream';
|
|
@@ -9,6 +9,7 @@ import path from 'path';
|
|
|
9
9
|
import { loadMeta } from '../../storage/repo-manager.js';
|
|
10
10
|
import { IMPACT_MAX_DEPTH, PDG_QUERY_DEFAULT_LIMIT, PDG_QUERY_MAX_LIMIT } from '../tools.js';
|
|
11
11
|
import { CALLEES_TRUNCATED_SENTINEL, CALLEE_ID_SEP } from '../../core/ingestion/cfg/emit.js';
|
|
12
|
+
import { toDisplayLine } from './line-display.js';
|
|
12
13
|
import { decodeCallSummary } from '../../core/ingestion/taint/call-summary-codec.js';
|
|
13
14
|
import { decodeReachingDefReason } from '../../core/ingestion/cfg/reaching-def-reason-codec.js';
|
|
14
15
|
import { getProviderForFile } from '../../core/ingestion/languages/index.js';
|
|
@@ -84,8 +85,10 @@ export function splitCalleeIds(raw) {
|
|
|
84
85
|
* Contract version of the mode:'pdg' impact result shape. A stable discriminator
|
|
85
86
|
* for external MCP/agent consumers — distinct from the DB INCREMENTAL_SCHEMA_VERSION.
|
|
86
87
|
* Bump on any breaking change to the PDG result fields.
|
|
88
|
+
* v2: `startLine` in the result is now 1-based display (#2380), matching the
|
|
89
|
+
* context/query/impact tools (was 0-based).
|
|
87
90
|
*/
|
|
88
|
-
export const PDG_RESULT_VERSION =
|
|
91
|
+
export const PDG_RESULT_VERSION = 2;
|
|
89
92
|
/**
|
|
90
93
|
* FU-B-2 intra-block def→use line walk. Given a block's self REACHING_DEF
|
|
91
94
|
* def→use line PAIRS (every `defLine → useLine` step decoded from the edge
|
|
@@ -519,7 +522,7 @@ function assemblePdgImpactResult(input) {
|
|
|
519
522
|
name: s.name,
|
|
520
523
|
type: s.type,
|
|
521
524
|
filePath: s.filePath,
|
|
522
|
-
...(s.startLine !== undefined ? { startLine: s.startLine } : {}),
|
|
525
|
+
...(s.startLine !== undefined ? { startLine: toDisplayLine(s.startLine) } : {}),
|
|
523
526
|
...(s.ambiguous ? { ambiguous: true } : {}),
|
|
524
527
|
...(s.id === null ? { unresolved: true } : {}),
|
|
525
528
|
pdgEvidence: (s.id === null ? 'degraded' : 'owner-projection'),
|
package/dist/mcp/resources.js
CHANGED
|
@@ -365,6 +365,7 @@ additional_node_types: "Multi-language: Struct, Enum, Macro, Typedef, Union, Nam
|
|
|
365
365
|
|
|
366
366
|
node_properties:
|
|
367
367
|
common: "name (STRING), filePath (STRING), startLine (INT32), endLine (INT32)"
|
|
368
|
+
line_numbers: "startLine/endLine on symbol nodes are 0-BASED (tree-sitter rows) in storage AND in raw Cypher results. The context, query, impact, group/cross-repo trace, and explain/pdg_query (symbol anchor) tools present them 1-BASED (editor / sed / less -N aligned), so a symbol spans editor lines (startLine+1)..(endLine+1) — e.g. sed '<startLine+1>,<endLine+1>!d' <file>. Single-repo trace symbol lines stay 0-BASED for now (full-parity follow-up). content holds the exact symbol span. (BasicBlock / PDG statement lines are separately 1-based.) (#2377, #2380)"
|
|
368
369
|
Method: "parameterCount (INT32), returnType (STRING), isVariadic (BOOL), visibility (STRING), isStatic (BOOL), isAbstract (BOOL), isFinal (BOOL), isVirtual (BOOL), isOverride (BOOL), isAsync (BOOL), isPartial (BOOL), requiredParameterCount (INT32), parameterTypes (STRING[]), annotations (STRING[])"
|
|
369
370
|
Function: "parameterCount (INT32), returnType (STRING), isVariadic (BOOL), visibility (STRING), isStatic (BOOL), isAbstract (BOOL), isFinal (BOOL), isAsync (BOOL), parameterTypes (STRING[]), annotations (STRING[])"
|
|
370
371
|
Property: "declaredType (STRING) — the field's type annotation (e.g., 'Address', 'City'). Used for field-access chain resolution."
|
package/dist/mcp/tools.js
CHANGED
|
@@ -379,7 +379,7 @@ MODE (opt-in): "callgraph" (default) walks symbol→symbol edges (CALLS/IMPORTS/
|
|
|
379
379
|
|
|
380
380
|
STATEMENT-ANCHORED PDG SLICE: with mode:'pdg', pass "line" (1-based source line within the target symbol) to seed the dependence slice on the statement at that line and return what depends on it in affectedStatements (line + text). Inter-procedural symbols are still reported through interproceduralByDepth/pdgInterprocedural and the compatibility byDepth bucket. Without "line", pdg returns whole-symbol inter-procedural reach plus local whole-symbol PDG diagnostics.
|
|
381
381
|
|
|
382
|
-
PDG OUTPUT CONTRACT: every mode:'pdg' result (success, empty, degraded, or error) carries pdgResultVersion:
|
|
382
|
+
PDG OUTPUT CONTRACT: every mode:'pdg' result (success, empty, degraded, or error) carries pdgResultVersion:2 — a stable discriminator for external consumers that bumps on any breaking change to the PDG result shape (distinct from the DB schema version). Successful PDG results include mode:'pdg', a full target envelope (id/name/type/filePath), affectedStatements, affectedStatementCount, interproceduralByDepth/pdgInterprocedural for cross-function reach, compatibility byDepth/byDepthCounts, risk:'UNKNOWN', and a note describing the unified contract. Degraded PDG results (no-layer, sub-layer-missing, unknown) keep mode:'pdg', pdgResultVersion:2, target metadata when the target resolves, risk:'UNKNOWN', note/remediation, and empty byDepth parity fields — never a false-safe zero. If depth and limit both bound the slice, truncatedByReasons reports both causes while truncatedBy remains scalar.
|
|
383
383
|
|
|
384
384
|
WHEN TO USE: Before making code changes — especially refactoring, renaming, or modifying shared code. Shows what would break.
|
|
385
385
|
AFTER THIS: Review d=1 items (WILL BREAK). Use context() on high-risk symbols.
|
package/dist/server/api.js
CHANGED
|
@@ -18,6 +18,7 @@ import { isValidQueryParams } from '../core/lbug/query-params.js';
|
|
|
18
18
|
import { NODE_TABLES } from '../_shared/index.js';
|
|
19
19
|
import { searchFTSFromLbug } from '../core/search/bm25-index.js';
|
|
20
20
|
import { hybridSearch } from '../core/search/hybrid-search.js';
|
|
21
|
+
import { ftsDegradedWarning } from '../core/search/fts-indexes.js';
|
|
21
22
|
import { LocalBackend } from '../mcp/local/local-backend.js';
|
|
22
23
|
import { mountMCPEndpoints } from './mcp-http.js';
|
|
23
24
|
import { fileURLToPath } from 'url';
|
|
@@ -1140,8 +1141,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
|
|
|
1140
1141
|
}, { readOnly: true });
|
|
1141
1142
|
const response = { results: results.searchResults ?? results };
|
|
1142
1143
|
if (results.ftsAvailable === false) {
|
|
1143
|
-
response.warning =
|
|
1144
|
-
'FTS indexes missing — keyword search degraded. Run: gitnexus analyze --repair-fts (or gitnexus analyze --force) to rebuild indexes.';
|
|
1144
|
+
response.warning = ftsDegradedWarning();
|
|
1145
1145
|
}
|
|
1146
1146
|
res.json(response);
|
|
1147
1147
|
}
|
|
@@ -52,7 +52,7 @@ import { fileURLToPath } from 'url';
|
|
|
52
52
|
// the main thread (the #1983 OOM). Because the two stores share this version,
|
|
53
53
|
// any future change to the `ParsedFile` serialization shape MUST bump
|
|
54
54
|
// SCHEMA_BUMP so both invalidate in lockstep.
|
|
55
|
-
const SCHEMA_BUMP =
|
|
55
|
+
const SCHEMA_BUMP = 12; // #2391 follow-up: extractPythonModuleConstants changed what it EMITS for the same source (binding mutual-exclusivity clears stale imports; RHS refs are snapshotted; `$imp$N` aliases). `moduleConstants` is cached verbatim, so a warm shard built pre-fix would replay stale/WRONG folds and the correctness fixes would silently no-op on upgrade — bump to force re-extraction. (11 = #2391: ExtractedDecoratorRoute gained `routePathExpr`/`routePathOperands` + ParseWorkerResult gained per-file `moduleConstants`. 10 = PR #2200: Property nodes gained `rawDeclaredType` + `annotations` for Spring DI)
|
|
56
56
|
const GITNEXUS_PKG_VERSION = (() => {
|
|
57
57
|
try {
|
|
58
58
|
// package.json sits at gitnexus/package.json — two levels up from
|
|
@@ -242,8 +242,13 @@ export interface RepoMeta {
|
|
|
242
242
|
* URL-only id). The incremental writeback preserves unchanged-file rows, so a
|
|
243
243
|
* top-up against a pre-v5 index would strand old url-keyed Route nodes alongside
|
|
244
244
|
* new composite-keyed ones — force a full re-analyze instead.
|
|
245
|
+
* v6: line-number storage flipped to uniform 0-based for the last 1-based
|
|
246
|
+
* GraphNode emitters — COBOL/JCL/markdown/scope (#2377/#2379/#2380). Incremental
|
|
247
|
+
* writeback preserves unchanged-file rows, so a top-up against a pre-v6 index
|
|
248
|
+
* would MIX old 1-based rows with new 0-based ones — and the 1-based MCP display
|
|
249
|
+
* would render the stale rows one line too high — so force a full re-analyze.
|
|
245
250
|
*/
|
|
246
|
-
export declare const INCREMENTAL_SCHEMA_VERSION =
|
|
251
|
+
export declare const INCREMENTAL_SCHEMA_VERSION = 6;
|
|
247
252
|
export interface IndexedRepo {
|
|
248
253
|
repoPath: string;
|
|
249
254
|
storagePath: string;
|
|
@@ -90,8 +90,13 @@ export const registryPathEquals = (a, b) => process.platform === 'win32' ? a.toL
|
|
|
90
90
|
* URL-only id). The incremental writeback preserves unchanged-file rows, so a
|
|
91
91
|
* top-up against a pre-v5 index would strand old url-keyed Route nodes alongside
|
|
92
92
|
* new composite-keyed ones — force a full re-analyze instead.
|
|
93
|
+
* v6: line-number storage flipped to uniform 0-based for the last 1-based
|
|
94
|
+
* GraphNode emitters — COBOL/JCL/markdown/scope (#2377/#2379/#2380). Incremental
|
|
95
|
+
* writeback preserves unchanged-file rows, so a top-up against a pre-v6 index
|
|
96
|
+
* would MIX old 1-based rows with new 0-based ones — and the 1-based MCP display
|
|
97
|
+
* would render the stale rows one line too high — so force a full re-analyze.
|
|
93
98
|
*/
|
|
94
|
-
export const INCREMENTAL_SCHEMA_VERSION =
|
|
99
|
+
export const INCREMENTAL_SCHEMA_VERSION = 6;
|
|
95
100
|
const GITNEXUS_DIR = '.gitnexus';
|
|
96
101
|
const GITNEXUS_EXCLUDE_ENTRY = `${GITNEXUS_DIR}/`;
|
|
97
102
|
export const INDEX_METADATA_FILE = 'gitnexus.json';
|
|
@@ -391,6 +391,49 @@ function buildAfterToolContext(input) {
|
|
|
391
391
|
return parts.length > 0 ? parts.join('\n\n') : null;
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Fallback augmentation for the #2396 path: when a GitNexus process holds the
|
|
396
|
+
* lbug DB write lock the CLI `augment` can't run, so point the agent at the MCP
|
|
397
|
+
* `query` tool instead. Phrased conditionally ("if the MCP tools are live") so it
|
|
398
|
+
* stays truthful on every owner path — a confirmed MCP owner, a `serve` owner, or
|
|
399
|
+
* a fail-closed probe where no server is actually confirmed. `pattern` is embedded
|
|
400
|
+
* verbatim; the caller (writeAdditionalContext) JSON-escapes it structurally.
|
|
401
|
+
*/
|
|
402
|
+
function buildMcpQueryHint(pattern) {
|
|
403
|
+
return (
|
|
404
|
+
`[GitNexus] Local augment is unavailable (the graph DB is held by another ` +
|
|
405
|
+
`GitNexus process). If the GitNexus MCP tools are live in this session, call ` +
|
|
406
|
+
`the GitNexus \`query\` MCP tool (e.g. mcp__gitnexus__query) with ` +
|
|
407
|
+
`search_query "${pattern}".`
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* #2396 throttle: emit the MCP-query hint at most once per repo per window, so an
|
|
413
|
+
* owner-locked session isn't nudged on every search. Window (ms) via
|
|
414
|
+
* GITNEXUS_MCP_HINT_THROTTLE_MS (default 10min; 0/invalid disables). Best-effort —
|
|
415
|
+
* any fs error falls back to emitting.
|
|
416
|
+
* ponytail: per-repo mtime marker, shared across concurrent sessions on the same
|
|
417
|
+
* repo; add per-session dedup only if that sharing becomes a problem.
|
|
418
|
+
*/
|
|
419
|
+
function shouldEmitMcpHint(gitNexusDir) {
|
|
420
|
+
const raw = process.env.GITNEXUS_MCP_HINT_THROTTLE_MS;
|
|
421
|
+
const windowMs = raw === undefined || raw === '' ? 600000 : Number(raw);
|
|
422
|
+
if (!Number.isFinite(windowMs) || windowMs <= 0) return true;
|
|
423
|
+
const marker = path.join(gitNexusDir, '.mcp-hint-shown');
|
|
424
|
+
try {
|
|
425
|
+
if (Date.now() - fs.statSync(marker).mtimeMs < windowMs) return false;
|
|
426
|
+
} catch {
|
|
427
|
+
/* marker missing/unreadable → emit */
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
fs.writeFileSync(marker, '');
|
|
431
|
+
} catch {
|
|
432
|
+
/* best-effort; still emit */
|
|
433
|
+
}
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
|
|
394
437
|
function runAugment(gitNexusDir, cwd, pattern) {
|
|
395
438
|
// Acquire the per-repo slot BEFORE the DB-owner probe (#2163): the probe
|
|
396
439
|
// itself spawns lsof/ps, so it must be bounded by the same ≤3-per-repo cap
|
|
@@ -410,12 +453,16 @@ function runAugment(gitNexusDir, cwd, pattern) {
|
|
|
410
453
|
}
|
|
411
454
|
try {
|
|
412
455
|
if (hasGitNexusServerOwner(gitNexusDir)) {
|
|
413
|
-
//
|
|
414
|
-
//
|
|
456
|
+
// #2396: the MCP server holds the DB write lock, so a competing CLI
|
|
457
|
+
// `augment` would only contend on it (LadybugDB is single-writer). The
|
|
458
|
+
// session has the GitNexus MCP tools live — route the augmentation to the
|
|
459
|
+
// agent via additionalContext instead of dropping it. Mirror the skip
|
|
460
|
+
// reason to stderr only under GITNEXUS_DEBUG (strict-runner contract,
|
|
461
|
+
// #1913); the hint itself rides the sanctioned additionalContext channel.
|
|
415
462
|
if (isDebugEnabled()) {
|
|
416
463
|
process.stderr.write('[GitNexus] augment skipped: MCP server owns DB\n');
|
|
417
464
|
}
|
|
418
|
-
return '';
|
|
465
|
+
return shouldEmitMcpHint(gitNexusDir) ? buildMcpQueryHint(pattern) : '';
|
|
419
466
|
}
|
|
420
467
|
const cliPath = resolveCliPath();
|
|
421
468
|
const child = runGitNexusCli(cliPath, ['augment', '--', pattern], cwd, 7000);
|