opencode-codebase-index 0.20.1 → 0.21.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +23 -6
- package/dist/cli.cjs +635 -334
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +629 -330
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +809 -508
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +808 -509
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +732 -457
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +735 -462
- package/dist/pi-extension.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +3 -3
package/dist/cli.cjs
CHANGED
|
@@ -664,8 +664,10 @@ __export(cli_exports, {
|
|
|
664
664
|
parseArgs: () => parseArgs
|
|
665
665
|
});
|
|
666
666
|
module.exports = __toCommonJS(cli_exports);
|
|
667
|
+
|
|
668
|
+
// src/adapters/mcp/cli.ts
|
|
667
669
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
668
|
-
var
|
|
670
|
+
var import_fs20 = require("fs");
|
|
669
671
|
var os8 = __toESM(require("os"), 1);
|
|
670
672
|
var path29 = __toESM(require("path"), 1);
|
|
671
673
|
var import_url = require("url");
|
|
@@ -4152,24 +4154,62 @@ var os2 = __toESM(require("os"), 1);
|
|
|
4152
4154
|
var path5 = __toESM(require("path"), 1);
|
|
4153
4155
|
var module2 = __toESM(require("module"), 1);
|
|
4154
4156
|
var import_node_url = require("url");
|
|
4157
|
+
|
|
4158
|
+
// src/identity-catalog.json
|
|
4159
|
+
var identity_catalog_default = {
|
|
4160
|
+
product: {
|
|
4161
|
+
current: {
|
|
4162
|
+
productName: "opencode-codebase-index",
|
|
4163
|
+
packageName: "opencode-codebase-index",
|
|
4164
|
+
repository: "https://github.com/Helweg/opencode-codebase-index",
|
|
4165
|
+
mcpBinary: "opencode-codebase-index-mcp",
|
|
4166
|
+
mcpServerName: "opencode-codebase-index"
|
|
4167
|
+
},
|
|
4168
|
+
future: {
|
|
4169
|
+
productName: "open-codebase-index",
|
|
4170
|
+
packageName: "open-codebase-index",
|
|
4171
|
+
repository: "https://github.com/Helweg/open-codebase-index",
|
|
4172
|
+
mcpBinary: "open-codebase-index-mcp",
|
|
4173
|
+
mcpServerName: "open-codebase-index"
|
|
4174
|
+
}
|
|
4175
|
+
},
|
|
4176
|
+
native: {
|
|
4177
|
+
binaryName: "codebase-index-native"
|
|
4178
|
+
}
|
|
4179
|
+
};
|
|
4180
|
+
|
|
4181
|
+
// src/identity-catalog.ts
|
|
4182
|
+
var IDENTITY_CATALOG = identity_catalog_default;
|
|
4183
|
+
var CURRENT_PRODUCT = IDENTITY_CATALOG.product.current;
|
|
4184
|
+
var FUTURE_PRODUCT = IDENTITY_CATALOG.product.future;
|
|
4185
|
+
var MCP_SERVER_CURRENT_NAME = CURRENT_PRODUCT.mcpServerName;
|
|
4186
|
+
var MCP_BINARY_CURRENT_NAME = CURRENT_PRODUCT.mcpBinary;
|
|
4187
|
+
var STABLE_NATIVE_BINARY_NAME = IDENTITY_CATALOG.native.binaryName;
|
|
4188
|
+
|
|
4189
|
+
// src/native/binding.ts
|
|
4155
4190
|
var import_meta = {};
|
|
4156
|
-
function
|
|
4157
|
-
const platform2 = os2.platform();
|
|
4158
|
-
const arch2 = os2.arch();
|
|
4159
|
-
let bindingName;
|
|
4191
|
+
function getNativeBindingFilename(platform2 = os2.platform(), arch2 = os2.arch()) {
|
|
4160
4192
|
if (platform2 === "darwin" && arch2 === "arm64") {
|
|
4161
|
-
|
|
4162
|
-
}
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
} else if (platform2 === "win32" && arch2 === "x64") {
|
|
4169
|
-
bindingName = "codebase-index-native.win32-x64-msvc.node";
|
|
4170
|
-
} else {
|
|
4171
|
-
throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
|
|
4193
|
+
return `${STABLE_NATIVE_BINARY_NAME}.darwin-arm64.node`;
|
|
4194
|
+
}
|
|
4195
|
+
if (platform2 === "darwin" && arch2 === "x64") {
|
|
4196
|
+
return `${STABLE_NATIVE_BINARY_NAME}.darwin-x64.node`;
|
|
4197
|
+
}
|
|
4198
|
+
if (platform2 === "linux" && arch2 === "x64") {
|
|
4199
|
+
return `${STABLE_NATIVE_BINARY_NAME}.linux-x64-gnu.node`;
|
|
4172
4200
|
}
|
|
4201
|
+
if (platform2 === "linux" && arch2 === "arm64") {
|
|
4202
|
+
return `${STABLE_NATIVE_BINARY_NAME}.linux-arm64-gnu.node`;
|
|
4203
|
+
}
|
|
4204
|
+
if (platform2 === "win32" && arch2 === "x64") {
|
|
4205
|
+
return `${STABLE_NATIVE_BINARY_NAME}.win32-x64-msvc.node`;
|
|
4206
|
+
}
|
|
4207
|
+
throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
|
|
4208
|
+
}
|
|
4209
|
+
function resolveNativeBindingPath(packageRoot, platform2 = os2.platform(), arch2 = os2.arch()) {
|
|
4210
|
+
return path5.join(packageRoot, "native", getNativeBindingFilename(platform2, arch2));
|
|
4211
|
+
}
|
|
4212
|
+
function getNativeBinding() {
|
|
4173
4213
|
let currentDir;
|
|
4174
4214
|
let requireTarget;
|
|
4175
4215
|
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
@@ -4185,7 +4225,7 @@ function getNativeBinding() {
|
|
|
4185
4225
|
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
4186
4226
|
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path5.join("src", "native"));
|
|
4187
4227
|
const packageRoot = isDevMode ? path5.resolve(currentDir, "../..") : path5.resolve(currentDir, "..");
|
|
4188
|
-
const nativePath =
|
|
4228
|
+
const nativePath = resolveNativeBindingPath(packageRoot);
|
|
4189
4229
|
const require2 = module2.createRequire(requireTarget);
|
|
4190
4230
|
return require2(nativePath);
|
|
4191
4231
|
}
|
|
@@ -5515,8 +5555,37 @@ function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
|
5515
5555
|
const fallbackPath = path10.join(mainRepoRoot, relativePath);
|
|
5516
5556
|
return (0, import_fs7.existsSync)(fallbackPath) ? fallbackPath : null;
|
|
5517
5557
|
}
|
|
5518
|
-
function
|
|
5519
|
-
|
|
5558
|
+
function getProjectConfigCandidatePaths(projectRoot, host) {
|
|
5559
|
+
const candidates = [path10.join(projectRoot, getProjectConfigRelativePath(host))];
|
|
5560
|
+
if (host !== "opencode") {
|
|
5561
|
+
candidates.push(path10.join(projectRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH));
|
|
5562
|
+
}
|
|
5563
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
5564
|
+
if (mainRepoRoot) {
|
|
5565
|
+
candidates.push(path10.join(mainRepoRoot, getProjectConfigRelativePath(host)));
|
|
5566
|
+
if (host !== "opencode") {
|
|
5567
|
+
candidates.push(path10.join(mainRepoRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH));
|
|
5568
|
+
}
|
|
5569
|
+
}
|
|
5570
|
+
return [...new Set(candidates)];
|
|
5571
|
+
}
|
|
5572
|
+
function isProjectIndexPathOwnedByProject(projectRoot, indexPath, host) {
|
|
5573
|
+
const projectRoots = [projectRoot];
|
|
5574
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
5575
|
+
if (mainRepoRoot) {
|
|
5576
|
+
projectRoots.push(mainRepoRoot);
|
|
5577
|
+
}
|
|
5578
|
+
const ownedIndexPaths = projectRoots.flatMap((root) => {
|
|
5579
|
+
const indexPaths = [path10.join(root, getProjectIndexRelativePath(host))];
|
|
5580
|
+
if (host !== "opencode") {
|
|
5581
|
+
indexPaths.push(path10.join(root, OPENCODE_PROJECT_INDEX_RELATIVE_PATH));
|
|
5582
|
+
}
|
|
5583
|
+
return indexPaths;
|
|
5584
|
+
});
|
|
5585
|
+
const canonicalIndexPath = canonicalizePathForComparison(indexPath);
|
|
5586
|
+
return ownedIndexPaths.some(
|
|
5587
|
+
(ownedPath) => canonicalizePathForComparison(ownedPath) === canonicalIndexPath
|
|
5588
|
+
);
|
|
5520
5589
|
}
|
|
5521
5590
|
function hasHostProjectConfig(projectRoot, host) {
|
|
5522
5591
|
return (0, import_fs7.existsSync)(path10.join(projectRoot, getProjectConfigRelativePath(host)));
|
|
@@ -5524,7 +5593,7 @@ function hasHostProjectConfig(projectRoot, host) {
|
|
|
5524
5593
|
function hasHostGlobalConfig(host) {
|
|
5525
5594
|
return (0, import_fs7.existsSync)(getGlobalConfigPath(host));
|
|
5526
5595
|
}
|
|
5527
|
-
function getGlobalIndexPath(host
|
|
5596
|
+
function getGlobalIndexPath(host) {
|
|
5528
5597
|
switch (host) {
|
|
5529
5598
|
case "opencode":
|
|
5530
5599
|
return path10.join(os4.homedir(), ".opencode", "global-index");
|
|
@@ -5534,7 +5603,7 @@ function getGlobalIndexPath(host = "opencode") {
|
|
|
5534
5603
|
return path10.join(os4.homedir(), ".codebase-index", "global-index");
|
|
5535
5604
|
}
|
|
5536
5605
|
}
|
|
5537
|
-
function getGlobalConfigPath(host
|
|
5606
|
+
function getGlobalConfigPath(host) {
|
|
5538
5607
|
switch (host) {
|
|
5539
5608
|
case "opencode":
|
|
5540
5609
|
return path10.join(os4.homedir(), ".config", "opencode", "codebase-index.json");
|
|
@@ -5544,7 +5613,7 @@ function getGlobalConfigPath(host = "opencode") {
|
|
|
5544
5613
|
return path10.join(os4.homedir(), ".config", "codebase-index", "config.json");
|
|
5545
5614
|
}
|
|
5546
5615
|
}
|
|
5547
|
-
function resolveGlobalConfigPath(host
|
|
5616
|
+
function resolveGlobalConfigPath(host) {
|
|
5548
5617
|
const hostConfigPath = getGlobalConfigPath(host);
|
|
5549
5618
|
if ((0, import_fs7.existsSync)(hostConfigPath)) {
|
|
5550
5619
|
return hostConfigPath;
|
|
@@ -5557,7 +5626,7 @@ function resolveGlobalConfigPath(host = "opencode") {
|
|
|
5557
5626
|
}
|
|
5558
5627
|
return hostConfigPath;
|
|
5559
5628
|
}
|
|
5560
|
-
function resolveGlobalIndexPath(host
|
|
5629
|
+
function resolveGlobalIndexPath(host) {
|
|
5561
5630
|
const hostIndexPath = getGlobalIndexPath(host);
|
|
5562
5631
|
if ((0, import_fs7.existsSync)(hostIndexPath)) {
|
|
5563
5632
|
return hostIndexPath;
|
|
@@ -5573,37 +5642,35 @@ function resolveGlobalIndexPath(host = "opencode") {
|
|
|
5573
5642
|
}
|
|
5574
5643
|
return hostIndexPath;
|
|
5575
5644
|
}
|
|
5576
|
-
function resolveProjectConfigPath(projectRoot, host
|
|
5577
|
-
const
|
|
5578
|
-
|
|
5579
|
-
return hostConfigPath;
|
|
5580
|
-
}
|
|
5581
|
-
if (host !== "opencode") {
|
|
5582
|
-
const legacyConfigPath = path10.join(projectRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH);
|
|
5583
|
-
if ((0, import_fs7.existsSync)(legacyConfigPath)) {
|
|
5584
|
-
return legacyConfigPath;
|
|
5585
|
-
}
|
|
5586
|
-
}
|
|
5587
|
-
const hostFallback = resolveWorktreeFallbackPath(projectRoot, getProjectConfigRelativePath(host));
|
|
5588
|
-
if (hostFallback) {
|
|
5589
|
-
return hostFallback;
|
|
5590
|
-
}
|
|
5591
|
-
if (host !== "opencode") {
|
|
5592
|
-
const legacyFallback = resolveWorktreeFallbackPath(projectRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH);
|
|
5593
|
-
if (legacyFallback) {
|
|
5594
|
-
return legacyFallback;
|
|
5595
|
-
}
|
|
5596
|
-
}
|
|
5597
|
-
return hostConfigPath;
|
|
5598
|
-
}
|
|
5599
|
-
function resolveWritableProjectConfigPath(projectRoot, host = "opencode") {
|
|
5600
|
-
return path10.join(projectRoot, getProjectConfigRelativePath(host));
|
|
5645
|
+
function resolveProjectConfigPath(projectRoot, host) {
|
|
5646
|
+
const candidates = getProjectConfigCandidatePaths(projectRoot, host);
|
|
5647
|
+
return candidates.find((candidate) => (0, import_fs7.existsSync)(candidate)) ?? path10.join(projectRoot, getProjectConfigRelativePath(host));
|
|
5601
5648
|
}
|
|
5602
|
-
function resolveProjectIndexPath(projectRoot, scope, host
|
|
5649
|
+
function resolveProjectIndexPath(projectRoot, scope, host) {
|
|
5603
5650
|
if (scope === "global") {
|
|
5604
5651
|
return resolveGlobalIndexPath(host);
|
|
5605
5652
|
}
|
|
5606
5653
|
const localIndexPath = path10.join(projectRoot, getProjectIndexRelativePath(host));
|
|
5654
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
5655
|
+
if (mainRepoRoot) {
|
|
5656
|
+
if (hasHostProjectConfig(projectRoot, host)) {
|
|
5657
|
+
return localIndexPath;
|
|
5658
|
+
}
|
|
5659
|
+
if (host !== "opencode") {
|
|
5660
|
+
const localLegacyConfigPath = path10.join(projectRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH);
|
|
5661
|
+
if ((0, import_fs7.existsSync)(localLegacyConfigPath)) {
|
|
5662
|
+
return path10.join(projectRoot, OPENCODE_PROJECT_INDEX_RELATIVE_PATH);
|
|
5663
|
+
}
|
|
5664
|
+
const mainHostConfigPath = path10.join(mainRepoRoot, getProjectConfigRelativePath(host));
|
|
5665
|
+
const mainHostIndexPath = path10.join(mainRepoRoot, getProjectIndexRelativePath(host));
|
|
5666
|
+
const mainLegacyConfigPath = path10.join(mainRepoRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH);
|
|
5667
|
+
const mainLegacyIndexPath = path10.join(mainRepoRoot, OPENCODE_PROJECT_INDEX_RELATIVE_PATH);
|
|
5668
|
+
if (!(0, import_fs7.existsSync)(mainHostConfigPath) && !(0, import_fs7.existsSync)(mainHostIndexPath) && ((0, import_fs7.existsSync)(mainLegacyConfigPath) || (0, import_fs7.existsSync)(mainLegacyIndexPath))) {
|
|
5669
|
+
return mainLegacyIndexPath;
|
|
5670
|
+
}
|
|
5671
|
+
}
|
|
5672
|
+
return path10.join(mainRepoRoot, getProjectIndexRelativePath(host));
|
|
5673
|
+
}
|
|
5607
5674
|
if ((0, import_fs7.existsSync)(localIndexPath)) {
|
|
5608
5675
|
return localIndexPath;
|
|
5609
5676
|
}
|
|
@@ -5616,9 +5683,6 @@ function resolveProjectIndexPath(projectRoot, scope, host = "opencode") {
|
|
|
5616
5683
|
if (hasHostProjectConfig(projectRoot, host)) {
|
|
5617
5684
|
return localIndexPath;
|
|
5618
5685
|
}
|
|
5619
|
-
if (resolveWorktreeMainRepoRoot(projectRoot)) {
|
|
5620
|
-
return localIndexPath;
|
|
5621
|
-
}
|
|
5622
5686
|
const hostFallback = resolveWorktreeFallbackPath(projectRoot, getProjectIndexRelativePath(host));
|
|
5623
5687
|
if (hostFallback) {
|
|
5624
5688
|
return hostFallback;
|
|
@@ -7685,6 +7749,8 @@ function hasBlameMetadata(metadata) {
|
|
|
7685
7749
|
return blameFromMetadata(metadata) !== void 0;
|
|
7686
7750
|
}
|
|
7687
7751
|
var INDEX_METADATA_VERSION = "1";
|
|
7752
|
+
var PROJECT_PATH_STORAGE_VERSION = "2";
|
|
7753
|
+
var GLOBAL_PATH_STORAGE_VERSION = "1";
|
|
7688
7754
|
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
7689
7755
|
var SWIFT_PARSER_VERSION = "1";
|
|
7690
7756
|
var METAL_PARSER_VERSION = "1";
|
|
@@ -8015,18 +8081,20 @@ function selectIndexableChunks(chunks, limit, semanticOnly) {
|
|
|
8015
8081
|
const indexableChunks = semanticOnly ? chunks.filter((chunk) => chunk.chunkType !== "other") : chunks;
|
|
8016
8082
|
return selectChunksWithFileCoverage(indexableChunks, limit);
|
|
8017
8083
|
}
|
|
8018
|
-
function matchesHardSearchFilters(candidate, options) {
|
|
8084
|
+
function matchesHardSearchFilters(candidate, options, projectRoot) {
|
|
8019
8085
|
if (options?.fileType) {
|
|
8020
8086
|
const ext = candidate.metadata.filePath.split(".").pop()?.toLowerCase();
|
|
8021
8087
|
const requestedExtension = options.fileType.trim().toLowerCase().replace(/^\./, "");
|
|
8022
8088
|
if (ext !== requestedExtension) return false;
|
|
8023
8089
|
}
|
|
8024
8090
|
if (options?.directory) {
|
|
8025
|
-
const
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
const
|
|
8029
|
-
|
|
8091
|
+
const candidatePath = canonicalizePathForComparison(
|
|
8092
|
+
path14.resolve(projectRoot, candidate.metadata.filePath.replace(/\\/g, path14.sep))
|
|
8093
|
+
);
|
|
8094
|
+
const directoryPath = canonicalizePathForComparison(
|
|
8095
|
+
path14.resolve(projectRoot, options.directory.trim().replace(/\\/g, path14.sep))
|
|
8096
|
+
);
|
|
8097
|
+
if (!isPathWithinRoot2(candidatePath, directoryPath)) return false;
|
|
8030
8098
|
}
|
|
8031
8099
|
if (options?.chunkType && candidate.metadata.chunkType !== options.chunkType) {
|
|
8032
8100
|
return false;
|
|
@@ -8048,8 +8116,8 @@ function matchesHardSearchFilters(candidate, options) {
|
|
|
8048
8116
|
}
|
|
8049
8117
|
return true;
|
|
8050
8118
|
}
|
|
8051
|
-
function matchesSearchFilters(candidate, options, minScore) {
|
|
8052
|
-
return candidate.score >= minScore && matchesHardSearchFilters(candidate, options);
|
|
8119
|
+
function matchesSearchFilters(candidate, options, minScore, projectRoot) {
|
|
8120
|
+
return candidate.score >= minScore && matchesHardSearchFilters(candidate, options, projectRoot);
|
|
8053
8121
|
}
|
|
8054
8122
|
function unionCandidates(semanticCandidates, keywordCandidates) {
|
|
8055
8123
|
const byId = /* @__PURE__ */ new Map();
|
|
@@ -8100,7 +8168,7 @@ var Indexer = class _Indexer {
|
|
|
8100
8168
|
readerArtifactFingerprint = null;
|
|
8101
8169
|
writerArtifactFingerprint = null;
|
|
8102
8170
|
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
8103
|
-
constructor(projectRoot, config, host
|
|
8171
|
+
constructor(projectRoot, config, host, runtimeOptions = {}) {
|
|
8104
8172
|
this.projectRoot = projectRoot;
|
|
8105
8173
|
this.projectIdentityHash = hashContent(this.getCanonicalPath(projectRoot)).slice(0, 16);
|
|
8106
8174
|
this.materializedProjectRoot = runtimeOptions.materializedProjectRoot ?? projectRoot;
|
|
@@ -8113,37 +8181,90 @@ var Indexer = class _Indexer {
|
|
|
8113
8181
|
this.indexPathOverride = runtimeOptions.indexPath;
|
|
8114
8182
|
this.config = config;
|
|
8115
8183
|
this.host = host;
|
|
8184
|
+
if (isGitRepo(this.materializedProjectRoot)) {
|
|
8185
|
+
this.currentBranch = this.branchNameOverride ?? getBranchOrDefault(this.materializedProjectRoot);
|
|
8186
|
+
this.baseBranch = getBaseBranch(this.materializedProjectRoot);
|
|
8187
|
+
} else {
|
|
8188
|
+
this.currentBranch = "default";
|
|
8189
|
+
this.baseBranch = "default";
|
|
8190
|
+
}
|
|
8116
8191
|
this.indexPath = this.getIndexPath();
|
|
8117
|
-
this.
|
|
8118
|
-
this.failedBatchesPath = this.getRuntimeArtifactPath("failed-batches.json");
|
|
8192
|
+
this.refreshRuntimeArtifactPaths();
|
|
8119
8193
|
this.logger = initializeLogger(config.debug);
|
|
8120
8194
|
}
|
|
8121
8195
|
getIndexPath() {
|
|
8122
8196
|
return this.indexPathOverride ?? resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
8123
8197
|
}
|
|
8124
8198
|
toCanonicalFilePath(filePath) {
|
|
8199
|
+
if (!path14.isAbsolute(filePath)) {
|
|
8200
|
+
return this.resolveStoredFilePath(filePath, this.projectRoot);
|
|
8201
|
+
}
|
|
8125
8202
|
if (path14.resolve(this.materializedProjectRoot) === path14.resolve(this.projectRoot) || !isPathWithinRoot2(filePath, this.materializedProjectRoot)) {
|
|
8126
8203
|
return filePath;
|
|
8127
8204
|
}
|
|
8128
8205
|
return path14.resolve(this.projectRoot, path14.relative(this.materializedProjectRoot, filePath));
|
|
8129
8206
|
}
|
|
8130
|
-
|
|
8131
|
-
|
|
8207
|
+
toStoredFilePath(filePath) {
|
|
8208
|
+
const canonicalFilePath = this.toCanonicalFilePath(filePath);
|
|
8209
|
+
if (this.config.scope !== "project" || !isPathWithinRoot2(canonicalFilePath, this.projectRoot)) {
|
|
8210
|
+
return canonicalFilePath;
|
|
8211
|
+
}
|
|
8212
|
+
return path14.relative(this.projectRoot, canonicalFilePath).split(path14.sep).join("/");
|
|
8213
|
+
}
|
|
8214
|
+
resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
|
|
8215
|
+
if (path14.isAbsolute(filePath)) {
|
|
8132
8216
|
return filePath;
|
|
8133
8217
|
}
|
|
8134
|
-
|
|
8218
|
+
const resolvedPath = path14.resolve(rootPath, ...filePath.split("/"));
|
|
8219
|
+
if (!isPathWithinRoot2(resolvedPath, rootPath)) {
|
|
8220
|
+
throw new Error(`Stored project path escapes project root: ${JSON.stringify(filePath)}`);
|
|
8221
|
+
}
|
|
8222
|
+
return resolvedPath;
|
|
8223
|
+
}
|
|
8224
|
+
getCanonicalStoredFilePath(filePath) {
|
|
8225
|
+
return this.getCanonicalPath(this.resolveStoredFilePath(filePath));
|
|
8226
|
+
}
|
|
8227
|
+
resolveFilePathRecord(record) {
|
|
8228
|
+
return {
|
|
8229
|
+
...record,
|
|
8230
|
+
filePath: this.resolveStoredFilePath(record.filePath)
|
|
8231
|
+
};
|
|
8232
|
+
}
|
|
8233
|
+
resolveCallEdgeFilePath(edge) {
|
|
8234
|
+
if (!edge.fromSymbolFilePath) return edge;
|
|
8235
|
+
return {
|
|
8236
|
+
...edge,
|
|
8237
|
+
fromSymbolFilePath: this.resolveStoredFilePath(edge.fromSymbolFilePath)
|
|
8238
|
+
};
|
|
8239
|
+
}
|
|
8240
|
+
toMaterializedFilePath(filePath) {
|
|
8241
|
+
const storedFilePath = this.toStoredFilePath(filePath);
|
|
8242
|
+
if (path14.isAbsolute(storedFilePath)) {
|
|
8243
|
+
return storedFilePath;
|
|
8244
|
+
}
|
|
8245
|
+
return this.resolveStoredFilePath(storedFilePath, this.materializedProjectRoot);
|
|
8135
8246
|
}
|
|
8136
8247
|
getPreparedBranchNamespace() {
|
|
8137
8248
|
if (!this.branchNameOverride && !this.catalogIdentityOverride) return null;
|
|
8138
8249
|
return hashContent(this.getBranchCatalogKey()).slice(0, 16);
|
|
8139
8250
|
}
|
|
8251
|
+
getRuntimeArtifactNamespace() {
|
|
8252
|
+
if (this.config.scope !== "project" || this.getBranchCatalogIdentity() === "default") {
|
|
8253
|
+
return this.getPreparedBranchNamespace();
|
|
8254
|
+
}
|
|
8255
|
+
return hashContent(this.getBranchCatalogKey()).slice(0, 16);
|
|
8256
|
+
}
|
|
8140
8257
|
getRuntimeArtifactPath(fileName) {
|
|
8141
|
-
const namespace = this.
|
|
8258
|
+
const namespace = this.getRuntimeArtifactNamespace();
|
|
8142
8259
|
if (!namespace) return path14.join(this.indexPath, fileName);
|
|
8143
8260
|
const extension = path14.extname(fileName);
|
|
8144
8261
|
const baseName = fileName.slice(0, fileName.length - extension.length);
|
|
8145
8262
|
return path14.join(this.indexPath, `${baseName}.${namespace}${extension}`);
|
|
8146
8263
|
}
|
|
8264
|
+
refreshRuntimeArtifactPaths() {
|
|
8265
|
+
this.fileHashCachePath = this.getRuntimeArtifactPath("file-hashes.json");
|
|
8266
|
+
this.failedBatchesPath = this.getRuntimeArtifactPath("failed-batches.json");
|
|
8267
|
+
}
|
|
8147
8268
|
getPreparedChunkId(chunkId) {
|
|
8148
8269
|
const namespace = this.getPreparedBranchNamespace();
|
|
8149
8270
|
return namespace ? `${chunkId}_${namespace}` : chunkId;
|
|
@@ -8169,19 +8290,8 @@ var Indexer = class _Indexer {
|
|
|
8169
8290
|
return path14.resolve(targetPath);
|
|
8170
8291
|
}
|
|
8171
8292
|
}
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
if (this.host !== "opencode") {
|
|
8175
|
-
localProjectIndexPaths.push(path14.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
8176
|
-
}
|
|
8177
|
-
return localProjectIndexPaths.some((localPath) => {
|
|
8178
|
-
if (!(0, import_fs10.existsSync)(localPath) || !(0, import_fs10.existsSync)(this.indexPath)) {
|
|
8179
|
-
return path14.resolve(this.indexPath) === path14.resolve(localPath);
|
|
8180
|
-
}
|
|
8181
|
-
const indexStats = (0, import_fs10.statSync)(this.indexPath);
|
|
8182
|
-
const localStats = (0, import_fs10.statSync)(localPath);
|
|
8183
|
-
return indexStats.dev === localStats.dev && indexStats.ino === localStats.ino;
|
|
8184
|
-
});
|
|
8293
|
+
isProjectOwnedIndexPath() {
|
|
8294
|
+
return isProjectIndexPathOwnedByProject(this.projectRoot, this.indexPath, this.host);
|
|
8185
8295
|
}
|
|
8186
8296
|
resetLoadedIndexState(retireDatabase = false) {
|
|
8187
8297
|
if (this.database) {
|
|
@@ -8216,10 +8326,10 @@ var Indexer = class _Indexer {
|
|
|
8216
8326
|
this.readerArtifactRetryAfter.clear();
|
|
8217
8327
|
}
|
|
8218
8328
|
async withIndexMutationLease(operation, callback) {
|
|
8329
|
+
this.refreshBranchInfo();
|
|
8219
8330
|
const lease = acquireIndexLock(this.indexPath, operation);
|
|
8220
8331
|
this.indexPath = lease.canonicalIndexPath;
|
|
8221
|
-
this.
|
|
8222
|
-
this.failedBatchesPath = this.getRuntimeArtifactPath("failed-batches.json");
|
|
8332
|
+
this.refreshRuntimeArtifactPaths();
|
|
8223
8333
|
this.activeIndexLease = lease;
|
|
8224
8334
|
let result;
|
|
8225
8335
|
let callbackError;
|
|
@@ -8391,29 +8501,24 @@ var Indexer = class _Indexer {
|
|
|
8391
8501
|
getProjectForceReembedMetadataKey() {
|
|
8392
8502
|
return `index.forceReembed.${this.projectIdentityHash}`;
|
|
8393
8503
|
}
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
}
|
|
8398
|
-
return `index.callGraphResolutionVersion.${this.projectIdentityHash}`;
|
|
8504
|
+
getBranchMigrationMetadataKey(prefix, catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8505
|
+
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
8506
|
+
return `${prefix}.${hashContent(branchKey).slice(0, 24)}`;
|
|
8399
8507
|
}
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
if (this.config.scope !== "global") {
|
|
8403
|
-
return key;
|
|
8404
|
-
}
|
|
8405
|
-
return `${key}.${this.projectIdentityHash}`;
|
|
8508
|
+
getCallGraphResolutionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8509
|
+
return this.getBranchMigrationMetadataKey("index.callGraphResolutionVersion", catalogIdentity);
|
|
8406
8510
|
}
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
return `${key}.${this.projectIdentityHash}`;
|
|
8511
|
+
getSwiftParserVersionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8512
|
+
return this.getBranchMigrationMetadataKey("index.parser.swiftVersion", catalogIdentity);
|
|
8513
|
+
}
|
|
8514
|
+
getMetalParserVersionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8515
|
+
return this.getBranchMigrationMetadataKey("index.parser.metalVersion", catalogIdentity);
|
|
8413
8516
|
}
|
|
8414
8517
|
getSymbolExtractorVersionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8415
|
-
|
|
8416
|
-
|
|
8518
|
+
return this.getBranchMigrationMetadataKey("index.symbolExtractorVersion", catalogIdentity);
|
|
8519
|
+
}
|
|
8520
|
+
areBranchMigrationVersionsCurrent(database, catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8521
|
+
return database.getMetadata(this.getCallGraphResolutionMetadataKey(catalogIdentity)) === CALL_GRAPH_RESOLUTION_VERSION && database.getMetadata(this.getSwiftParserVersionMetadataKey(catalogIdentity)) === SWIFT_PARSER_VERSION && database.getMetadata(this.getMetalParserVersionMetadataKey(catalogIdentity)) === METAL_PARSER_VERSION && database.getMetadata(this.getSymbolExtractorVersionMetadataKey(catalogIdentity)) === SYMBOL_EXTRACTOR_VERSION;
|
|
8417
8522
|
}
|
|
8418
8523
|
hasProjectForceReembedPending() {
|
|
8419
8524
|
return this.config.scope === "global" && this.database?.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
@@ -8548,13 +8653,14 @@ var Indexer = class _Indexer {
|
|
|
8548
8653
|
return Array.from(keys);
|
|
8549
8654
|
}
|
|
8550
8655
|
isFileInCurrentScope(filePath, roots) {
|
|
8551
|
-
|
|
8552
|
-
const canonicalFilePath = this.getCanonicalPath(filePath);
|
|
8656
|
+
const canonicalFilePath = this.getCanonicalStoredFilePath(filePath);
|
|
8553
8657
|
return roots.some((root) => isPathWithinRoot2(canonicalFilePath, root));
|
|
8554
8658
|
}
|
|
8555
8659
|
isFileInProjectRoot(filePath) {
|
|
8556
|
-
|
|
8557
|
-
|
|
8660
|
+
return isPathWithinRoot2(
|
|
8661
|
+
this.getCanonicalStoredFilePath(filePath),
|
|
8662
|
+
this.getCanonicalPath(this.projectRoot)
|
|
8663
|
+
);
|
|
8558
8664
|
}
|
|
8559
8665
|
clearScopedFileHashCache(roots) {
|
|
8560
8666
|
for (const filePath of Array.from(this.fileHashCache.keys())) {
|
|
@@ -8955,7 +9061,10 @@ var Indexer = class _Indexer {
|
|
|
8955
9061
|
const intent = isLikelyImplementationPath2(candidate.metadata.filePath) ? "implementation" : "doc_or_test";
|
|
8956
9062
|
parts.push(`intent_hint: ${intent}`);
|
|
8957
9063
|
try {
|
|
8958
|
-
const fileContent = await import_fs10.promises.readFile(
|
|
9064
|
+
const fileContent = await import_fs10.promises.readFile(
|
|
9065
|
+
this.toMaterializedFilePath(candidate.metadata.filePath),
|
|
9066
|
+
"utf-8"
|
|
9067
|
+
);
|
|
8959
9068
|
const lines = fileContent.split("\n");
|
|
8960
9069
|
const snippetStartLine = Math.max(1, candidate.metadata.startLine);
|
|
8961
9070
|
const snippetEndLine = Math.min(lines.length, candidate.metadata.endLine);
|
|
@@ -9024,7 +9133,7 @@ var Indexer = class _Indexer {
|
|
|
9024
9133
|
if (this.config.scope === "global") {
|
|
9025
9134
|
return "Shared vector index could not be read. Restore or repair the complete fingerprinted shared vector artifacts; automatic reset is disabled for global scope.";
|
|
9026
9135
|
}
|
|
9027
|
-
if (!this.
|
|
9136
|
+
if (!this.isProjectOwnedIndexPath()) {
|
|
9028
9137
|
return "Vector index could not be read from an inherited project index. Restore or fingerprint it from the checkout that owns the index; do not remove or rebuild it from this worktree.";
|
|
9029
9138
|
}
|
|
9030
9139
|
return "Vector index could not be read. Run index_codebase after the active writer finishes to fingerprint a structurally valid legacy pair, or remove this checkout's local index directory and run index_codebase to rebuild it.";
|
|
@@ -9033,7 +9142,7 @@ var Indexer = class _Indexer {
|
|
|
9033
9142
|
if (this.config.scope === "global") {
|
|
9034
9143
|
return "Shared keyword index could not be read; semantic search remains available. Restore or repair the shared keyword artifact; automatic reset is disabled for global scope.";
|
|
9035
9144
|
}
|
|
9036
|
-
if (!this.
|
|
9145
|
+
if (!this.isProjectOwnedIndexPath()) {
|
|
9037
9146
|
return "Keyword index could not be read from an inherited project index; semantic search remains available. Restore or repair it from the checkout that owns the index; do not rebuild it from this worktree.";
|
|
9038
9147
|
}
|
|
9039
9148
|
return "Keyword index could not be read; semantic search remains available. Restore a readable published keyword index, or run index_codebase with force=true after the active writer finishes.";
|
|
@@ -9042,10 +9151,10 @@ var Indexer = class _Indexer {
|
|
|
9042
9151
|
if (this.config.scope === "global") {
|
|
9043
9152
|
return "Shared index database could not be read. Restore or repair the shared SQLite database; automatic reset is disabled for global scope.";
|
|
9044
9153
|
}
|
|
9045
|
-
if (!this.
|
|
9154
|
+
if (!this.isProjectOwnedIndexPath()) {
|
|
9046
9155
|
return "Index database could not be read from an inherited project index. Restore or repair it from the checkout that owns the index; do not migrate or rebuild it from this worktree.";
|
|
9047
9156
|
}
|
|
9048
|
-
return "Index database could not be read. Run index_codebase
|
|
9157
|
+
return "Index database could not be read. Run index_codebase with force=true to rebuild a legacy absolute-path schema, or repair the database after the active writer finishes.";
|
|
9049
9158
|
}
|
|
9050
9159
|
getReaderFileFingerprint(filePath, identityOnly = false) {
|
|
9051
9160
|
try {
|
|
@@ -9225,7 +9334,7 @@ var Indexer = class _Indexer {
|
|
|
9225
9334
|
const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
|
|
9226
9335
|
if (mode === "writer") {
|
|
9227
9336
|
await import_fs10.promises.mkdir(this.indexPath, { recursive: true });
|
|
9228
|
-
if (recoveredOwners.length > 0 && this.config.scope === "project" && !this.
|
|
9337
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project" && !this.isProjectOwnedIndexPath()) {
|
|
9229
9338
|
throw new Error(
|
|
9230
9339
|
"Interrupted indexing recovery is unsafe while using an inherited worktree index. Run index_codebase with force=true to create a local project index boundary."
|
|
9231
9340
|
);
|
|
@@ -9326,6 +9435,7 @@ var Indexer = class _Indexer {
|
|
|
9326
9435
|
this.baseBranch = "default";
|
|
9327
9436
|
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
9328
9437
|
}
|
|
9438
|
+
this.refreshRuntimeArtifactPaths();
|
|
9329
9439
|
if (mode === "writer" && recoveredOwners.length > 0) {
|
|
9330
9440
|
await this.recoverFromInterruptedIndexingUnlocked(recoveredOwners);
|
|
9331
9441
|
}
|
|
@@ -9481,6 +9591,14 @@ var Indexer = class _Indexer {
|
|
|
9481
9591
|
}
|
|
9482
9592
|
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
9483
9593
|
}
|
|
9594
|
+
async removeProjectRuntimeStateArtifacts() {
|
|
9595
|
+
if (!(0, import_fs10.existsSync)(this.indexPath)) return;
|
|
9596
|
+
const names = await import_fs10.promises.readdir(this.indexPath);
|
|
9597
|
+
const runtimeStatePattern = /^(?:file-hashes|failed-batches)(?:\.[a-f0-9]{16})?\.json$/;
|
|
9598
|
+
await Promise.all(
|
|
9599
|
+
names.filter((name) => runtimeStatePattern.test(name)).map((name) => import_fs10.promises.rm(path14.join(this.indexPath, name), { force: true }))
|
|
9600
|
+
);
|
|
9601
|
+
}
|
|
9484
9602
|
async resetLocalIndexArtifacts() {
|
|
9485
9603
|
this.store = null;
|
|
9486
9604
|
this.invertedIndex = null;
|
|
@@ -9500,11 +9618,10 @@ var Indexer = class _Indexer {
|
|
|
9500
9618
|
path14.join(this.indexPath, "vectors"),
|
|
9501
9619
|
path14.join(this.indexPath, "vectors.usearch"),
|
|
9502
9620
|
path14.join(this.indexPath, "vectors.meta.json"),
|
|
9503
|
-
path14.join(this.indexPath, "inverted-index.json")
|
|
9504
|
-
path14.join(this.indexPath, "file-hashes.json"),
|
|
9505
|
-
path14.join(this.indexPath, "failed-batches.json")
|
|
9621
|
+
path14.join(this.indexPath, "inverted-index.json")
|
|
9506
9622
|
];
|
|
9507
9623
|
await Promise.all(resetPaths.map((targetPath) => import_fs10.promises.rm(targetPath, { recursive: true, force: true })));
|
|
9624
|
+
await this.removeProjectRuntimeStateArtifacts();
|
|
9508
9625
|
await import_fs10.promises.mkdir(this.indexPath, { recursive: true });
|
|
9509
9626
|
}
|
|
9510
9627
|
async tryResetCorruptedIndex(stage, error) {
|
|
@@ -9554,12 +9671,20 @@ var Indexer = class _Indexer {
|
|
|
9554
9671
|
}
|
|
9555
9672
|
this.database.addChunksToBranchBatch(this.getBranchCatalogKey(), chunkIds);
|
|
9556
9673
|
}
|
|
9674
|
+
getExpectedPathStorageVersion() {
|
|
9675
|
+
return this.config.scope === "project" ? PROJECT_PATH_STORAGE_VERSION : GLOBAL_PATH_STORAGE_VERSION;
|
|
9676
|
+
}
|
|
9677
|
+
hasStoredIndexData() {
|
|
9678
|
+
const stats = this.database?.getStats();
|
|
9679
|
+
return (this.store?.count() ?? 0) > 0 || (stats?.chunkCount ?? 0) > 0 || (stats?.symbolCount ?? 0) > 0 || this.fileHashCache.size > 0;
|
|
9680
|
+
}
|
|
9557
9681
|
loadIndexMetadata() {
|
|
9558
9682
|
if (!this.database) return null;
|
|
9559
9683
|
const version = this.database.getMetadata("index.version");
|
|
9560
9684
|
if (!version) return null;
|
|
9561
9685
|
return {
|
|
9562
9686
|
indexVersion: version,
|
|
9687
|
+
pathStorageVersion: this.database.getMetadata("index.pathStorageVersion") ?? GLOBAL_PATH_STORAGE_VERSION,
|
|
9563
9688
|
embeddingProvider: this.database.getMetadata("index.embeddingProvider") ?? "",
|
|
9564
9689
|
embeddingModel: this.database.getMetadata("index.embeddingModel") ?? "",
|
|
9565
9690
|
embeddingDimensions: parseInt(this.database.getMetadata("index.embeddingDimensions") ?? "0", 10),
|
|
@@ -9574,6 +9699,7 @@ var Indexer = class _Indexer {
|
|
|
9574
9699
|
const existingCreatedAt = this.database.getMetadata("index.createdAt");
|
|
9575
9700
|
const completeProjectEmbeddingStrategyReset = !this.hasProjectForceReembedPending();
|
|
9576
9701
|
this.database.setMetadata("index.version", INDEX_METADATA_VERSION);
|
|
9702
|
+
this.database.setMetadata("index.pathStorageVersion", this.getExpectedPathStorageVersion());
|
|
9577
9703
|
this.database.setMetadata("index.embeddingProvider", provider.provider);
|
|
9578
9704
|
this.database.setMetadata("index.embeddingModel", provider.modelInfo.model);
|
|
9579
9705
|
this.database.setMetadata("index.embeddingDimensions", provider.modelInfo.dimensions.toString());
|
|
@@ -9596,6 +9722,16 @@ var Indexer = class _Indexer {
|
|
|
9596
9722
|
}
|
|
9597
9723
|
validateIndexCompatibility(provider) {
|
|
9598
9724
|
const storedMetadata = this.loadIndexMetadata();
|
|
9725
|
+
const storedPathStorageVersion = this.database?.getMetadata("index.pathStorageVersion") ?? GLOBAL_PATH_STORAGE_VERSION;
|
|
9726
|
+
const expectedPathStorageVersion = this.getExpectedPathStorageVersion();
|
|
9727
|
+
if (this.hasStoredIndexData() && storedPathStorageVersion !== expectedPathStorageVersion) {
|
|
9728
|
+
return {
|
|
9729
|
+
compatible: false,
|
|
9730
|
+
code: "PATH_STORAGE_MISMATCH" /* PATH_STORAGE_MISMATCH */,
|
|
9731
|
+
reason: `Path storage format mismatch: index uses v${storedPathStorageVersion} checkout-absolute paths, but this project requires portable v${expectedPathStorageVersion} paths. Run index_codebase with force=true to rebuild the shared project index once.`,
|
|
9732
|
+
storedMetadata: storedMetadata ?? void 0
|
|
9733
|
+
};
|
|
9734
|
+
}
|
|
9599
9735
|
if (!storedMetadata) {
|
|
9600
9736
|
return { compatible: true };
|
|
9601
9737
|
}
|
|
@@ -9647,6 +9783,7 @@ var Indexer = class _Indexer {
|
|
|
9647
9783
|
return this.indexCompatibility;
|
|
9648
9784
|
}
|
|
9649
9785
|
async ensureInitialized() {
|
|
9786
|
+
this.refreshBranchInfo();
|
|
9650
9787
|
let initializedReader = false;
|
|
9651
9788
|
while (true) {
|
|
9652
9789
|
if (this.initializationPromise) {
|
|
@@ -9754,8 +9891,8 @@ var Indexer = class _Indexer {
|
|
|
9754
9891
|
}
|
|
9755
9892
|
const branchKey = this.getBranchCatalogKey();
|
|
9756
9893
|
const alreadyIndexed = database.getBranchChunkIds(branchKey).length > 0 && database.getBranchSymbolIds(branchKey).length > 0;
|
|
9757
|
-
const
|
|
9758
|
-
if (alreadyIndexed &&
|
|
9894
|
+
const migrationsCurrent = this.areBranchMigrationVersionsCurrent(database);
|
|
9895
|
+
if (alreadyIndexed && migrationsCurrent && this.getStoredBranchCommit(database) === normalizedCommit) {
|
|
9759
9896
|
return { prepared: false };
|
|
9760
9897
|
}
|
|
9761
9898
|
const stats = await this.indexUnlocked(onProgress, [], true);
|
|
@@ -9849,21 +9986,21 @@ var Indexer = class _Indexer {
|
|
|
9849
9986
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
9850
9987
|
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
9851
9988
|
for (const f of files) {
|
|
9852
|
-
const
|
|
9989
|
+
const storedPath = this.toStoredFilePath(f.path);
|
|
9853
9990
|
const currentHash = hashFile(f.path);
|
|
9854
|
-
currentFileHashes.set(
|
|
9855
|
-
const cachedHashMatches = this.fileHashCache.get(
|
|
9856
|
-
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(
|
|
9991
|
+
currentFileHashes.set(storedPath, currentHash);
|
|
9992
|
+
const cachedHashMatches = this.fileHashCache.get(storedPath) === currentHash;
|
|
9993
|
+
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
|
|
9857
9994
|
(chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
|
|
9858
9995
|
);
|
|
9859
|
-
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path14.extname(
|
|
9860
|
-
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path14.extname(
|
|
9996
|
+
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path14.extname(storedPath).toLowerCase() === ".swift";
|
|
9997
|
+
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path14.extname(storedPath).toLowerCase() === ".metal";
|
|
9861
9998
|
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
9862
|
-
unchangedFilePaths.add(
|
|
9999
|
+
unchangedFilePaths.add(storedPath);
|
|
9863
10000
|
this.logger.recordCacheHit();
|
|
9864
10001
|
} else {
|
|
9865
10002
|
const content = await import_fs10.promises.readFile(f.path, "utf-8");
|
|
9866
|
-
changedFiles.push({ path:
|
|
10003
|
+
changedFiles.push({ path: storedPath, content, hash: currentHash });
|
|
9867
10004
|
this.logger.recordCacheMiss();
|
|
9868
10005
|
}
|
|
9869
10006
|
}
|
|
@@ -9968,8 +10105,7 @@ var Indexer = class _Indexer {
|
|
|
9968
10105
|
for (const parsed of parsedFiles) {
|
|
9969
10106
|
currentFilePaths.add(parsed.path);
|
|
9970
10107
|
if (parsed.chunks.length === 0) {
|
|
9971
|
-
|
|
9972
|
-
stats.parseFailures.push(relativePath);
|
|
10108
|
+
stats.parseFailures.push(path14.isAbsolute(parsed.path) ? path14.relative(this.projectRoot, parsed.path) : parsed.path);
|
|
9973
10109
|
}
|
|
9974
10110
|
let chunksToProcess = parsed.chunks;
|
|
9975
10111
|
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
@@ -10610,6 +10746,43 @@ var Indexer = class _Indexer {
|
|
|
10610
10746
|
const union = a.size + b.size - intersection;
|
|
10611
10747
|
return intersection / union;
|
|
10612
10748
|
}
|
|
10749
|
+
getBranchPrefilterState(database, branchChunkIds) {
|
|
10750
|
+
const hasInitializedBranchCatalog = branchChunkIds !== null && database.getAllBranches().length > 0;
|
|
10751
|
+
return {
|
|
10752
|
+
hasInitializedBranchCatalog,
|
|
10753
|
+
shouldPrefilterByBranch: branchChunkIds !== null && (this.config.scope === "global" || hasInitializedBranchCatalog)
|
|
10754
|
+
};
|
|
10755
|
+
}
|
|
10756
|
+
searchCandidatesWithBranchPrefilter(initialLimit, totalCount, branchChunkIds, shouldPrefilterByBranch, search, getChunkId) {
|
|
10757
|
+
const normalizedLimit = Math.max(0, Math.floor(initialLimit));
|
|
10758
|
+
if (normalizedLimit === 0) return [];
|
|
10759
|
+
if (!shouldPrefilterByBranch || !branchChunkIds) {
|
|
10760
|
+
return search(normalizedLimit);
|
|
10761
|
+
}
|
|
10762
|
+
const targetCount = Math.min(normalizedLimit, branchChunkIds.size);
|
|
10763
|
+
if (targetCount === 0 || totalCount === 0) return [];
|
|
10764
|
+
let requestedLimit = Math.min(normalizedLimit, totalCount);
|
|
10765
|
+
while (true) {
|
|
10766
|
+
const results = search(requestedLimit);
|
|
10767
|
+
const branchResults = results.filter((candidate) => branchChunkIds.has(getChunkId(candidate)));
|
|
10768
|
+
if (branchResults.length >= targetCount || results.length < requestedLimit || requestedLimit >= totalCount) {
|
|
10769
|
+
return branchResults;
|
|
10770
|
+
}
|
|
10771
|
+
const nextLimit = Math.min(totalCount, Math.max(requestedLimit + 1, requestedLimit * 2));
|
|
10772
|
+
if (nextLimit === requestedLimit) return branchResults;
|
|
10773
|
+
requestedLimit = nextLimit;
|
|
10774
|
+
}
|
|
10775
|
+
}
|
|
10776
|
+
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch) {
|
|
10777
|
+
return this.searchCandidatesWithBranchPrefilter(
|
|
10778
|
+
initialLimit,
|
|
10779
|
+
store.count(),
|
|
10780
|
+
branchChunkIds,
|
|
10781
|
+
shouldPrefilterByBranch,
|
|
10782
|
+
(requestedLimit) => store.search(embedding, requestedLimit),
|
|
10783
|
+
(candidate) => candidate.id
|
|
10784
|
+
);
|
|
10785
|
+
}
|
|
10613
10786
|
async search(query, limit, options) {
|
|
10614
10787
|
const { store, provider, invertedIndex, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
10615
10788
|
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
@@ -10654,12 +10827,7 @@ var Indexer = class _Indexer {
|
|
|
10654
10827
|
});
|
|
10655
10828
|
}
|
|
10656
10829
|
const embeddingMs = import_perf_hooks.performance.now() - embeddingStartTime;
|
|
10657
|
-
const
|
|
10658
|
-
const semanticResults = embedding ? store.search(embedding, maxResults * 4) : [];
|
|
10659
|
-
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
10660
|
-
const keywordStartTime = import_perf_hooks.performance.now();
|
|
10661
|
-
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
10662
|
-
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
10830
|
+
const prefilterStartTime = import_perf_hooks.performance.now();
|
|
10663
10831
|
let branchChunkIds = null;
|
|
10664
10832
|
let branchSymbolIds = null;
|
|
10665
10833
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
@@ -10667,35 +10835,38 @@ var Indexer = class _Indexer {
|
|
|
10667
10835
|
branchChunkIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchChunkIds(branchKey)));
|
|
10668
10836
|
branchSymbolIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchSymbolIds(branchKey)));
|
|
10669
10837
|
}
|
|
10670
|
-
const
|
|
10671
|
-
const
|
|
10672
|
-
const
|
|
10673
|
-
const
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
10838
|
+
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
10839
|
+
const prefilterMs = import_perf_hooks.performance.now() - prefilterStartTime;
|
|
10840
|
+
const vectorStartTime = import_perf_hooks.performance.now();
|
|
10841
|
+
const semanticCandidates = embedding ? this.searchSemanticCandidates(
|
|
10842
|
+
store,
|
|
10843
|
+
embedding,
|
|
10844
|
+
maxResults * 4,
|
|
10845
|
+
branchChunkIds,
|
|
10846
|
+
shouldPrefilterByBranch
|
|
10847
|
+
) : [];
|
|
10848
|
+
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
10849
|
+
const keywordStartTime = import_perf_hooks.performance.now();
|
|
10850
|
+
const keywordCandidates = await this.keywordSearch(
|
|
10851
|
+
query,
|
|
10852
|
+
maxResults * 4,
|
|
10853
|
+
store,
|
|
10854
|
+
invertedIndex,
|
|
10855
|
+
branchChunkIds,
|
|
10856
|
+
shouldPrefilterByBranch
|
|
10857
|
+
);
|
|
10858
|
+
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
10677
10859
|
const scopedSemanticCandidates = semanticCandidates.filter(
|
|
10678
|
-
(candidate) => matchesHardSearchFilters(candidate, options)
|
|
10860
|
+
(candidate) => matchesHardSearchFilters(candidate, options, this.projectRoot)
|
|
10679
10861
|
);
|
|
10680
10862
|
const scopedKeywordCandidates = keywordCandidates.filter(
|
|
10681
|
-
(candidate) => matchesHardSearchFilters(candidate, options)
|
|
10863
|
+
(candidate) => matchesHardSearchFilters(candidate, options, this.projectRoot)
|
|
10682
10864
|
);
|
|
10683
|
-
|
|
10684
|
-
if (this.config.scope !== "global" && branchChunkIds && branchChunkIds.size === 0) {
|
|
10865
|
+
if (this.config.scope !== "global" && branchChunkIds && !hasInitializedBranchCatalog) {
|
|
10685
10866
|
this.logger.search("warn", "Branch prefilter skipped because branch catalog is empty", {
|
|
10686
10867
|
branch: this.currentBranch
|
|
10687
10868
|
});
|
|
10688
10869
|
}
|
|
10689
|
-
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
10690
|
-
this.logger.search("warn", "Branch prefilter produced no semantic overlap, using unfiltered semantic candidates", {
|
|
10691
|
-
branch: this.currentBranch
|
|
10692
|
-
});
|
|
10693
|
-
}
|
|
10694
|
-
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && keywordResults.length > 0 && prefilteredKeyword.length === 0) {
|
|
10695
|
-
this.logger.search("warn", "Branch prefilter produced no keyword overlap, using unfiltered keyword candidates", {
|
|
10696
|
-
branch: this.currentBranch
|
|
10697
|
-
});
|
|
10698
|
-
}
|
|
10699
10870
|
const fusionStartTime = import_perf_hooks.performance.now();
|
|
10700
10871
|
const rankingHybridWeight = embedding === void 0 && fusionStrategy === "weighted" ? 1 : effectiveHybridWeight;
|
|
10701
10872
|
const combined = rankHybridResults(query, scopedSemanticCandidates, scopedKeywordCandidates, {
|
|
@@ -10746,12 +10917,14 @@ var Indexer = class _Indexer {
|
|
|
10746
10917
|
const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
10747
10918
|
const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
|
|
10748
10919
|
const hasCodeHints = extractCodeTermHints(query).length > 0 || identifierHints.length > 0;
|
|
10749
|
-
const baseFiltered = tiered.filter(
|
|
10920
|
+
const baseFiltered = tiered.filter(
|
|
10921
|
+
(r) => matchesSearchFilters(r, options, this.config.search.minScore, this.projectRoot)
|
|
10922
|
+
);
|
|
10750
10923
|
const implementationOnly = baseFiltered.filter(
|
|
10751
10924
|
(r) => isLikelyImplementationPath2(r.metadata.filePath) && isImplementationChunkType(r.metadata.chunkType)
|
|
10752
10925
|
);
|
|
10753
10926
|
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly : baseFiltered).slice(0, maxResults);
|
|
10754
|
-
const identifierFallback = !options?.definitionIntent && filtered.length === 0 && identifierHints.length > 0 ? buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, maxResults, union, true).filter((r) => matchesSearchFilters(r, options, this.config.search.minScore)).slice(0, maxResults) : [];
|
|
10927
|
+
const identifierFallback = !options?.definitionIntent && filtered.length === 0 && identifierHints.length > 0 ? buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, maxResults, union, true).filter((r) => matchesSearchFilters(r, options, this.config.search.minScore, this.projectRoot)).slice(0, maxResults) : [];
|
|
10755
10928
|
const finalResults = filtered.length > 0 ? filtered : identifierFallback;
|
|
10756
10929
|
const totalSearchMs = import_perf_hooks.performance.now() - searchStartTime;
|
|
10757
10930
|
this.logger.recordSearch(totalSearchMs, {
|
|
@@ -10776,10 +10949,11 @@ var Indexer = class _Indexer {
|
|
|
10776
10949
|
let content = "";
|
|
10777
10950
|
let contextStartLine = r.metadata.startLine;
|
|
10778
10951
|
let contextEndLine = r.metadata.endLine;
|
|
10952
|
+
const resolvedFilePath = this.resolveStoredFilePath(r.metadata.filePath);
|
|
10779
10953
|
if (!metadataOnly && this.config.search.includeContext) {
|
|
10780
10954
|
try {
|
|
10781
10955
|
const fileContent = await import_fs10.promises.readFile(
|
|
10782
|
-
|
|
10956
|
+
resolvedFilePath,
|
|
10783
10957
|
"utf-8"
|
|
10784
10958
|
);
|
|
10785
10959
|
const lines = fileContent.split("\n");
|
|
@@ -10792,7 +10966,7 @@ var Indexer = class _Indexer {
|
|
|
10792
10966
|
}
|
|
10793
10967
|
}
|
|
10794
10968
|
return {
|
|
10795
|
-
filePath:
|
|
10969
|
+
filePath: resolvedFilePath,
|
|
10796
10970
|
startLine: contextStartLine,
|
|
10797
10971
|
endLine: contextEndLine,
|
|
10798
10972
|
content,
|
|
@@ -10804,8 +10978,18 @@ var Indexer = class _Indexer {
|
|
|
10804
10978
|
})
|
|
10805
10979
|
);
|
|
10806
10980
|
}
|
|
10807
|
-
async keywordSearch(query, limit, store, invertedIndex) {
|
|
10808
|
-
const
|
|
10981
|
+
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false) {
|
|
10982
|
+
const normalizedLimit = Math.max(0, Math.floor(limit));
|
|
10983
|
+
if (normalizedLimit === 0) return [];
|
|
10984
|
+
const scoreEntries = this.searchCandidatesWithBranchPrefilter(
|
|
10985
|
+
normalizedLimit,
|
|
10986
|
+
invertedIndex.getDocumentCount(),
|
|
10987
|
+
branchChunkIds,
|
|
10988
|
+
shouldPrefilterByBranch,
|
|
10989
|
+
(requestedLimit) => Array.from(invertedIndex.search(query, requestedLimit)),
|
|
10990
|
+
([chunkId]) => chunkId
|
|
10991
|
+
);
|
|
10992
|
+
const scores = new Map(scoreEntries);
|
|
10809
10993
|
if (scores.size === 0) {
|
|
10810
10994
|
return [];
|
|
10811
10995
|
}
|
|
@@ -10819,7 +11003,7 @@ var Indexer = class _Indexer {
|
|
|
10819
11003
|
}
|
|
10820
11004
|
}
|
|
10821
11005
|
results.sort((a, b) => b.score - a.score);
|
|
10822
|
-
return results.slice(0,
|
|
11006
|
+
return results.slice(0, normalizedLimit);
|
|
10823
11007
|
}
|
|
10824
11008
|
async getStatus() {
|
|
10825
11009
|
const { store, configuredProviderInfo, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
@@ -10886,7 +11070,7 @@ var Indexer = class _Indexer {
|
|
|
10886
11070
|
);
|
|
10887
11071
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
10888
11072
|
for (const file of files) {
|
|
10889
|
-
currentFileHashes.set(this.
|
|
11073
|
+
currentFileHashes.set(this.toStoredFilePath(file.path), hashFile(file.path));
|
|
10890
11074
|
}
|
|
10891
11075
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
10892
11076
|
const cachedFileHashes = scopedRoots ? new Map(Array.from(this.fileHashCache).filter(([filePath]) => this.isFileInCurrentScope(filePath, scopedRoots))) : this.fileHashCache;
|
|
@@ -10898,17 +11082,7 @@ var Indexer = class _Indexer {
|
|
|
10898
11082
|
return { readable: true, current: false, reason: "files-changed" };
|
|
10899
11083
|
}
|
|
10900
11084
|
}
|
|
10901
|
-
|
|
10902
|
-
(filePath) => path14.extname(filePath).toLowerCase() === ".swift"
|
|
10903
|
-
);
|
|
10904
|
-
const hasMetalFiles = Array.from(currentFileHashes.keys()).some(
|
|
10905
|
-
(filePath) => path14.extname(filePath).toLowerCase() === ".metal"
|
|
10906
|
-
);
|
|
10907
|
-
const hasCallGraphMigrationFiles = Array.from(currentFileHashes.keys()).some((filePath) => {
|
|
10908
|
-
const extension = path14.extname(filePath).toLowerCase();
|
|
10909
|
-
return extension === ".php" || extension === ".c" || extension === ".cc" || extension === ".cpp" || extension === ".cxx";
|
|
10910
|
-
});
|
|
10911
|
-
if (hasSwiftFiles && database.getMetadata(this.getSwiftParserVersionMetadataKey()) !== SWIFT_PARSER_VERSION || hasMetalFiles && database.getMetadata(this.getMetalParserVersionMetadataKey()) !== METAL_PARSER_VERSION || database.getMetadata(this.getSymbolExtractorVersionMetadataKey()) !== SYMBOL_EXTRACTOR_VERSION || hasCallGraphMigrationFiles && database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION) {
|
|
11085
|
+
if (!this.areBranchMigrationVersionsCurrent(database)) {
|
|
10912
11086
|
return { readable: true, current: false, reason: "migration-required" };
|
|
10913
11087
|
}
|
|
10914
11088
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -10968,6 +11142,7 @@ var Indexer = class _Indexer {
|
|
|
10968
11142
|
this.deleteBranchCommitMetadata(database, clearedBranchKeys2);
|
|
10969
11143
|
this.saveFailedBatches([]);
|
|
10970
11144
|
database.deleteMetadata("index.version");
|
|
11145
|
+
database.deleteMetadata("index.pathStorageVersion");
|
|
10971
11146
|
database.deleteMetadata("index.embeddingProvider");
|
|
10972
11147
|
database.deleteMetadata("index.embeddingModel");
|
|
10973
11148
|
database.deleteMetadata("index.embeddingDimensions");
|
|
@@ -10986,7 +11161,7 @@ var Indexer = class _Indexer {
|
|
|
10986
11161
|
this.indexCompatibility = compatibility;
|
|
10987
11162
|
return;
|
|
10988
11163
|
}
|
|
10989
|
-
if (!this.
|
|
11164
|
+
if (!this.isProjectOwnedIndexPath()) {
|
|
10990
11165
|
throw new Error(
|
|
10991
11166
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
10992
11167
|
);
|
|
@@ -10997,11 +11172,11 @@ var Indexer = class _Indexer {
|
|
|
10997
11172
|
invertedIndex.clear();
|
|
10998
11173
|
this.saveInvertedIndex(invertedIndex);
|
|
10999
11174
|
this.fileHashCache.clear();
|
|
11000
|
-
this.
|
|
11175
|
+
await this.removeProjectRuntimeStateArtifacts();
|
|
11001
11176
|
database.clearAllIndexedData();
|
|
11002
11177
|
this.deleteBranchCommitMetadata(database, clearedBranchKeys);
|
|
11003
|
-
this.saveFailedBatches([]);
|
|
11004
11178
|
database.deleteMetadata("index.version");
|
|
11179
|
+
database.deleteMetadata("index.pathStorageVersion");
|
|
11005
11180
|
database.deleteMetadata("index.embeddingProvider");
|
|
11006
11181
|
database.deleteMetadata("index.embeddingModel");
|
|
11007
11182
|
database.deleteMetadata("index.embeddingDimensions");
|
|
@@ -11029,32 +11204,46 @@ var Indexer = class _Indexer {
|
|
|
11029
11204
|
existing.push(key);
|
|
11030
11205
|
filePathsToChunkKeys.set(metadata.filePath, existing);
|
|
11031
11206
|
}
|
|
11032
|
-
const
|
|
11033
|
-
const
|
|
11207
|
+
const missingStoredFilePaths = [];
|
|
11208
|
+
const missingChunkKeys = [];
|
|
11034
11209
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
11035
11210
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
11036
|
-
if (!(0, import_fs10.existsSync)(filePath)) {
|
|
11211
|
+
if (!(0, import_fs10.existsSync)(this.toMaterializedFilePath(filePath))) {
|
|
11037
11212
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
11038
11213
|
for (const key of chunkKeys) {
|
|
11039
|
-
|
|
11214
|
+
missingChunkKeys.push(key);
|
|
11040
11215
|
}
|
|
11041
|
-
|
|
11216
|
+
missingStoredFilePaths.push(filePath);
|
|
11042
11217
|
}
|
|
11043
11218
|
}
|
|
11219
|
+
const branchCatalogKeys = this.getBranchCatalogKeys();
|
|
11220
|
+
for (const branchKey of branchCatalogKeys) {
|
|
11221
|
+
database.deleteBranchChunksForBranch(branchKey, missingChunkKeys);
|
|
11222
|
+
}
|
|
11223
|
+
const referencedChunkKeys = new Set(database.getReferencedChunkIds(missingChunkKeys));
|
|
11224
|
+
const removedChunkKeys = missingChunkKeys.filter((key) => !referencedChunkKeys.has(key));
|
|
11044
11225
|
if (removedChunkKeys.length > 0) {
|
|
11045
11226
|
this.rebuildVectorStoreExcludingChunkIds(store, database, removedChunkKeys);
|
|
11046
11227
|
for (const key of removedChunkKeys) {
|
|
11047
11228
|
invertedIndex.removeChunk(key);
|
|
11048
11229
|
}
|
|
11230
|
+
database.deleteChunksByIds(removedChunkKeys);
|
|
11049
11231
|
}
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
database.
|
|
11057
|
-
}
|
|
11232
|
+
const missingSymbolIds = Array.from(new Set(
|
|
11233
|
+
missingStoredFilePaths.flatMap(
|
|
11234
|
+
(filePath) => database.getSymbolsByFile(filePath).map((symbol) => symbol.id)
|
|
11235
|
+
)
|
|
11236
|
+
));
|
|
11237
|
+
for (const branchKey of branchCatalogKeys) {
|
|
11238
|
+
database.deleteBranchSymbolsForBranch(branchKey, missingSymbolIds);
|
|
11239
|
+
}
|
|
11240
|
+
const referencedSymbolIds = new Set(database.getReferencedSymbolIds(missingSymbolIds));
|
|
11241
|
+
const removedSymbolIds = missingSymbolIds.filter((symbolId) => !referencedSymbolIds.has(symbolId));
|
|
11242
|
+
database.clearCallEdgeTargetsForSymbols(removedSymbolIds);
|
|
11243
|
+
const removedChunkKeySet = new Set(removedChunkKeys);
|
|
11244
|
+
const removedStoredFilePaths = missingStoredFilePaths.filter(
|
|
11245
|
+
(filePath) => (chunkKeysByRemovedFile.get(filePath) ?? []).some((key) => removedChunkKeySet.has(key))
|
|
11246
|
+
);
|
|
11058
11247
|
const removedCount = removedChunkKeys.length;
|
|
11059
11248
|
if (removedCount > 0) {
|
|
11060
11249
|
store.save();
|
|
@@ -11090,9 +11279,16 @@ var Indexer = class _Indexer {
|
|
|
11090
11279
|
removedStale: removedCount,
|
|
11091
11280
|
orphanEmbeddings: gcOrphanEmbeddings,
|
|
11092
11281
|
orphanChunks: gcOrphanChunks,
|
|
11093
|
-
removedFiles:
|
|
11282
|
+
removedFiles: removedStoredFilePaths.length
|
|
11094
11283
|
});
|
|
11095
|
-
return {
|
|
11284
|
+
return {
|
|
11285
|
+
removed: removedCount,
|
|
11286
|
+
filePaths: removedStoredFilePaths.map((filePath) => this.resolveStoredFilePath(filePath)),
|
|
11287
|
+
gcOrphanEmbeddings,
|
|
11288
|
+
gcOrphanChunks,
|
|
11289
|
+
gcOrphanSymbols,
|
|
11290
|
+
gcOrphanCallEdges
|
|
11291
|
+
};
|
|
11096
11292
|
}
|
|
11097
11293
|
async retryFailedBatches() {
|
|
11098
11294
|
return this.withIndexMutationLease("retry-failed-batches", async (recoveredOwners) => {
|
|
@@ -11287,9 +11483,18 @@ var Indexer = class _Indexer {
|
|
|
11287
11483
|
return this.baseBranch;
|
|
11288
11484
|
}
|
|
11289
11485
|
refreshBranchInfo() {
|
|
11486
|
+
const previousBranch = this.currentBranch;
|
|
11290
11487
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
11291
11488
|
this.currentBranch = this.branchNameOverride ?? getBranchOrDefault(this.materializedProjectRoot);
|
|
11292
11489
|
this.baseBranch = getBaseBranch(this.materializedProjectRoot);
|
|
11490
|
+
} else {
|
|
11491
|
+
this.currentBranch = "default";
|
|
11492
|
+
this.baseBranch = "default";
|
|
11493
|
+
}
|
|
11494
|
+
if (this.currentBranch !== previousBranch) {
|
|
11495
|
+
this.refreshRuntimeArtifactPaths();
|
|
11496
|
+
this.fileHashCache.clear();
|
|
11497
|
+
this.loadFileHashCache();
|
|
11293
11498
|
}
|
|
11294
11499
|
}
|
|
11295
11500
|
async getDatabaseStats() {
|
|
@@ -11314,6 +11519,7 @@ var Indexer = class _Indexer {
|
|
|
11314
11519
|
return [];
|
|
11315
11520
|
}
|
|
11316
11521
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
11522
|
+
const excludedStoredFile = options?.excludeFile ? this.toStoredFilePath(options.excludeFile) : void 0;
|
|
11317
11523
|
this.logger.search("debug", "Starting find similar", {
|
|
11318
11524
|
codeLength: code.length,
|
|
11319
11525
|
limit,
|
|
@@ -11323,31 +11529,29 @@ var Indexer = class _Indexer {
|
|
|
11323
11529
|
const { embedding, tokensUsed } = await provider.embedDocument(code);
|
|
11324
11530
|
const embeddingMs = import_perf_hooks.performance.now() - embeddingStartTime;
|
|
11325
11531
|
this.logger.recordEmbeddingApiCall(tokensUsed);
|
|
11326
|
-
const
|
|
11327
|
-
const semanticResults = store.search(embedding, limit * 2);
|
|
11328
|
-
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
11532
|
+
const prefilterStartTime = import_perf_hooks.performance.now();
|
|
11329
11533
|
let branchChunkIds = null;
|
|
11330
11534
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
11331
11535
|
branchChunkIds = new Set(
|
|
11332
11536
|
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
11333
11537
|
);
|
|
11334
11538
|
}
|
|
11335
|
-
const
|
|
11336
|
-
const shouldPrefilterByBranch = branchChunkIds !== null && (this.config.scope === "global" || branchChunkIds.size > 0);
|
|
11337
|
-
const allowBranchPrefilterFallback = this.config.scope !== "global";
|
|
11338
|
-
const prefilteredSemantic = shouldPrefilterByBranch && branchChunkIds ? semanticResults.filter((r) => branchChunkIds.has(r.id)) : semanticResults;
|
|
11339
|
-
const semanticCandidates = allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0 ? semanticResults : prefilteredSemantic;
|
|
11539
|
+
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
11340
11540
|
const prefilterMs = import_perf_hooks.performance.now() - prefilterStartTime;
|
|
11341
|
-
|
|
11541
|
+
const vectorStartTime = import_perf_hooks.performance.now();
|
|
11542
|
+
const semanticCandidates = this.searchSemanticCandidates(
|
|
11543
|
+
store,
|
|
11544
|
+
embedding,
|
|
11545
|
+
limit * 2,
|
|
11546
|
+
branchChunkIds,
|
|
11547
|
+
shouldPrefilterByBranch
|
|
11548
|
+
);
|
|
11549
|
+
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
11550
|
+
if (this.config.scope !== "global" && branchChunkIds && !hasInitializedBranchCatalog) {
|
|
11342
11551
|
this.logger.search("warn", "Branch prefilter skipped because branch catalog is empty", {
|
|
11343
11552
|
branch: this.currentBranch
|
|
11344
11553
|
});
|
|
11345
11554
|
}
|
|
11346
|
-
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
11347
|
-
this.logger.search("warn", "Branch prefilter produced no semantic overlap, using unfiltered semantic candidates", {
|
|
11348
|
-
branch: this.currentBranch
|
|
11349
|
-
});
|
|
11350
|
-
}
|
|
11351
11555
|
const rerankTopN = this.config.search.rerankTopN;
|
|
11352
11556
|
const ranked = rankSemanticOnlyResults(code, semanticCandidates, {
|
|
11353
11557
|
rerankTopN,
|
|
@@ -11356,21 +11560,10 @@ var Indexer = class _Indexer {
|
|
|
11356
11560
|
});
|
|
11357
11561
|
const filtered = ranked.filter((r) => {
|
|
11358
11562
|
if (r.score < this.config.search.minScore) return false;
|
|
11359
|
-
if (
|
|
11360
|
-
if (r.metadata.filePath ===
|
|
11563
|
+
if (excludedStoredFile) {
|
|
11564
|
+
if (r.metadata.filePath === excludedStoredFile) return false;
|
|
11361
11565
|
}
|
|
11362
|
-
|
|
11363
|
-
const ext = r.metadata.filePath.split(".").pop()?.toLowerCase();
|
|
11364
|
-
if (ext !== options.fileType.toLowerCase().replace(/^\./, "")) return false;
|
|
11365
|
-
}
|
|
11366
|
-
if (options?.directory) {
|
|
11367
|
-
const normalizedDir = options.directory.replace(/^\/|\/$/g, "");
|
|
11368
|
-
if (!r.metadata.filePath.includes(`/${normalizedDir}/`) && !r.metadata.filePath.includes(`${normalizedDir}/`)) return false;
|
|
11369
|
-
}
|
|
11370
|
-
if (options?.chunkType) {
|
|
11371
|
-
if (r.metadata.chunkType !== options.chunkType) return false;
|
|
11372
|
-
}
|
|
11373
|
-
return true;
|
|
11566
|
+
return matchesHardSearchFilters(r, options, this.projectRoot);
|
|
11374
11567
|
}).slice(0, limit);
|
|
11375
11568
|
const totalSearchMs = import_perf_hooks.performance.now() - searchStartTime;
|
|
11376
11569
|
this.logger.recordSearch(totalSearchMs, {
|
|
@@ -11390,10 +11583,11 @@ var Indexer = class _Indexer {
|
|
|
11390
11583
|
return Promise.all(
|
|
11391
11584
|
filtered.map(async (r) => {
|
|
11392
11585
|
let content = "";
|
|
11586
|
+
const resolvedFilePath = this.resolveStoredFilePath(r.metadata.filePath);
|
|
11393
11587
|
if (this.config.search.includeContext) {
|
|
11394
11588
|
try {
|
|
11395
11589
|
const fileContent = await import_fs10.promises.readFile(
|
|
11396
|
-
|
|
11590
|
+
resolvedFilePath,
|
|
11397
11591
|
"utf-8"
|
|
11398
11592
|
);
|
|
11399
11593
|
const lines = fileContent.split("\n");
|
|
@@ -11403,7 +11597,7 @@ var Indexer = class _Indexer {
|
|
|
11403
11597
|
}
|
|
11404
11598
|
}
|
|
11405
11599
|
return {
|
|
11406
|
-
filePath:
|
|
11600
|
+
filePath: resolvedFilePath,
|
|
11407
11601
|
startLine: r.metadata.startLine,
|
|
11408
11602
|
endLine: r.metadata.endLine,
|
|
11409
11603
|
content,
|
|
@@ -11424,7 +11618,7 @@ var Indexer = class _Indexer {
|
|
|
11424
11618
|
for (const edge of database.getCallersWithContext(targetName, branchKey, callTypeFilter)) {
|
|
11425
11619
|
if (!seen.has(edge.id)) {
|
|
11426
11620
|
seen.add(edge.id);
|
|
11427
|
-
results.push(edge);
|
|
11621
|
+
results.push(this.resolveCallEdgeFilePath(edge));
|
|
11428
11622
|
}
|
|
11429
11623
|
}
|
|
11430
11624
|
}
|
|
@@ -11443,7 +11637,7 @@ var Indexer = class _Indexer {
|
|
|
11443
11637
|
const safelyMatchesUnresolvedSymbol = includeUnresolved && !edge.toSymbolId;
|
|
11444
11638
|
if (!matchesResolvedSymbol && !safelyMatchesUnresolvedSymbol || seen.has(edge.id)) continue;
|
|
11445
11639
|
seen.add(edge.id);
|
|
11446
|
-
results.push(edge);
|
|
11640
|
+
results.push(this.resolveCallEdgeFilePath(edge));
|
|
11447
11641
|
}
|
|
11448
11642
|
}
|
|
11449
11643
|
return results;
|
|
@@ -11457,7 +11651,7 @@ var Indexer = class _Indexer {
|
|
|
11457
11651
|
for (const edge of database.getCallees(symbolId, branchKey, callTypeFilter)) {
|
|
11458
11652
|
if (!seen.has(edge.id)) {
|
|
11459
11653
|
seen.add(edge.id);
|
|
11460
|
-
results.push(edge);
|
|
11654
|
+
results.push(this.resolveCallEdgeFilePath(edge));
|
|
11461
11655
|
}
|
|
11462
11656
|
}
|
|
11463
11657
|
}
|
|
@@ -11473,7 +11667,7 @@ var Indexer = class _Indexer {
|
|
|
11473
11667
|
shortest = path30;
|
|
11474
11668
|
}
|
|
11475
11669
|
}
|
|
11476
|
-
return shortest;
|
|
11670
|
+
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
11477
11671
|
}
|
|
11478
11672
|
async findCallPathBySymbolIds(fromSymbolId, toSymbolId, maxDepth = 10) {
|
|
11479
11673
|
const { database, readIssues } = await this.ensureInitialized();
|
|
@@ -11539,7 +11733,7 @@ var Indexer = class _Indexer {
|
|
|
11539
11733
|
shortest = path30;
|
|
11540
11734
|
}
|
|
11541
11735
|
}
|
|
11542
|
-
return shortest;
|
|
11736
|
+
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
11543
11737
|
}
|
|
11544
11738
|
async getCallGraphSymbols() {
|
|
11545
11739
|
const { database, readIssues } = await this.ensureInitialized();
|
|
@@ -11547,7 +11741,7 @@ var Indexer = class _Indexer {
|
|
|
11547
11741
|
const symbols = /* @__PURE__ */ new Map();
|
|
11548
11742
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
11549
11743
|
for (const symbol of database.getSymbolsForBranch(branchKey)) {
|
|
11550
|
-
symbols.set(symbol.id, symbol);
|
|
11744
|
+
symbols.set(symbol.id, this.resolveFilePathRecord(symbol));
|
|
11551
11745
|
}
|
|
11552
11746
|
}
|
|
11553
11747
|
return [...symbols.values()];
|
|
@@ -11556,31 +11750,32 @@ var Indexer = class _Indexer {
|
|
|
11556
11750
|
const { database, readIssues } = await this.ensureInitialized();
|
|
11557
11751
|
this.requireReadableComponents(readIssues, "database");
|
|
11558
11752
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
11559
|
-
return database.getSymbolsForBranch(resolvedBranch);
|
|
11753
|
+
return database.getSymbolsForBranch(resolvedBranch).map((symbol) => this.resolveFilePathRecord(symbol));
|
|
11560
11754
|
}
|
|
11561
11755
|
async getSymbolsForFiles(filePaths, branch) {
|
|
11562
11756
|
const { database, readIssues } = await this.ensureInitialized();
|
|
11563
11757
|
this.requireReadableComponents(readIssues, "database");
|
|
11564
11758
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
11565
|
-
|
|
11759
|
+
const storedFilePaths = filePaths.map((filePath) => this.toStoredFilePath(filePath));
|
|
11760
|
+
return database.getSymbolsForFiles(storedFilePaths, resolvedBranch).map((symbol) => this.resolveFilePathRecord(symbol));
|
|
11566
11761
|
}
|
|
11567
11762
|
async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
|
|
11568
11763
|
const { database, readIssues } = await this.ensureInitialized();
|
|
11569
11764
|
this.requireReadableComponents(readIssues, "database");
|
|
11570
11765
|
const branch = this.getBranchCatalogKey();
|
|
11571
|
-
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
|
|
11766
|
+
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth).map((entry) => this.resolveFilePathRecord(entry));
|
|
11572
11767
|
}
|
|
11573
11768
|
async detectCommunities(branch, symbolIds) {
|
|
11574
11769
|
const { database, readIssues } = await this.ensureInitialized();
|
|
11575
11770
|
this.requireReadableComponents(readIssues, "database");
|
|
11576
11771
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
11577
|
-
return database.detectCommunities(resolvedBranch, symbolIds);
|
|
11772
|
+
return database.detectCommunities(resolvedBranch, symbolIds).map((entry) => this.resolveFilePathRecord(entry));
|
|
11578
11773
|
}
|
|
11579
11774
|
async computeCentrality(branch) {
|
|
11580
11775
|
const { database, readIssues } = await this.ensureInitialized();
|
|
11581
11776
|
this.requireReadableComponents(readIssues, "database");
|
|
11582
11777
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
11583
|
-
return database.computeCentrality(resolvedBranch);
|
|
11778
|
+
return database.computeCentrality(resolvedBranch).map((entry) => this.resolveFilePathRecord(entry));
|
|
11584
11779
|
}
|
|
11585
11780
|
async getPrImpact(opts, onPreparationProgress) {
|
|
11586
11781
|
const initialState = await this.ensureInitialized();
|
|
@@ -11616,10 +11811,8 @@ var Indexer = class _Indexer {
|
|
|
11616
11811
|
const requestedRef = opts.pr !== void 0 ? expectedCommit : headRefName ?? resolvedBranch;
|
|
11617
11812
|
const storedCommit = this.getStoredBranchCommit(database, catalogIdentity);
|
|
11618
11813
|
const catalogIdentityMatches = storedCommit === expectedCommit;
|
|
11619
|
-
const
|
|
11620
|
-
|
|
11621
|
-
) === SYMBOL_EXTRACTOR_VERSION;
|
|
11622
|
-
if (branchSymbols.length === 0 || !catalogIdentityMatches || !symbolsCurrent) {
|
|
11814
|
+
const migrationsCurrent = this.areBranchMigrationVersionsCurrent(database, catalogIdentity);
|
|
11815
|
+
if (branchSymbols.length === 0 || !catalogIdentityMatches || !migrationsCurrent) {
|
|
11623
11816
|
if (!resolvedBranch || resolvedBranch === "default") {
|
|
11624
11817
|
throw new Error("Run index_codebase first to build the call graph and symbol index for this project.");
|
|
11625
11818
|
}
|
|
@@ -11675,8 +11868,9 @@ var Indexer = class _Indexer {
|
|
|
11675
11868
|
);
|
|
11676
11869
|
}
|
|
11677
11870
|
}
|
|
11678
|
-
const
|
|
11679
|
-
const
|
|
11871
|
+
const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(path14.resolve(this.projectRoot, filePath)));
|
|
11872
|
+
const storedChangedFiles = toStoredChangedFiles(changedFiles);
|
|
11873
|
+
const directSymbols = database.getSymbolsForFiles(storedChangedFiles, branchKey);
|
|
11680
11874
|
const directIds = directSymbols.map((s) => s.id);
|
|
11681
11875
|
const direction = opts.direction ?? "both";
|
|
11682
11876
|
const maxDepth = opts.maxDepth ?? 5;
|
|
@@ -11718,7 +11912,7 @@ var Indexer = class _Indexer {
|
|
|
11718
11912
|
id: c.symbolId,
|
|
11719
11913
|
name: c.symbolName,
|
|
11720
11914
|
callerCount: c.callerCount,
|
|
11721
|
-
filePath: c.filePath
|
|
11915
|
+
filePath: this.resolveStoredFilePath(c.filePath)
|
|
11722
11916
|
}));
|
|
11723
11917
|
const totalAffected = allAffectedIds.length;
|
|
11724
11918
|
let riskLevel;
|
|
@@ -11758,9 +11952,9 @@ var Indexer = class _Indexer {
|
|
|
11758
11952
|
projectRoot: this.projectRoot,
|
|
11759
11953
|
baseBranch: this.baseBranch
|
|
11760
11954
|
});
|
|
11761
|
-
const
|
|
11955
|
+
const otherStored = toStoredChangedFiles(otherChanged.files);
|
|
11762
11956
|
const prBranchKey = this.getBranchCatalogKeyFor(otherChanged.catalogIdentity);
|
|
11763
|
-
const otherSymbols = database.getSymbolsForFiles(
|
|
11957
|
+
const otherSymbols = database.getSymbolsForFiles(otherStored, prBranchKey);
|
|
11764
11958
|
const otherLabels = /* @__PURE__ */ new Set();
|
|
11765
11959
|
for (const sym of otherSymbols) {
|
|
11766
11960
|
const label = symbolToCommunity.get(structuralKey(sym.filePath, sym.name));
|
|
@@ -11791,12 +11985,12 @@ var Indexer = class _Indexer {
|
|
|
11791
11985
|
id: s.id,
|
|
11792
11986
|
name: s.name,
|
|
11793
11987
|
kind: s.kind,
|
|
11794
|
-
filePath: s.filePath
|
|
11988
|
+
filePath: this.resolveStoredFilePath(s.filePath)
|
|
11795
11989
|
})),
|
|
11796
11990
|
transitiveCallers: transitiveCallers.map((c) => ({
|
|
11797
11991
|
id: c.symbolId,
|
|
11798
11992
|
name: c.symbolName,
|
|
11799
|
-
filePath: c.filePath,
|
|
11993
|
+
filePath: this.resolveStoredFilePath(c.filePath),
|
|
11800
11994
|
depth: c.depth
|
|
11801
11995
|
})),
|
|
11802
11996
|
totalAffected,
|
|
@@ -11826,7 +12020,7 @@ var Indexer = class _Indexer {
|
|
|
11826
12020
|
const absoluteDirectoryFilter = directory ? path14.resolve(this.projectRoot, directory) : void 0;
|
|
11827
12021
|
for (const filePath of filePaths) {
|
|
11828
12022
|
if (directory) {
|
|
11829
|
-
const absoluteFilePath =
|
|
12023
|
+
const absoluteFilePath = this.resolveStoredFilePath(filePath);
|
|
11830
12024
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
11831
12025
|
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path14.sep));
|
|
11832
12026
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
@@ -11835,14 +12029,14 @@ var Indexer = class _Indexer {
|
|
|
11835
12029
|
}
|
|
11836
12030
|
for (const sym of database.getSymbolsByFile(filePath)) {
|
|
11837
12031
|
if (symbolIdSet.has(sym.id) && !seenSymbols.has(sym.id)) {
|
|
11838
|
-
seenSymbols.set(sym.id, sym);
|
|
12032
|
+
seenSymbols.set(sym.id, this.resolveFilePathRecord(sym));
|
|
11839
12033
|
}
|
|
11840
12034
|
}
|
|
11841
12035
|
}
|
|
11842
12036
|
for (const symbolId of seenSymbols.keys()) {
|
|
11843
12037
|
for (const edge of database.getCallees(symbolId, branchKey)) {
|
|
11844
12038
|
if (!seenEdges.has(edge.id)) {
|
|
11845
|
-
seenEdges.set(edge.id, edge);
|
|
12039
|
+
seenEdges.set(edge.id, this.resolveCallEdgeFilePath(edge));
|
|
11846
12040
|
}
|
|
11847
12041
|
}
|
|
11848
12042
|
}
|
|
@@ -12114,11 +12308,11 @@ function formatIndexStats(stats, verbose = false) {
|
|
|
12114
12308
|
} else if (stats.indexedChunks === 0) {
|
|
12115
12309
|
lines.push(`${stats.totalFiles} files, removed ${stats.removedChunks} stale chunks, ${stats.existingChunks} chunks remain.`);
|
|
12116
12310
|
} else {
|
|
12117
|
-
let
|
|
12311
|
+
let main = `${stats.totalFiles} files processed, ${stats.indexedChunks} new chunks embedded.`;
|
|
12118
12312
|
if (stats.existingChunks > 0) {
|
|
12119
|
-
|
|
12313
|
+
main += ` ${stats.existingChunks} unchanged chunks skipped.`;
|
|
12120
12314
|
}
|
|
12121
|
-
lines.push(
|
|
12315
|
+
lines.push(main);
|
|
12122
12316
|
if (stats.removedChunks > 0) {
|
|
12123
12317
|
lines.push(`Removed ${stats.removedChunks} stale chunks.`);
|
|
12124
12318
|
}
|
|
@@ -13292,10 +13486,10 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
|
|
|
13292
13486
|
}
|
|
13293
13487
|
coordinatorKeysByProject.set(projectKey, key);
|
|
13294
13488
|
}
|
|
13295
|
-
function startAutoIndex(projectRoot, host
|
|
13489
|
+
function startAutoIndex(projectRoot, host, source = "startup") {
|
|
13296
13490
|
return getCoordinator(projectRoot, host)?.start(source) ?? null;
|
|
13297
13491
|
}
|
|
13298
|
-
function requestBackgroundIndex(projectRoot, host
|
|
13492
|
+
function requestBackgroundIndex(projectRoot, host) {
|
|
13299
13493
|
return getCoordinator(projectRoot, host)?.request({
|
|
13300
13494
|
checkFreshness: false,
|
|
13301
13495
|
force: false,
|
|
@@ -13310,14 +13504,14 @@ function runCoordinatedIndex(projectRoot, host, force, onProgress) {
|
|
|
13310
13504
|
source: "manual"
|
|
13311
13505
|
}) ?? null;
|
|
13312
13506
|
}
|
|
13313
|
-
function getAutoIndexStatus(projectRoot, host
|
|
13507
|
+
function getAutoIndexStatus(projectRoot, host) {
|
|
13314
13508
|
return getCoordinator(projectRoot, host)?.snapshot() ?? {
|
|
13315
13509
|
enabled: false,
|
|
13316
13510
|
state: "idle",
|
|
13317
13511
|
updatedAt: now()
|
|
13318
13512
|
};
|
|
13319
13513
|
}
|
|
13320
|
-
async function waitForAutoIndexForRetrieval(projectRoot, host
|
|
13514
|
+
async function waitForAutoIndexForRetrieval(projectRoot, host) {
|
|
13321
13515
|
const coordinator = getCoordinator(projectRoot, host);
|
|
13322
13516
|
if (!coordinator) return { ready: true };
|
|
13323
13517
|
const initial = coordinator.snapshot();
|
|
@@ -13364,7 +13558,7 @@ async function waitForAutoIndexForRetrieval(projectRoot, host = "opencode") {
|
|
|
13364
13558
|
text: `Automatic indexing is ${status.state}. Retry shortly or call index_status for progress. You can also run index_codebase explicitly.`
|
|
13365
13559
|
};
|
|
13366
13560
|
}
|
|
13367
|
-
async function stopAutoIndex(projectRoot, host
|
|
13561
|
+
async function stopAutoIndex(projectRoot, host) {
|
|
13368
13562
|
await getCoordinator(projectRoot, host)?.stop();
|
|
13369
13563
|
}
|
|
13370
13564
|
async function hasReadableCurrentIndex(coordinator) {
|
|
@@ -13511,7 +13705,7 @@ function loadJsonFile(filePath) {
|
|
|
13511
13705
|
function loadConfigFile(filePath) {
|
|
13512
13706
|
return loadJsonFile(filePath);
|
|
13513
13707
|
}
|
|
13514
|
-
function loadProjectConfigLayer(projectRoot, host
|
|
13708
|
+
function loadProjectConfigLayer(projectRoot, host) {
|
|
13515
13709
|
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
13516
13710
|
const projectConfig = loadJsonFile(projectConfigPath);
|
|
13517
13711
|
if (!projectConfig) {
|
|
@@ -13528,7 +13722,7 @@ function loadProjectConfigLayer(projectRoot, host = "opencode") {
|
|
|
13528
13722
|
}
|
|
13529
13723
|
return normalizedConfig;
|
|
13530
13724
|
}
|
|
13531
|
-
function loadMergedConfig(projectRoot, host
|
|
13725
|
+
function loadMergedConfig(projectRoot, host) {
|
|
13532
13726
|
const globalConfigPath = resolveGlobalConfigPath(host);
|
|
13533
13727
|
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
13534
13728
|
let globalConfig = null;
|
|
@@ -13597,7 +13791,7 @@ function toConfigRecord(rawConfig) {
|
|
|
13597
13791
|
}
|
|
13598
13792
|
return { ...rawConfig };
|
|
13599
13793
|
}
|
|
13600
|
-
function loadRuntimeConfig(projectRoot, host
|
|
13794
|
+
function loadRuntimeConfig(projectRoot, host) {
|
|
13601
13795
|
return normalizeKnowledgeBasePaths(toConfigRecord(loadMergedConfig(projectRoot, host)), projectRoot);
|
|
13602
13796
|
}
|
|
13603
13797
|
|
|
@@ -13680,14 +13874,14 @@ function getOrCreateIndexer(projectRoot, host) {
|
|
|
13680
13874
|
configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
|
|
13681
13875
|
return indexer;
|
|
13682
13876
|
}
|
|
13683
|
-
function initializeTools(projectRoot, config, host
|
|
13877
|
+
function initializeTools(projectRoot, config, host) {
|
|
13684
13878
|
defaultProjectRoots.set(host, projectRoot);
|
|
13685
13879
|
const key = getIndexerCacheKey(projectRoot, host);
|
|
13686
13880
|
configCache.set(key, config);
|
|
13687
13881
|
indexerCache.set(key, new Indexer(projectRoot, config, host));
|
|
13688
13882
|
configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
|
|
13689
13883
|
}
|
|
13690
|
-
function getIndexerForProject(projectRoot, host
|
|
13884
|
+
function getIndexerForProject(projectRoot, host) {
|
|
13691
13885
|
const root = getProjectRoot(projectRoot, host);
|
|
13692
13886
|
return getOrCreateIndexer(root, host);
|
|
13693
13887
|
}
|
|
@@ -13695,7 +13889,7 @@ function recordToolEffectiveness(projectRoot, host, event) {
|
|
|
13695
13889
|
if (!isToolEffectivenessEnabled(projectRoot, host)) return;
|
|
13696
13890
|
safelyRecordToolEffectiveness(event);
|
|
13697
13891
|
}
|
|
13698
|
-
function refreshIndexerForDirectory(projectRoot, host
|
|
13892
|
+
function refreshIndexerForDirectory(projectRoot, host, config = parseConfig(loadRuntimeConfig(projectRoot, host))) {
|
|
13699
13893
|
const key = getIndexerCacheKey(projectRoot, host);
|
|
13700
13894
|
configCache.set(key, config);
|
|
13701
13895
|
indexerCache.set(key, new Indexer(projectRoot, config, host));
|
|
@@ -15229,7 +15423,7 @@ function loadRawConfig(projectRoot, configPath) {
|
|
|
15229
15423
|
fromPath
|
|
15230
15424
|
);
|
|
15231
15425
|
}
|
|
15232
|
-
const projectConfig = resolveProjectConfigPath(projectRoot);
|
|
15426
|
+
const projectConfig = resolveProjectConfigPath(projectRoot, "opencode");
|
|
15233
15427
|
if ((0, import_fs15.existsSync)(projectConfig)) {
|
|
15234
15428
|
return normalizeEvalConfigKnowledgeBases(
|
|
15235
15429
|
parseJsonConfigFile(projectConfig),
|
|
@@ -15244,7 +15438,7 @@ function loadRawConfig(projectRoot, configPath) {
|
|
|
15244
15438
|
return {};
|
|
15245
15439
|
}
|
|
15246
15440
|
function getIndexRootPath(projectRoot, scope) {
|
|
15247
|
-
return scope === "global" ? getGlobalIndexPath() : resolveProjectIndexPath(projectRoot, scope);
|
|
15441
|
+
return scope === "global" ? getGlobalIndexPath("opencode") : resolveProjectIndexPath(projectRoot, scope, "opencode");
|
|
15248
15442
|
}
|
|
15249
15443
|
function getLocalProjectIndexRoot(projectRoot) {
|
|
15250
15444
|
return path21.join(projectRoot, ".opencode", "index");
|
|
@@ -15260,7 +15454,7 @@ function clearIndexRoot(projectRoot, scope) {
|
|
|
15260
15454
|
}
|
|
15261
15455
|
function ensureLocalEvalProjectConfig(projectRoot, configPath) {
|
|
15262
15456
|
const localConfigPath = getLocalProjectConfigPath(projectRoot);
|
|
15263
|
-
const resolvedConfigPath = configPath ? toAbsolute(projectRoot, configPath) : resolveProjectConfigPath(projectRoot);
|
|
15457
|
+
const resolvedConfigPath = configPath ? toAbsolute(projectRoot, configPath) : resolveProjectConfigPath(projectRoot, "opencode");
|
|
15264
15458
|
if (!configPath && (0, import_fs15.existsSync)(localConfigPath)) {
|
|
15265
15459
|
return localConfigPath;
|
|
15266
15460
|
}
|
|
@@ -15695,7 +15889,7 @@ async function runEvaluation(options) {
|
|
|
15695
15889
|
if (options.reindex) {
|
|
15696
15890
|
clearIndexRoot(options.projectRoot, effectiveConfig.scope);
|
|
15697
15891
|
}
|
|
15698
|
-
const indexer = new Indexer(options.projectRoot, effectiveConfig);
|
|
15892
|
+
const indexer = new Indexer(options.projectRoot, effectiveConfig, "opencode");
|
|
15699
15893
|
try {
|
|
15700
15894
|
await indexer.index();
|
|
15701
15895
|
const perQuery = [];
|
|
@@ -15898,9 +16092,9 @@ var path23 = __toESM(require("path"), 1);
|
|
|
15898
16092
|
function printUsage() {
|
|
15899
16093
|
console.log(`
|
|
15900
16094
|
Usage:
|
|
15901
|
-
|
|
15902
|
-
|
|
15903
|
-
|
|
16095
|
+
${MCP_BINARY_CURRENT_NAME} eval run [options]
|
|
16096
|
+
${MCP_BINARY_CURRENT_NAME} eval compare --against <summary.json> [options]
|
|
16097
|
+
${MCP_BINARY_CURRENT_NAME} eval diff --current <summary.json> --against <summary.json> [options]
|
|
15904
16098
|
|
|
15905
16099
|
Options:
|
|
15906
16100
|
--project <path> Project root (default: cwd)
|
|
@@ -16189,11 +16383,21 @@ async function handleEvalCommand(args, cwd) {
|
|
|
16189
16383
|
throw new Error(`Unknown eval subcommand: ${subcommand}`);
|
|
16190
16384
|
}
|
|
16191
16385
|
|
|
16192
|
-
// src/mcp
|
|
16386
|
+
// src/adapters/mcp/server.ts
|
|
16193
16387
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
16388
|
+
|
|
16389
|
+
// src/package-metadata.ts
|
|
16194
16390
|
var import_fs18 = require("fs");
|
|
16391
|
+
var import_meta2 = {};
|
|
16392
|
+
function getPackageVersion() {
|
|
16393
|
+
const raw = JSON.parse((0, import_fs18.readFileSync)(new URL("../package.json", import_meta2.url), "utf-8"));
|
|
16394
|
+
if (raw && typeof raw === "object" && "version" in raw && typeof raw.version === "string") {
|
|
16395
|
+
return raw.version;
|
|
16396
|
+
}
|
|
16397
|
+
return "0.0.0";
|
|
16398
|
+
}
|
|
16195
16399
|
|
|
16196
|
-
// src/mcp
|
|
16400
|
+
// src/adapters/mcp/register-prompts.ts
|
|
16197
16401
|
var import_zod = require("zod");
|
|
16198
16402
|
function registerMcpPrompts(server) {
|
|
16199
16403
|
server.prompt(
|
|
@@ -16283,7 +16487,7 @@ Use the implementation_lookup tool to find where this symbol is defined. This pr
|
|
|
16283
16487
|
);
|
|
16284
16488
|
}
|
|
16285
16489
|
|
|
16286
|
-
// src/mcp
|
|
16490
|
+
// src/adapters/mcp/register-tools.ts
|
|
16287
16491
|
var import_zod2 = require("zod");
|
|
16288
16492
|
|
|
16289
16493
|
// src/tools/contracts.ts
|
|
@@ -16423,13 +16627,89 @@ function formatPrImpact(result) {
|
|
|
16423
16627
|
return lines.join("\n");
|
|
16424
16628
|
}
|
|
16425
16629
|
|
|
16426
|
-
// src/
|
|
16630
|
+
// src/tools/tool-names.ts
|
|
16631
|
+
var TOOL_NAME = {
|
|
16632
|
+
CODEBASE_CONTEXT: "codebase_context",
|
|
16633
|
+
CODEBASE_SEARCH: "codebase_search",
|
|
16634
|
+
CODEBASE_PEEK: "codebase_peek",
|
|
16635
|
+
FIND_SIMILAR: "find_similar",
|
|
16636
|
+
IMPLEMENTATION_LOOKUP: "implementation_lookup",
|
|
16637
|
+
INDEX_CODEBASE: "index_codebase",
|
|
16638
|
+
INDEX_STATUS: "index_status",
|
|
16639
|
+
INDEX_HEALTH_CHECK: "index_health_check",
|
|
16640
|
+
INDEX_METRICS: "index_metrics",
|
|
16641
|
+
INDEX_LOGS: "index_logs",
|
|
16642
|
+
CALL_GRAPH: "call_graph",
|
|
16643
|
+
CALL_GRAPH_PATH: "call_graph_path",
|
|
16644
|
+
PR_IMPACT: "pr_impact",
|
|
16645
|
+
ADD_KNOWLEDGE_BASE: "add_knowledge_base",
|
|
16646
|
+
LIST_KNOWLEDGE_BASES: "list_knowledge_bases",
|
|
16647
|
+
REMOVE_KNOWLEDGE_BASE: "remove_knowledge_base",
|
|
16648
|
+
PI_KNOWLEDGE_BASE_ADD: "knowledge_base_add",
|
|
16649
|
+
PI_KNOWLEDGE_BASE_LIST: "knowledge_base_list",
|
|
16650
|
+
PI_KNOWLEDGE_BASE_REMOVE: "knowledge_base_remove",
|
|
16651
|
+
INDEX_VISUALIZE: "index_visualize"
|
|
16652
|
+
};
|
|
16653
|
+
var PORTABLE_TOOL_NAMES = [
|
|
16654
|
+
TOOL_NAME.CODEBASE_CONTEXT,
|
|
16655
|
+
TOOL_NAME.CODEBASE_SEARCH,
|
|
16656
|
+
TOOL_NAME.CODEBASE_PEEK,
|
|
16657
|
+
TOOL_NAME.INDEX_CODEBASE,
|
|
16658
|
+
TOOL_NAME.INDEX_STATUS,
|
|
16659
|
+
TOOL_NAME.INDEX_HEALTH_CHECK,
|
|
16660
|
+
TOOL_NAME.INDEX_METRICS,
|
|
16661
|
+
TOOL_NAME.INDEX_LOGS,
|
|
16662
|
+
TOOL_NAME.FIND_SIMILAR,
|
|
16663
|
+
TOOL_NAME.IMPLEMENTATION_LOOKUP,
|
|
16664
|
+
TOOL_NAME.CALL_GRAPH,
|
|
16665
|
+
TOOL_NAME.CALL_GRAPH_PATH,
|
|
16666
|
+
TOOL_NAME.PR_IMPACT
|
|
16667
|
+
];
|
|
16668
|
+
var OPENCODE_TOOL_NAMES = [
|
|
16669
|
+
TOOL_NAME.CODEBASE_CONTEXT,
|
|
16670
|
+
TOOL_NAME.CODEBASE_SEARCH,
|
|
16671
|
+
TOOL_NAME.CODEBASE_PEEK,
|
|
16672
|
+
TOOL_NAME.INDEX_CODEBASE,
|
|
16673
|
+
TOOL_NAME.INDEX_STATUS,
|
|
16674
|
+
TOOL_NAME.INDEX_HEALTH_CHECK,
|
|
16675
|
+
TOOL_NAME.INDEX_METRICS,
|
|
16676
|
+
TOOL_NAME.INDEX_LOGS,
|
|
16677
|
+
TOOL_NAME.FIND_SIMILAR,
|
|
16678
|
+
TOOL_NAME.CALL_GRAPH,
|
|
16679
|
+
TOOL_NAME.CALL_GRAPH_PATH,
|
|
16680
|
+
TOOL_NAME.IMPLEMENTATION_LOOKUP,
|
|
16681
|
+
TOOL_NAME.ADD_KNOWLEDGE_BASE,
|
|
16682
|
+
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
16683
|
+
TOOL_NAME.REMOVE_KNOWLEDGE_BASE,
|
|
16684
|
+
TOOL_NAME.PR_IMPACT,
|
|
16685
|
+
TOOL_NAME.INDEX_VISUALIZE
|
|
16686
|
+
];
|
|
16687
|
+
var PI_TOOL_NAMES = [
|
|
16688
|
+
TOOL_NAME.CODEBASE_CONTEXT,
|
|
16689
|
+
TOOL_NAME.CODEBASE_SEARCH,
|
|
16690
|
+
TOOL_NAME.CODEBASE_PEEK,
|
|
16691
|
+
TOOL_NAME.FIND_SIMILAR,
|
|
16692
|
+
TOOL_NAME.IMPLEMENTATION_LOOKUP,
|
|
16693
|
+
TOOL_NAME.INDEX_CODEBASE,
|
|
16694
|
+
TOOL_NAME.INDEX_STATUS,
|
|
16695
|
+
TOOL_NAME.INDEX_HEALTH_CHECK,
|
|
16696
|
+
TOOL_NAME.INDEX_METRICS,
|
|
16697
|
+
TOOL_NAME.INDEX_LOGS,
|
|
16698
|
+
TOOL_NAME.CALL_GRAPH,
|
|
16699
|
+
TOOL_NAME.CALL_GRAPH_PATH,
|
|
16700
|
+
TOOL_NAME.PR_IMPACT,
|
|
16701
|
+
TOOL_NAME.PI_KNOWLEDGE_BASE_LIST,
|
|
16702
|
+
TOOL_NAME.PI_KNOWLEDGE_BASE_ADD,
|
|
16703
|
+
TOOL_NAME.PI_KNOWLEDGE_BASE_REMOVE
|
|
16704
|
+
];
|
|
16705
|
+
|
|
16706
|
+
// src/adapters/mcp/register-tools.ts
|
|
16427
16707
|
function allowNullAsUndefined(schema) {
|
|
16428
16708
|
return import_zod2.z.preprocess((value) => value === null ? void 0 : value, schema);
|
|
16429
16709
|
}
|
|
16430
16710
|
function registerMcpTools(server, runtime) {
|
|
16431
16711
|
server.tool(
|
|
16432
|
-
|
|
16712
|
+
TOOL_NAME.CODEBASE_CONTEXT,
|
|
16433
16713
|
"PREFERRED FIRST TOOL for any question about this repository. Returns a deduplicated, file-diverse evidence pack within tokenBudget. Use before built-in code search, grep, shell search, or broad file reads. Provide from+to for a dependency path, with optional fromFilePath/toFilePath when names are ambiguous; provide symbol for a definition; or provide only query for low-token conceptual discovery. Use call_graph directly for callers or callees.",
|
|
16434
16714
|
{
|
|
16435
16715
|
query: import_zod2.z.string().describe("The codebase question or behavior to locate. Always provide the user's repository question here."),
|
|
@@ -16456,7 +16736,7 @@ function registerMcpTools(server, runtime) {
|
|
|
16456
16736
|
}
|
|
16457
16737
|
);
|
|
16458
16738
|
server.tool(
|
|
16459
|
-
|
|
16739
|
+
TOOL_NAME.CODEBASE_SEARCH,
|
|
16460
16740
|
"FULL-CONTENT semantic retrieval. Use after codebase_peek when you need implementation text, not as the default first step. For exact identifiers or exhaustive matches use grep instead.",
|
|
16461
16741
|
{
|
|
16462
16742
|
query: import_zod2.z.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
|
|
@@ -16488,7 +16768,7 @@ ${formatSearchResults(results, "score")}`;
|
|
|
16488
16768
|
}
|
|
16489
16769
|
);
|
|
16490
16770
|
server.tool(
|
|
16491
|
-
|
|
16771
|
+
TOOL_NAME.CODEBASE_PEEK,
|
|
16492
16772
|
"DIRECT LOW-TOKEN semantic location lookup for unfamiliar-code discovery. Prefer codebase_context when the request may involve definitions or graph navigation; use this specialized tool when you only need conceptual locations.",
|
|
16493
16773
|
{
|
|
16494
16774
|
query: import_zod2.z.string().describe("Natural language description of what code you're looking for."),
|
|
@@ -16519,7 +16799,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16519
16799
|
}
|
|
16520
16800
|
);
|
|
16521
16801
|
server.tool(
|
|
16522
|
-
|
|
16802
|
+
TOOL_NAME.INDEX_CODEBASE,
|
|
16523
16803
|
"Create or refresh the semantic index. Call index_status first when readiness is unknown, then use this tool only if the index is missing, stale, or incompatible. Incremental by default; force=true rebuilds everything.",
|
|
16524
16804
|
{
|
|
16525
16805
|
force: allowNullAsUndefined(import_zod2.z.boolean().optional().default(false)).describe("Force reindex even if already indexed"),
|
|
@@ -16532,7 +16812,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16532
16812
|
}
|
|
16533
16813
|
);
|
|
16534
16814
|
server.tool(
|
|
16535
|
-
|
|
16815
|
+
TOOL_NAME.INDEX_STATUS,
|
|
16536
16816
|
"START HERE once per repository task when index readiness or freshness is unknown. Reports whether semantic retrieval is ready, chunk counts, compatibility, and the embedding provider. If ready, continue with codebase_peek or implementation_lookup; otherwise run index_codebase.",
|
|
16537
16817
|
{},
|
|
16538
16818
|
async () => {
|
|
@@ -16541,7 +16821,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16541
16821
|
}
|
|
16542
16822
|
);
|
|
16543
16823
|
server.tool(
|
|
16544
|
-
|
|
16824
|
+
TOOL_NAME.INDEX_HEALTH_CHECK,
|
|
16545
16825
|
"Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
|
|
16546
16826
|
{},
|
|
16547
16827
|
async () => {
|
|
@@ -16550,7 +16830,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16550
16830
|
}
|
|
16551
16831
|
);
|
|
16552
16832
|
server.tool(
|
|
16553
|
-
|
|
16833
|
+
TOOL_NAME.INDEX_METRICS,
|
|
16554
16834
|
"Get operational metrics plus opt-in privacy-safe repository-tool effectiveness counters. Metrics are memory-only. Set reset=true to clear them before reading. Operational metrics require debug.enabled=true and debug.metrics=true. Privacy-safe aggregates require only effectivenessMetrics.enabled=true.",
|
|
16555
16835
|
{
|
|
16556
16836
|
reset: import_zod2.z.boolean().optional().default(false).describe("Reset in-memory operational and effectiveness metrics before returning the snapshot")
|
|
@@ -16561,7 +16841,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16561
16841
|
}
|
|
16562
16842
|
);
|
|
16563
16843
|
server.tool(
|
|
16564
|
-
|
|
16844
|
+
TOOL_NAME.INDEX_LOGS,
|
|
16565
16845
|
"Get recent debug logs from the codebase indexer. Requires debug.enabled=true in config.",
|
|
16566
16846
|
{
|
|
16567
16847
|
limit: allowNullAsUndefined(import_zod2.z.number().optional().default(20)).describe("Maximum number of log entries to return"),
|
|
@@ -16578,7 +16858,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16578
16858
|
}
|
|
16579
16859
|
);
|
|
16580
16860
|
server.tool(
|
|
16581
|
-
|
|
16861
|
+
TOOL_NAME.FIND_SIMILAR,
|
|
16582
16862
|
"Use when you already have a code snippet and need analogous implementations, duplicates, patterns, or refactoring candidates. For a natural-language concept without example code, start with codebase_peek instead.",
|
|
16583
16863
|
{
|
|
16584
16864
|
code: import_zod2.z.string().describe("The code snippet to find similar code for"),
|
|
@@ -16605,7 +16885,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
16605
16885
|
}
|
|
16606
16886
|
);
|
|
16607
16887
|
server.tool(
|
|
16608
|
-
|
|
16888
|
+
TOOL_NAME.IMPLEMENTATION_LOOKUP,
|
|
16609
16889
|
"FIRST TOOL only for known-symbol definition questions. Returns authoritative source locations and prefers implementations over tests, docs, examples, and fixtures. Do not use for callers, callees, dependency paths, or code flow; use codebase_context with direction or from/to for those questions.",
|
|
16610
16890
|
{
|
|
16611
16891
|
query: import_zod2.z.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
|
|
@@ -16619,7 +16899,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
16619
16899
|
}
|
|
16620
16900
|
);
|
|
16621
16901
|
server.tool(
|
|
16622
|
-
|
|
16902
|
+
TOOL_NAME.CALL_GRAPH,
|
|
16623
16903
|
"Find direct callers or callees by function or method name. Unique names resolve automatically; when duplicate names are reported, retry with filePath. Supports relationship types: Call, MethodCall, Constructor, Import, Inherits, Implements.",
|
|
16624
16904
|
{
|
|
16625
16905
|
name: import_zod2.z.string().describe("Function or method name to query"),
|
|
@@ -16638,7 +16918,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
16638
16918
|
}
|
|
16639
16919
|
);
|
|
16640
16920
|
server.tool(
|
|
16641
|
-
|
|
16921
|
+
TOOL_NAME.CALL_GRAPH_PATH,
|
|
16642
16922
|
"Find the shortest known call path between two named functions or methods. Unique names resolve automatically; when duplicate endpoints are reported, retry with fromFilePath or toFilePath.",
|
|
16643
16923
|
{
|
|
16644
16924
|
from: import_zod2.z.string().describe("Source function/method name (starting point)"),
|
|
@@ -16653,7 +16933,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
16653
16933
|
}
|
|
16654
16934
|
);
|
|
16655
16935
|
server.tool(
|
|
16656
|
-
|
|
16936
|
+
TOOL_NAME.PR_IMPACT,
|
|
16657
16937
|
"FIRST TOOL for pull-request or branch blast-radius questions. Analyzes changed files, affected symbols, transitive dependencies, communities, hub nodes, conflicts, and risk before merging.",
|
|
16658
16938
|
{
|
|
16659
16939
|
pr: allowNullAsUndefined(import_zod2.z.number().optional()).describe("Pull request number to analyze"),
|
|
@@ -16684,22 +16964,14 @@ ${formatSearchResults(results)}` }] };
|
|
|
16684
16964
|
);
|
|
16685
16965
|
}
|
|
16686
16966
|
|
|
16687
|
-
// src/mcp
|
|
16688
|
-
var import_meta2 = {};
|
|
16967
|
+
// src/adapters/mcp/server.ts
|
|
16689
16968
|
function getServerInstructions(host) {
|
|
16690
16969
|
const hostText = `host ${host}`;
|
|
16691
16970
|
return `This MCP server is the preferred codebase-understanding path for ${hostText}. Start a repository task with index_status when index readiness or freshness is unknown. Use codebase_context as the preferred first entry point because it returns a token-budgeted location pack and routes to definitions or call-graph helpers when symbol intent is present. Keep the default tokenBudget for normal discovery, then use implementation_lookup, codebase_search, or a targeted file read only for selected locations that need source content. Use codebase_peek for direct conceptual location lookup. For exact identifiers or exhaustive matches, use grep. After identifying symbols, use call_graph or call_graph_path to trace dependencies. If the index is unavailable, run index_codebase, then retry the retrieval tool.`;
|
|
16692
16971
|
}
|
|
16693
|
-
function
|
|
16694
|
-
const raw = JSON.parse((0, import_fs18.readFileSync)(new URL("../package.json", import_meta2.url), "utf-8"));
|
|
16695
|
-
if (raw && typeof raw === "object" && "version" in raw && typeof raw.version === "string") {
|
|
16696
|
-
return raw.version;
|
|
16697
|
-
}
|
|
16698
|
-
return "0.0.0";
|
|
16699
|
-
}
|
|
16700
|
-
function createMcpServer(projectRoot, config, host = "opencode") {
|
|
16972
|
+
function createMcpServer(projectRoot, config, host) {
|
|
16701
16973
|
const server = new import_mcp.McpServer({
|
|
16702
|
-
name:
|
|
16974
|
+
name: MCP_SERVER_CURRENT_NAME,
|
|
16703
16975
|
version: getPackageVersion()
|
|
16704
16976
|
}, {
|
|
16705
16977
|
instructions: getServerInstructions(host)
|
|
@@ -16734,6 +17006,9 @@ function createMcpServer(projectRoot, config, host = "opencode") {
|
|
|
16734
17006
|
return server;
|
|
16735
17007
|
}
|
|
16736
17008
|
|
|
17009
|
+
// src/watcher/file-watcher.ts
|
|
17010
|
+
var import_fs19 = require("fs");
|
|
17011
|
+
|
|
16737
17012
|
// node_modules/chokidar/index.js
|
|
16738
17013
|
var import_node_events = require("events");
|
|
16739
17014
|
var import_node_fs2 = require("fs");
|
|
@@ -17278,10 +17553,10 @@ var foreach = (val, fn) => {
|
|
|
17278
17553
|
fn(val);
|
|
17279
17554
|
}
|
|
17280
17555
|
};
|
|
17281
|
-
var addAndConvert = (
|
|
17282
|
-
let container =
|
|
17556
|
+
var addAndConvert = (main, prop, item) => {
|
|
17557
|
+
let container = main[prop];
|
|
17283
17558
|
if (!(container instanceof Set)) {
|
|
17284
|
-
|
|
17559
|
+
main[prop] = container = /* @__PURE__ */ new Set([container]);
|
|
17285
17560
|
}
|
|
17286
17561
|
container.add(item);
|
|
17287
17562
|
};
|
|
@@ -17293,12 +17568,12 @@ var clearItem = (cont) => (key) => {
|
|
|
17293
17568
|
delete cont[key];
|
|
17294
17569
|
}
|
|
17295
17570
|
};
|
|
17296
|
-
var delFromSet = (
|
|
17297
|
-
const container =
|
|
17571
|
+
var delFromSet = (main, prop, item) => {
|
|
17572
|
+
const container = main[prop];
|
|
17298
17573
|
if (container instanceof Set) {
|
|
17299
17574
|
container.delete(item);
|
|
17300
17575
|
} else if (container === item) {
|
|
17301
|
-
delete
|
|
17576
|
+
delete main[prop];
|
|
17302
17577
|
}
|
|
17303
17578
|
};
|
|
17304
17579
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
@@ -17479,9 +17754,9 @@ var NodeFsHandler = class {
|
|
|
17479
17754
|
if (this.fsw.closed) {
|
|
17480
17755
|
return;
|
|
17481
17756
|
}
|
|
17482
|
-
const
|
|
17757
|
+
const dirname14 = sp.dirname(file);
|
|
17483
17758
|
const basename7 = sp.basename(file);
|
|
17484
|
-
const parent = this.fsw._getWatchedDir(
|
|
17759
|
+
const parent = this.fsw._getWatchedDir(dirname14);
|
|
17485
17760
|
let prevStats = stats;
|
|
17486
17761
|
if (parent.has(basename7))
|
|
17487
17762
|
return;
|
|
@@ -17508,7 +17783,7 @@ var NodeFsHandler = class {
|
|
|
17508
17783
|
prevStats = newStats2;
|
|
17509
17784
|
}
|
|
17510
17785
|
} catch (error) {
|
|
17511
|
-
this.fsw._remove(
|
|
17786
|
+
this.fsw._remove(dirname14, basename7);
|
|
17512
17787
|
}
|
|
17513
17788
|
} else if (parent.has(basename7)) {
|
|
17514
17789
|
const at = newStats.atimeMs;
|
|
@@ -18472,8 +18747,8 @@ var FileWatcher = class {
|
|
|
18472
18747
|
watcher = null;
|
|
18473
18748
|
projectRoot;
|
|
18474
18749
|
config;
|
|
18475
|
-
host;
|
|
18476
18750
|
configPath;
|
|
18751
|
+
projectConfigPaths;
|
|
18477
18752
|
pendingChanges = /* @__PURE__ */ new Map();
|
|
18478
18753
|
debounceTimer = null;
|
|
18479
18754
|
debounceMs = 1e3;
|
|
@@ -18482,11 +18757,11 @@ var FileWatcher = class {
|
|
|
18482
18757
|
resolveReady = null;
|
|
18483
18758
|
pollingFallbackAttempted = false;
|
|
18484
18759
|
pendingClose = null;
|
|
18485
|
-
constructor(projectRoot, config, host
|
|
18760
|
+
constructor(projectRoot, config, host, options = {}) {
|
|
18486
18761
|
this.projectRoot = projectRoot;
|
|
18487
18762
|
this.config = config;
|
|
18488
|
-
this.host = host;
|
|
18489
18763
|
this.configPath = options.configPath;
|
|
18764
|
+
this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
|
|
18490
18765
|
}
|
|
18491
18766
|
start(handler) {
|
|
18492
18767
|
if (this.watcher) {
|
|
@@ -18504,7 +18779,19 @@ var FileWatcher = class {
|
|
|
18504
18779
|
}
|
|
18505
18780
|
createWatcher(usePolling = false) {
|
|
18506
18781
|
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
18507
|
-
|
|
18782
|
+
let watchTargets = this.projectRoot;
|
|
18783
|
+
if (this.configPath) {
|
|
18784
|
+
watchTargets = [this.projectRoot, this.configPath];
|
|
18785
|
+
} else {
|
|
18786
|
+
const externalConfigTargets = this.projectConfigPaths.filter((projectConfigPath) => {
|
|
18787
|
+
const relativeConfigPath = path25.relative(this.projectRoot, projectConfigPath);
|
|
18788
|
+
return this.isOutsideProjectPath(relativeConfigPath);
|
|
18789
|
+
}).map((projectConfigPath) => (0, import_fs19.existsSync)(projectConfigPath) ? projectConfigPath : this.getNearestExistingDirectory(path25.dirname(projectConfigPath)));
|
|
18790
|
+
const uniqueExternalConfigTargets = [...new Set(externalConfigTargets)];
|
|
18791
|
+
if (uniqueExternalConfigTargets.length > 0) {
|
|
18792
|
+
watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
|
|
18793
|
+
}
|
|
18794
|
+
}
|
|
18508
18795
|
const watcherOptions = {
|
|
18509
18796
|
ignored: (filePath) => {
|
|
18510
18797
|
const relativePath = path25.relative(this.projectRoot, filePath);
|
|
@@ -18512,6 +18799,9 @@ var FileWatcher = class {
|
|
|
18512
18799
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
18513
18800
|
return false;
|
|
18514
18801
|
}
|
|
18802
|
+
if (this.isOutsideProjectPath(relativePath)) {
|
|
18803
|
+
return true;
|
|
18804
|
+
}
|
|
18515
18805
|
if (hasFilteredPathSegment(relativePath, path25.sep)) {
|
|
18516
18806
|
return true;
|
|
18517
18807
|
}
|
|
@@ -18614,14 +18904,22 @@ var FileWatcher = class {
|
|
|
18614
18904
|
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path25.sep}`)
|
|
18615
18905
|
);
|
|
18616
18906
|
}
|
|
18617
|
-
|
|
18618
|
-
|
|
18619
|
-
|
|
18907
|
+
isOutsideProjectPath(relativePath) {
|
|
18908
|
+
return relativePath === ".." || relativePath.startsWith(`..${path25.sep}`) || path25.isAbsolute(relativePath);
|
|
18909
|
+
}
|
|
18910
|
+
getNearestExistingDirectory(directoryPath) {
|
|
18911
|
+
let candidate = directoryPath;
|
|
18912
|
+
while (!(0, import_fs19.existsSync)(candidate)) {
|
|
18913
|
+
const parent = path25.dirname(candidate);
|
|
18914
|
+
if (parent === candidate) break;
|
|
18915
|
+
candidate = parent;
|
|
18620
18916
|
}
|
|
18621
|
-
return
|
|
18622
|
-
|
|
18623
|
-
|
|
18624
|
-
|
|
18917
|
+
return candidate;
|
|
18918
|
+
}
|
|
18919
|
+
getProjectConfigRelativePaths() {
|
|
18920
|
+
return this.projectConfigPaths.map(
|
|
18921
|
+
(configPath) => path25.normalize(path25.relative(this.projectRoot, configPath))
|
|
18922
|
+
);
|
|
18625
18923
|
}
|
|
18626
18924
|
scheduleFlush() {
|
|
18627
18925
|
if (this.debounceTimer) {
|
|
@@ -18748,8 +19046,9 @@ var GitHeadWatcher = class {
|
|
|
18748
19046
|
};
|
|
18749
19047
|
|
|
18750
19048
|
// src/watcher/index.ts
|
|
18751
|
-
function createWatcherWithIndexer(getIndexer, projectRoot, config, host
|
|
19049
|
+
function createWatcherWithIndexer(getIndexer, projectRoot, config, host, options = {}) {
|
|
18752
19050
|
const fileWatcher = new FileWatcher(projectRoot, config, host, options);
|
|
19051
|
+
const configPaths = getConfigPaths(projectRoot, host, options);
|
|
18753
19052
|
configureAutoIndex(projectRoot, host, parseConfig(config), getIndexer);
|
|
18754
19053
|
let stopped = false;
|
|
18755
19054
|
const requestReindex = () => {
|
|
@@ -18766,7 +19065,6 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
18766
19065
|
);
|
|
18767
19066
|
const hasDelete = changes.some((c) => c.type === "unlink");
|
|
18768
19067
|
if (hasAddOrChange || hasDelete) {
|
|
18769
|
-
const configPaths = getConfigPaths(projectRoot, host, options);
|
|
18770
19068
|
if (changes.some((change) => configPaths.includes(pathNormalize(change.path)))) {
|
|
18771
19069
|
const parsedConfig = options.configPath ? parseConfig(loadConfigFile(options.configPath)) : void 0;
|
|
18772
19070
|
const refreshedConfig = refreshIndexerForDirectory(projectRoot, host, parsedConfig);
|
|
@@ -18781,7 +19079,9 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
18781
19079
|
if (isGitRepo(projectRoot)) {
|
|
18782
19080
|
gitWatcher = new GitHeadWatcher(projectRoot);
|
|
18783
19081
|
gitWatcher.start(async (oldBranch, newBranch) => {
|
|
18784
|
-
getIndexer()
|
|
19082
|
+
const indexer = getIndexer();
|
|
19083
|
+
indexer.refreshBranchInfo();
|
|
19084
|
+
indexer.getLogger().branch("info", "Branch changed", {
|
|
18785
19085
|
oldBranch,
|
|
18786
19086
|
newBranch
|
|
18787
19087
|
});
|
|
@@ -18807,10 +19107,9 @@ function getConfigPaths(projectRoot, host, options) {
|
|
|
18807
19107
|
if (options.configPath) {
|
|
18808
19108
|
return [pathNormalize(options.configPath)];
|
|
18809
19109
|
}
|
|
18810
|
-
return
|
|
18811
|
-
|
|
18812
|
-
|
|
18813
|
-
].map((configPath) => pathNormalize(configPath));
|
|
19110
|
+
return getProjectConfigCandidatePaths(projectRoot, host).map(
|
|
19111
|
+
(configPath) => pathNormalize(configPath)
|
|
19112
|
+
);
|
|
18814
19113
|
}
|
|
18815
19114
|
|
|
18816
19115
|
// src/tools/visualize/activity.ts
|
|
@@ -19516,8 +19815,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
19516
19815
|
};
|
|
19517
19816
|
}
|
|
19518
19817
|
|
|
19519
|
-
// src/cli.ts
|
|
19520
|
-
var import_meta3 = {};
|
|
19818
|
+
// src/adapters/mcp/cli.ts
|
|
19521
19819
|
function parseArgs(argv) {
|
|
19522
19820
|
let project = process.cwd();
|
|
19523
19821
|
let config;
|
|
@@ -19539,7 +19837,7 @@ function loadCliRawConfig(args) {
|
|
|
19539
19837
|
return args.config ? loadConfigFile(args.config) : loadMergedConfig(args.project, args.host);
|
|
19540
19838
|
}
|
|
19541
19839
|
function isCliEntrypoint(moduleUrl, argvPath) {
|
|
19542
|
-
return argvPath !== void 0 && (0,
|
|
19840
|
+
return argvPath !== void 0 && (0, import_fs20.realpathSync)((0, import_url.fileURLToPath)(moduleUrl)) === (0, import_fs20.realpathSync)(argvPath);
|
|
19543
19841
|
}
|
|
19544
19842
|
function parseVisualizeArgs(argv, cwd) {
|
|
19545
19843
|
let project = cwd;
|
|
@@ -19568,8 +19866,8 @@ function parseVisualizeArgs(argv, cwd) {
|
|
|
19568
19866
|
async function handleVisualizeCommand(argv, cwd) {
|
|
19569
19867
|
try {
|
|
19570
19868
|
const args = parseVisualizeArgs(argv, cwd);
|
|
19571
|
-
const config = parseConfig(loadMergedConfig(args.project));
|
|
19572
|
-
const indexer = new Indexer(args.project, config);
|
|
19869
|
+
const config = parseConfig(loadMergedConfig(args.project, "opencode"));
|
|
19870
|
+
const indexer = new Indexer(args.project, config, "opencode");
|
|
19573
19871
|
const rawData = await indexer.getVisualizationData({
|
|
19574
19872
|
directory: args.directory
|
|
19575
19873
|
});
|
|
@@ -19587,7 +19885,7 @@ async function handleVisualizeCommand(argv, cwd) {
|
|
|
19587
19885
|
return 1;
|
|
19588
19886
|
}
|
|
19589
19887
|
const outputPath = path29.join(os8.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
19590
|
-
(0,
|
|
19888
|
+
(0, import_fs20.writeFileSync)(outputPath, generateVisualizationHtml(vizData), "utf-8");
|
|
19591
19889
|
console.log(`Temporal call graph visualization generated: ${outputPath}`);
|
|
19592
19890
|
console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
|
|
19593
19891
|
console.log(`Recent change lenses: ${vizData.changes?.length ?? 0}`);
|
|
@@ -19600,16 +19898,16 @@ async function handleVisualizeCommand(argv, cwd) {
|
|
|
19600
19898
|
return 1;
|
|
19601
19899
|
}
|
|
19602
19900
|
}
|
|
19603
|
-
async function
|
|
19604
|
-
if (
|
|
19605
|
-
const exitCode = await handleEvalCommand(
|
|
19901
|
+
async function runMcpCli(argv) {
|
|
19902
|
+
if (argv[2] === "eval") {
|
|
19903
|
+
const exitCode = await handleEvalCommand(argv.slice(3), process.cwd());
|
|
19606
19904
|
process.exit(exitCode);
|
|
19607
19905
|
}
|
|
19608
|
-
if (
|
|
19609
|
-
const exitCode = await handleVisualizeCommand(
|
|
19906
|
+
if (argv[2] === "visualize") {
|
|
19907
|
+
const exitCode = await handleVisualizeCommand(argv.slice(3), process.cwd());
|
|
19610
19908
|
process.exit(exitCode);
|
|
19611
19909
|
}
|
|
19612
|
-
const args = parseArgs(
|
|
19910
|
+
const args = parseArgs(argv);
|
|
19613
19911
|
const rawConfig = loadCliRawConfig(args);
|
|
19614
19912
|
const config = parseConfig(rawConfig);
|
|
19615
19913
|
const server = createMcpServer(args.project, config, args.host);
|
|
@@ -19660,8 +19958,11 @@ function handleMainError(error) {
|
|
|
19660
19958
|
console.error("Fatal: failed to start MCP server");
|
|
19661
19959
|
process.exit(1);
|
|
19662
19960
|
}
|
|
19961
|
+
|
|
19962
|
+
// src/cli.ts
|
|
19963
|
+
var import_meta3 = {};
|
|
19663
19964
|
if (isCliEntrypoint(import_meta3.url, process.argv[1])) {
|
|
19664
|
-
|
|
19965
|
+
runMcpCli(process.argv).catch(handleMainError);
|
|
19665
19966
|
}
|
|
19666
19967
|
// Annotate the CommonJS export names for ESM import in node:
|
|
19667
19968
|
0 && (module.exports = {
|