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.js
CHANGED
|
@@ -651,7 +651,7 @@ var require_ignore = __commonJS({
|
|
|
651
651
|
}
|
|
652
652
|
});
|
|
653
653
|
|
|
654
|
-
// src/cli.ts
|
|
654
|
+
// src/adapters/mcp/cli.ts
|
|
655
655
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
656
656
|
import { realpathSync as realpathSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
657
657
|
import * as os8 from "os";
|
|
@@ -4140,23 +4140,61 @@ import * as os2 from "os";
|
|
|
4140
4140
|
import * as path5 from "path";
|
|
4141
4141
|
import * as module from "module";
|
|
4142
4142
|
import { fileURLToPath } from "url";
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4143
|
+
|
|
4144
|
+
// src/identity-catalog.json
|
|
4145
|
+
var identity_catalog_default = {
|
|
4146
|
+
product: {
|
|
4147
|
+
current: {
|
|
4148
|
+
productName: "opencode-codebase-index",
|
|
4149
|
+
packageName: "opencode-codebase-index",
|
|
4150
|
+
repository: "https://github.com/Helweg/opencode-codebase-index",
|
|
4151
|
+
mcpBinary: "opencode-codebase-index-mcp",
|
|
4152
|
+
mcpServerName: "opencode-codebase-index"
|
|
4153
|
+
},
|
|
4154
|
+
future: {
|
|
4155
|
+
productName: "open-codebase-index",
|
|
4156
|
+
packageName: "open-codebase-index",
|
|
4157
|
+
repository: "https://github.com/Helweg/open-codebase-index",
|
|
4158
|
+
mcpBinary: "open-codebase-index-mcp",
|
|
4159
|
+
mcpServerName: "open-codebase-index"
|
|
4160
|
+
}
|
|
4161
|
+
},
|
|
4162
|
+
native: {
|
|
4163
|
+
binaryName: "codebase-index-native"
|
|
4164
|
+
}
|
|
4165
|
+
};
|
|
4166
|
+
|
|
4167
|
+
// src/identity-catalog.ts
|
|
4168
|
+
var IDENTITY_CATALOG = identity_catalog_default;
|
|
4169
|
+
var CURRENT_PRODUCT = IDENTITY_CATALOG.product.current;
|
|
4170
|
+
var FUTURE_PRODUCT = IDENTITY_CATALOG.product.future;
|
|
4171
|
+
var MCP_SERVER_CURRENT_NAME = CURRENT_PRODUCT.mcpServerName;
|
|
4172
|
+
var MCP_BINARY_CURRENT_NAME = CURRENT_PRODUCT.mcpBinary;
|
|
4173
|
+
var STABLE_NATIVE_BINARY_NAME = IDENTITY_CATALOG.native.binaryName;
|
|
4174
|
+
|
|
4175
|
+
// src/native/binding.ts
|
|
4176
|
+
function getNativeBindingFilename(platform2 = os2.platform(), arch2 = os2.arch()) {
|
|
4147
4177
|
if (platform2 === "darwin" && arch2 === "arm64") {
|
|
4148
|
-
|
|
4149
|
-
}
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
}
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
|
|
4178
|
+
return `${STABLE_NATIVE_BINARY_NAME}.darwin-arm64.node`;
|
|
4179
|
+
}
|
|
4180
|
+
if (platform2 === "darwin" && arch2 === "x64") {
|
|
4181
|
+
return `${STABLE_NATIVE_BINARY_NAME}.darwin-x64.node`;
|
|
4182
|
+
}
|
|
4183
|
+
if (platform2 === "linux" && arch2 === "x64") {
|
|
4184
|
+
return `${STABLE_NATIVE_BINARY_NAME}.linux-x64-gnu.node`;
|
|
4185
|
+
}
|
|
4186
|
+
if (platform2 === "linux" && arch2 === "arm64") {
|
|
4187
|
+
return `${STABLE_NATIVE_BINARY_NAME}.linux-arm64-gnu.node`;
|
|
4159
4188
|
}
|
|
4189
|
+
if (platform2 === "win32" && arch2 === "x64") {
|
|
4190
|
+
return `${STABLE_NATIVE_BINARY_NAME}.win32-x64-msvc.node`;
|
|
4191
|
+
}
|
|
4192
|
+
throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
|
|
4193
|
+
}
|
|
4194
|
+
function resolveNativeBindingPath(packageRoot, platform2 = os2.platform(), arch2 = os2.arch()) {
|
|
4195
|
+
return path5.join(packageRoot, "native", getNativeBindingFilename(platform2, arch2));
|
|
4196
|
+
}
|
|
4197
|
+
function getNativeBinding() {
|
|
4160
4198
|
let currentDir;
|
|
4161
4199
|
let requireTarget;
|
|
4162
4200
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
@@ -4172,7 +4210,7 @@ function getNativeBinding() {
|
|
|
4172
4210
|
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
4173
4211
|
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path5.join("src", "native"));
|
|
4174
4212
|
const packageRoot = isDevMode ? path5.resolve(currentDir, "../..") : path5.resolve(currentDir, "..");
|
|
4175
|
-
const nativePath =
|
|
4213
|
+
const nativePath = resolveNativeBindingPath(packageRoot);
|
|
4176
4214
|
const require2 = module.createRequire(requireTarget);
|
|
4177
4215
|
return require2(nativePath);
|
|
4178
4216
|
}
|
|
@@ -5502,8 +5540,37 @@ function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
|
5502
5540
|
const fallbackPath = path10.join(mainRepoRoot, relativePath);
|
|
5503
5541
|
return existsSync5(fallbackPath) ? fallbackPath : null;
|
|
5504
5542
|
}
|
|
5505
|
-
function
|
|
5506
|
-
|
|
5543
|
+
function getProjectConfigCandidatePaths(projectRoot, host) {
|
|
5544
|
+
const candidates = [path10.join(projectRoot, getProjectConfigRelativePath(host))];
|
|
5545
|
+
if (host !== "opencode") {
|
|
5546
|
+
candidates.push(path10.join(projectRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH));
|
|
5547
|
+
}
|
|
5548
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
5549
|
+
if (mainRepoRoot) {
|
|
5550
|
+
candidates.push(path10.join(mainRepoRoot, getProjectConfigRelativePath(host)));
|
|
5551
|
+
if (host !== "opencode") {
|
|
5552
|
+
candidates.push(path10.join(mainRepoRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH));
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
return [...new Set(candidates)];
|
|
5556
|
+
}
|
|
5557
|
+
function isProjectIndexPathOwnedByProject(projectRoot, indexPath, host) {
|
|
5558
|
+
const projectRoots = [projectRoot];
|
|
5559
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
5560
|
+
if (mainRepoRoot) {
|
|
5561
|
+
projectRoots.push(mainRepoRoot);
|
|
5562
|
+
}
|
|
5563
|
+
const ownedIndexPaths = projectRoots.flatMap((root) => {
|
|
5564
|
+
const indexPaths = [path10.join(root, getProjectIndexRelativePath(host))];
|
|
5565
|
+
if (host !== "opencode") {
|
|
5566
|
+
indexPaths.push(path10.join(root, OPENCODE_PROJECT_INDEX_RELATIVE_PATH));
|
|
5567
|
+
}
|
|
5568
|
+
return indexPaths;
|
|
5569
|
+
});
|
|
5570
|
+
const canonicalIndexPath = canonicalizePathForComparison(indexPath);
|
|
5571
|
+
return ownedIndexPaths.some(
|
|
5572
|
+
(ownedPath) => canonicalizePathForComparison(ownedPath) === canonicalIndexPath
|
|
5573
|
+
);
|
|
5507
5574
|
}
|
|
5508
5575
|
function hasHostProjectConfig(projectRoot, host) {
|
|
5509
5576
|
return existsSync5(path10.join(projectRoot, getProjectConfigRelativePath(host)));
|
|
@@ -5511,7 +5578,7 @@ function hasHostProjectConfig(projectRoot, host) {
|
|
|
5511
5578
|
function hasHostGlobalConfig(host) {
|
|
5512
5579
|
return existsSync5(getGlobalConfigPath(host));
|
|
5513
5580
|
}
|
|
5514
|
-
function getGlobalIndexPath(host
|
|
5581
|
+
function getGlobalIndexPath(host) {
|
|
5515
5582
|
switch (host) {
|
|
5516
5583
|
case "opencode":
|
|
5517
5584
|
return path10.join(os4.homedir(), ".opencode", "global-index");
|
|
@@ -5521,7 +5588,7 @@ function getGlobalIndexPath(host = "opencode") {
|
|
|
5521
5588
|
return path10.join(os4.homedir(), ".codebase-index", "global-index");
|
|
5522
5589
|
}
|
|
5523
5590
|
}
|
|
5524
|
-
function getGlobalConfigPath(host
|
|
5591
|
+
function getGlobalConfigPath(host) {
|
|
5525
5592
|
switch (host) {
|
|
5526
5593
|
case "opencode":
|
|
5527
5594
|
return path10.join(os4.homedir(), ".config", "opencode", "codebase-index.json");
|
|
@@ -5531,7 +5598,7 @@ function getGlobalConfigPath(host = "opencode") {
|
|
|
5531
5598
|
return path10.join(os4.homedir(), ".config", "codebase-index", "config.json");
|
|
5532
5599
|
}
|
|
5533
5600
|
}
|
|
5534
|
-
function resolveGlobalConfigPath(host
|
|
5601
|
+
function resolveGlobalConfigPath(host) {
|
|
5535
5602
|
const hostConfigPath = getGlobalConfigPath(host);
|
|
5536
5603
|
if (existsSync5(hostConfigPath)) {
|
|
5537
5604
|
return hostConfigPath;
|
|
@@ -5544,7 +5611,7 @@ function resolveGlobalConfigPath(host = "opencode") {
|
|
|
5544
5611
|
}
|
|
5545
5612
|
return hostConfigPath;
|
|
5546
5613
|
}
|
|
5547
|
-
function resolveGlobalIndexPath(host
|
|
5614
|
+
function resolveGlobalIndexPath(host) {
|
|
5548
5615
|
const hostIndexPath = getGlobalIndexPath(host);
|
|
5549
5616
|
if (existsSync5(hostIndexPath)) {
|
|
5550
5617
|
return hostIndexPath;
|
|
@@ -5560,37 +5627,35 @@ function resolveGlobalIndexPath(host = "opencode") {
|
|
|
5560
5627
|
}
|
|
5561
5628
|
return hostIndexPath;
|
|
5562
5629
|
}
|
|
5563
|
-
function resolveProjectConfigPath(projectRoot, host
|
|
5564
|
-
const
|
|
5565
|
-
|
|
5566
|
-
return hostConfigPath;
|
|
5567
|
-
}
|
|
5568
|
-
if (host !== "opencode") {
|
|
5569
|
-
const legacyConfigPath = path10.join(projectRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH);
|
|
5570
|
-
if (existsSync5(legacyConfigPath)) {
|
|
5571
|
-
return legacyConfigPath;
|
|
5572
|
-
}
|
|
5573
|
-
}
|
|
5574
|
-
const hostFallback = resolveWorktreeFallbackPath(projectRoot, getProjectConfigRelativePath(host));
|
|
5575
|
-
if (hostFallback) {
|
|
5576
|
-
return hostFallback;
|
|
5577
|
-
}
|
|
5578
|
-
if (host !== "opencode") {
|
|
5579
|
-
const legacyFallback = resolveWorktreeFallbackPath(projectRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH);
|
|
5580
|
-
if (legacyFallback) {
|
|
5581
|
-
return legacyFallback;
|
|
5582
|
-
}
|
|
5583
|
-
}
|
|
5584
|
-
return hostConfigPath;
|
|
5585
|
-
}
|
|
5586
|
-
function resolveWritableProjectConfigPath(projectRoot, host = "opencode") {
|
|
5587
|
-
return path10.join(projectRoot, getProjectConfigRelativePath(host));
|
|
5630
|
+
function resolveProjectConfigPath(projectRoot, host) {
|
|
5631
|
+
const candidates = getProjectConfigCandidatePaths(projectRoot, host);
|
|
5632
|
+
return candidates.find((candidate) => existsSync5(candidate)) ?? path10.join(projectRoot, getProjectConfigRelativePath(host));
|
|
5588
5633
|
}
|
|
5589
|
-
function resolveProjectIndexPath(projectRoot, scope, host
|
|
5634
|
+
function resolveProjectIndexPath(projectRoot, scope, host) {
|
|
5590
5635
|
if (scope === "global") {
|
|
5591
5636
|
return resolveGlobalIndexPath(host);
|
|
5592
5637
|
}
|
|
5593
5638
|
const localIndexPath = path10.join(projectRoot, getProjectIndexRelativePath(host));
|
|
5639
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
5640
|
+
if (mainRepoRoot) {
|
|
5641
|
+
if (hasHostProjectConfig(projectRoot, host)) {
|
|
5642
|
+
return localIndexPath;
|
|
5643
|
+
}
|
|
5644
|
+
if (host !== "opencode") {
|
|
5645
|
+
const localLegacyConfigPath = path10.join(projectRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH);
|
|
5646
|
+
if (existsSync5(localLegacyConfigPath)) {
|
|
5647
|
+
return path10.join(projectRoot, OPENCODE_PROJECT_INDEX_RELATIVE_PATH);
|
|
5648
|
+
}
|
|
5649
|
+
const mainHostConfigPath = path10.join(mainRepoRoot, getProjectConfigRelativePath(host));
|
|
5650
|
+
const mainHostIndexPath = path10.join(mainRepoRoot, getProjectIndexRelativePath(host));
|
|
5651
|
+
const mainLegacyConfigPath = path10.join(mainRepoRoot, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH);
|
|
5652
|
+
const mainLegacyIndexPath = path10.join(mainRepoRoot, OPENCODE_PROJECT_INDEX_RELATIVE_PATH);
|
|
5653
|
+
if (!existsSync5(mainHostConfigPath) && !existsSync5(mainHostIndexPath) && (existsSync5(mainLegacyConfigPath) || existsSync5(mainLegacyIndexPath))) {
|
|
5654
|
+
return mainLegacyIndexPath;
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5657
|
+
return path10.join(mainRepoRoot, getProjectIndexRelativePath(host));
|
|
5658
|
+
}
|
|
5594
5659
|
if (existsSync5(localIndexPath)) {
|
|
5595
5660
|
return localIndexPath;
|
|
5596
5661
|
}
|
|
@@ -5603,9 +5668,6 @@ function resolveProjectIndexPath(projectRoot, scope, host = "opencode") {
|
|
|
5603
5668
|
if (hasHostProjectConfig(projectRoot, host)) {
|
|
5604
5669
|
return localIndexPath;
|
|
5605
5670
|
}
|
|
5606
|
-
if (resolveWorktreeMainRepoRoot(projectRoot)) {
|
|
5607
|
-
return localIndexPath;
|
|
5608
|
-
}
|
|
5609
5671
|
const hostFallback = resolveWorktreeFallbackPath(projectRoot, getProjectIndexRelativePath(host));
|
|
5610
5672
|
if (hostFallback) {
|
|
5611
5673
|
return hostFallback;
|
|
@@ -7682,6 +7744,8 @@ function hasBlameMetadata(metadata) {
|
|
|
7682
7744
|
return blameFromMetadata(metadata) !== void 0;
|
|
7683
7745
|
}
|
|
7684
7746
|
var INDEX_METADATA_VERSION = "1";
|
|
7747
|
+
var PROJECT_PATH_STORAGE_VERSION = "2";
|
|
7748
|
+
var GLOBAL_PATH_STORAGE_VERSION = "1";
|
|
7685
7749
|
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
7686
7750
|
var SWIFT_PARSER_VERSION = "1";
|
|
7687
7751
|
var METAL_PARSER_VERSION = "1";
|
|
@@ -8012,18 +8076,20 @@ function selectIndexableChunks(chunks, limit, semanticOnly) {
|
|
|
8012
8076
|
const indexableChunks = semanticOnly ? chunks.filter((chunk) => chunk.chunkType !== "other") : chunks;
|
|
8013
8077
|
return selectChunksWithFileCoverage(indexableChunks, limit);
|
|
8014
8078
|
}
|
|
8015
|
-
function matchesHardSearchFilters(candidate, options) {
|
|
8079
|
+
function matchesHardSearchFilters(candidate, options, projectRoot) {
|
|
8016
8080
|
if (options?.fileType) {
|
|
8017
8081
|
const ext = candidate.metadata.filePath.split(".").pop()?.toLowerCase();
|
|
8018
8082
|
const requestedExtension = options.fileType.trim().toLowerCase().replace(/^\./, "");
|
|
8019
8083
|
if (ext !== requestedExtension) return false;
|
|
8020
8084
|
}
|
|
8021
8085
|
if (options?.directory) {
|
|
8022
|
-
const
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
const
|
|
8026
|
-
|
|
8086
|
+
const candidatePath = canonicalizePathForComparison(
|
|
8087
|
+
path14.resolve(projectRoot, candidate.metadata.filePath.replace(/\\/g, path14.sep))
|
|
8088
|
+
);
|
|
8089
|
+
const directoryPath = canonicalizePathForComparison(
|
|
8090
|
+
path14.resolve(projectRoot, options.directory.trim().replace(/\\/g, path14.sep))
|
|
8091
|
+
);
|
|
8092
|
+
if (!isPathWithinRoot2(candidatePath, directoryPath)) return false;
|
|
8027
8093
|
}
|
|
8028
8094
|
if (options?.chunkType && candidate.metadata.chunkType !== options.chunkType) {
|
|
8029
8095
|
return false;
|
|
@@ -8045,8 +8111,8 @@ function matchesHardSearchFilters(candidate, options) {
|
|
|
8045
8111
|
}
|
|
8046
8112
|
return true;
|
|
8047
8113
|
}
|
|
8048
|
-
function matchesSearchFilters(candidate, options, minScore) {
|
|
8049
|
-
return candidate.score >= minScore && matchesHardSearchFilters(candidate, options);
|
|
8114
|
+
function matchesSearchFilters(candidate, options, minScore, projectRoot) {
|
|
8115
|
+
return candidate.score >= minScore && matchesHardSearchFilters(candidate, options, projectRoot);
|
|
8050
8116
|
}
|
|
8051
8117
|
function unionCandidates(semanticCandidates, keywordCandidates) {
|
|
8052
8118
|
const byId = /* @__PURE__ */ new Map();
|
|
@@ -8097,7 +8163,7 @@ var Indexer = class _Indexer {
|
|
|
8097
8163
|
readerArtifactFingerprint = null;
|
|
8098
8164
|
writerArtifactFingerprint = null;
|
|
8099
8165
|
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
8100
|
-
constructor(projectRoot, config, host
|
|
8166
|
+
constructor(projectRoot, config, host, runtimeOptions = {}) {
|
|
8101
8167
|
this.projectRoot = projectRoot;
|
|
8102
8168
|
this.projectIdentityHash = hashContent(this.getCanonicalPath(projectRoot)).slice(0, 16);
|
|
8103
8169
|
this.materializedProjectRoot = runtimeOptions.materializedProjectRoot ?? projectRoot;
|
|
@@ -8110,37 +8176,90 @@ var Indexer = class _Indexer {
|
|
|
8110
8176
|
this.indexPathOverride = runtimeOptions.indexPath;
|
|
8111
8177
|
this.config = config;
|
|
8112
8178
|
this.host = host;
|
|
8179
|
+
if (isGitRepo(this.materializedProjectRoot)) {
|
|
8180
|
+
this.currentBranch = this.branchNameOverride ?? getBranchOrDefault(this.materializedProjectRoot);
|
|
8181
|
+
this.baseBranch = getBaseBranch(this.materializedProjectRoot);
|
|
8182
|
+
} else {
|
|
8183
|
+
this.currentBranch = "default";
|
|
8184
|
+
this.baseBranch = "default";
|
|
8185
|
+
}
|
|
8113
8186
|
this.indexPath = this.getIndexPath();
|
|
8114
|
-
this.
|
|
8115
|
-
this.failedBatchesPath = this.getRuntimeArtifactPath("failed-batches.json");
|
|
8187
|
+
this.refreshRuntimeArtifactPaths();
|
|
8116
8188
|
this.logger = initializeLogger(config.debug);
|
|
8117
8189
|
}
|
|
8118
8190
|
getIndexPath() {
|
|
8119
8191
|
return this.indexPathOverride ?? resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
8120
8192
|
}
|
|
8121
8193
|
toCanonicalFilePath(filePath) {
|
|
8194
|
+
if (!path14.isAbsolute(filePath)) {
|
|
8195
|
+
return this.resolveStoredFilePath(filePath, this.projectRoot);
|
|
8196
|
+
}
|
|
8122
8197
|
if (path14.resolve(this.materializedProjectRoot) === path14.resolve(this.projectRoot) || !isPathWithinRoot2(filePath, this.materializedProjectRoot)) {
|
|
8123
8198
|
return filePath;
|
|
8124
8199
|
}
|
|
8125
8200
|
return path14.resolve(this.projectRoot, path14.relative(this.materializedProjectRoot, filePath));
|
|
8126
8201
|
}
|
|
8127
|
-
|
|
8128
|
-
|
|
8202
|
+
toStoredFilePath(filePath) {
|
|
8203
|
+
const canonicalFilePath = this.toCanonicalFilePath(filePath);
|
|
8204
|
+
if (this.config.scope !== "project" || !isPathWithinRoot2(canonicalFilePath, this.projectRoot)) {
|
|
8205
|
+
return canonicalFilePath;
|
|
8206
|
+
}
|
|
8207
|
+
return path14.relative(this.projectRoot, canonicalFilePath).split(path14.sep).join("/");
|
|
8208
|
+
}
|
|
8209
|
+
resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
|
|
8210
|
+
if (path14.isAbsolute(filePath)) {
|
|
8129
8211
|
return filePath;
|
|
8130
8212
|
}
|
|
8131
|
-
|
|
8213
|
+
const resolvedPath = path14.resolve(rootPath, ...filePath.split("/"));
|
|
8214
|
+
if (!isPathWithinRoot2(resolvedPath, rootPath)) {
|
|
8215
|
+
throw new Error(`Stored project path escapes project root: ${JSON.stringify(filePath)}`);
|
|
8216
|
+
}
|
|
8217
|
+
return resolvedPath;
|
|
8218
|
+
}
|
|
8219
|
+
getCanonicalStoredFilePath(filePath) {
|
|
8220
|
+
return this.getCanonicalPath(this.resolveStoredFilePath(filePath));
|
|
8221
|
+
}
|
|
8222
|
+
resolveFilePathRecord(record) {
|
|
8223
|
+
return {
|
|
8224
|
+
...record,
|
|
8225
|
+
filePath: this.resolveStoredFilePath(record.filePath)
|
|
8226
|
+
};
|
|
8227
|
+
}
|
|
8228
|
+
resolveCallEdgeFilePath(edge) {
|
|
8229
|
+
if (!edge.fromSymbolFilePath) return edge;
|
|
8230
|
+
return {
|
|
8231
|
+
...edge,
|
|
8232
|
+
fromSymbolFilePath: this.resolveStoredFilePath(edge.fromSymbolFilePath)
|
|
8233
|
+
};
|
|
8234
|
+
}
|
|
8235
|
+
toMaterializedFilePath(filePath) {
|
|
8236
|
+
const storedFilePath = this.toStoredFilePath(filePath);
|
|
8237
|
+
if (path14.isAbsolute(storedFilePath)) {
|
|
8238
|
+
return storedFilePath;
|
|
8239
|
+
}
|
|
8240
|
+
return this.resolveStoredFilePath(storedFilePath, this.materializedProjectRoot);
|
|
8132
8241
|
}
|
|
8133
8242
|
getPreparedBranchNamespace() {
|
|
8134
8243
|
if (!this.branchNameOverride && !this.catalogIdentityOverride) return null;
|
|
8135
8244
|
return hashContent(this.getBranchCatalogKey()).slice(0, 16);
|
|
8136
8245
|
}
|
|
8246
|
+
getRuntimeArtifactNamespace() {
|
|
8247
|
+
if (this.config.scope !== "project" || this.getBranchCatalogIdentity() === "default") {
|
|
8248
|
+
return this.getPreparedBranchNamespace();
|
|
8249
|
+
}
|
|
8250
|
+
return hashContent(this.getBranchCatalogKey()).slice(0, 16);
|
|
8251
|
+
}
|
|
8137
8252
|
getRuntimeArtifactPath(fileName) {
|
|
8138
|
-
const namespace = this.
|
|
8253
|
+
const namespace = this.getRuntimeArtifactNamespace();
|
|
8139
8254
|
if (!namespace) return path14.join(this.indexPath, fileName);
|
|
8140
8255
|
const extension = path14.extname(fileName);
|
|
8141
8256
|
const baseName = fileName.slice(0, fileName.length - extension.length);
|
|
8142
8257
|
return path14.join(this.indexPath, `${baseName}.${namespace}${extension}`);
|
|
8143
8258
|
}
|
|
8259
|
+
refreshRuntimeArtifactPaths() {
|
|
8260
|
+
this.fileHashCachePath = this.getRuntimeArtifactPath("file-hashes.json");
|
|
8261
|
+
this.failedBatchesPath = this.getRuntimeArtifactPath("failed-batches.json");
|
|
8262
|
+
}
|
|
8144
8263
|
getPreparedChunkId(chunkId) {
|
|
8145
8264
|
const namespace = this.getPreparedBranchNamespace();
|
|
8146
8265
|
return namespace ? `${chunkId}_${namespace}` : chunkId;
|
|
@@ -8166,19 +8285,8 @@ var Indexer = class _Indexer {
|
|
|
8166
8285
|
return path14.resolve(targetPath);
|
|
8167
8286
|
}
|
|
8168
8287
|
}
|
|
8169
|
-
|
|
8170
|
-
|
|
8171
|
-
if (this.host !== "opencode") {
|
|
8172
|
-
localProjectIndexPaths.push(path14.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
8173
|
-
}
|
|
8174
|
-
return localProjectIndexPaths.some((localPath) => {
|
|
8175
|
-
if (!existsSync7(localPath) || !existsSync7(this.indexPath)) {
|
|
8176
|
-
return path14.resolve(this.indexPath) === path14.resolve(localPath);
|
|
8177
|
-
}
|
|
8178
|
-
const indexStats = statSync4(this.indexPath);
|
|
8179
|
-
const localStats = statSync4(localPath);
|
|
8180
|
-
return indexStats.dev === localStats.dev && indexStats.ino === localStats.ino;
|
|
8181
|
-
});
|
|
8288
|
+
isProjectOwnedIndexPath() {
|
|
8289
|
+
return isProjectIndexPathOwnedByProject(this.projectRoot, this.indexPath, this.host);
|
|
8182
8290
|
}
|
|
8183
8291
|
resetLoadedIndexState(retireDatabase = false) {
|
|
8184
8292
|
if (this.database) {
|
|
@@ -8213,10 +8321,10 @@ var Indexer = class _Indexer {
|
|
|
8213
8321
|
this.readerArtifactRetryAfter.clear();
|
|
8214
8322
|
}
|
|
8215
8323
|
async withIndexMutationLease(operation, callback) {
|
|
8324
|
+
this.refreshBranchInfo();
|
|
8216
8325
|
const lease = acquireIndexLock(this.indexPath, operation);
|
|
8217
8326
|
this.indexPath = lease.canonicalIndexPath;
|
|
8218
|
-
this.
|
|
8219
|
-
this.failedBatchesPath = this.getRuntimeArtifactPath("failed-batches.json");
|
|
8327
|
+
this.refreshRuntimeArtifactPaths();
|
|
8220
8328
|
this.activeIndexLease = lease;
|
|
8221
8329
|
let result;
|
|
8222
8330
|
let callbackError;
|
|
@@ -8388,29 +8496,24 @@ var Indexer = class _Indexer {
|
|
|
8388
8496
|
getProjectForceReembedMetadataKey() {
|
|
8389
8497
|
return `index.forceReembed.${this.projectIdentityHash}`;
|
|
8390
8498
|
}
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
}
|
|
8395
|
-
return `index.callGraphResolutionVersion.${this.projectIdentityHash}`;
|
|
8499
|
+
getBranchMigrationMetadataKey(prefix, catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8500
|
+
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
8501
|
+
return `${prefix}.${hashContent(branchKey).slice(0, 24)}`;
|
|
8396
8502
|
}
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
if (this.config.scope !== "global") {
|
|
8400
|
-
return key;
|
|
8401
|
-
}
|
|
8402
|
-
return `${key}.${this.projectIdentityHash}`;
|
|
8503
|
+
getCallGraphResolutionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8504
|
+
return this.getBranchMigrationMetadataKey("index.callGraphResolutionVersion", catalogIdentity);
|
|
8403
8505
|
}
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
return `${key}.${this.projectIdentityHash}`;
|
|
8506
|
+
getSwiftParserVersionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8507
|
+
return this.getBranchMigrationMetadataKey("index.parser.swiftVersion", catalogIdentity);
|
|
8508
|
+
}
|
|
8509
|
+
getMetalParserVersionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8510
|
+
return this.getBranchMigrationMetadataKey("index.parser.metalVersion", catalogIdentity);
|
|
8410
8511
|
}
|
|
8411
8512
|
getSymbolExtractorVersionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8412
|
-
|
|
8413
|
-
|
|
8513
|
+
return this.getBranchMigrationMetadataKey("index.symbolExtractorVersion", catalogIdentity);
|
|
8514
|
+
}
|
|
8515
|
+
areBranchMigrationVersionsCurrent(database, catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8516
|
+
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;
|
|
8414
8517
|
}
|
|
8415
8518
|
hasProjectForceReembedPending() {
|
|
8416
8519
|
return this.config.scope === "global" && this.database?.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
@@ -8545,13 +8648,14 @@ var Indexer = class _Indexer {
|
|
|
8545
8648
|
return Array.from(keys);
|
|
8546
8649
|
}
|
|
8547
8650
|
isFileInCurrentScope(filePath, roots) {
|
|
8548
|
-
|
|
8549
|
-
const canonicalFilePath = this.getCanonicalPath(filePath);
|
|
8651
|
+
const canonicalFilePath = this.getCanonicalStoredFilePath(filePath);
|
|
8550
8652
|
return roots.some((root) => isPathWithinRoot2(canonicalFilePath, root));
|
|
8551
8653
|
}
|
|
8552
8654
|
isFileInProjectRoot(filePath) {
|
|
8553
|
-
|
|
8554
|
-
|
|
8655
|
+
return isPathWithinRoot2(
|
|
8656
|
+
this.getCanonicalStoredFilePath(filePath),
|
|
8657
|
+
this.getCanonicalPath(this.projectRoot)
|
|
8658
|
+
);
|
|
8555
8659
|
}
|
|
8556
8660
|
clearScopedFileHashCache(roots) {
|
|
8557
8661
|
for (const filePath of Array.from(this.fileHashCache.keys())) {
|
|
@@ -8952,7 +9056,10 @@ var Indexer = class _Indexer {
|
|
|
8952
9056
|
const intent = isLikelyImplementationPath2(candidate.metadata.filePath) ? "implementation" : "doc_or_test";
|
|
8953
9057
|
parts.push(`intent_hint: ${intent}`);
|
|
8954
9058
|
try {
|
|
8955
|
-
const fileContent = await fsPromises3.readFile(
|
|
9059
|
+
const fileContent = await fsPromises3.readFile(
|
|
9060
|
+
this.toMaterializedFilePath(candidate.metadata.filePath),
|
|
9061
|
+
"utf-8"
|
|
9062
|
+
);
|
|
8956
9063
|
const lines = fileContent.split("\n");
|
|
8957
9064
|
const snippetStartLine = Math.max(1, candidate.metadata.startLine);
|
|
8958
9065
|
const snippetEndLine = Math.min(lines.length, candidate.metadata.endLine);
|
|
@@ -9021,7 +9128,7 @@ var Indexer = class _Indexer {
|
|
|
9021
9128
|
if (this.config.scope === "global") {
|
|
9022
9129
|
return "Shared vector index could not be read. Restore or repair the complete fingerprinted shared vector artifacts; automatic reset is disabled for global scope.";
|
|
9023
9130
|
}
|
|
9024
|
-
if (!this.
|
|
9131
|
+
if (!this.isProjectOwnedIndexPath()) {
|
|
9025
9132
|
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.";
|
|
9026
9133
|
}
|
|
9027
9134
|
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.";
|
|
@@ -9030,7 +9137,7 @@ var Indexer = class _Indexer {
|
|
|
9030
9137
|
if (this.config.scope === "global") {
|
|
9031
9138
|
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.";
|
|
9032
9139
|
}
|
|
9033
|
-
if (!this.
|
|
9140
|
+
if (!this.isProjectOwnedIndexPath()) {
|
|
9034
9141
|
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.";
|
|
9035
9142
|
}
|
|
9036
9143
|
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.";
|
|
@@ -9039,10 +9146,10 @@ var Indexer = class _Indexer {
|
|
|
9039
9146
|
if (this.config.scope === "global") {
|
|
9040
9147
|
return "Shared index database could not be read. Restore or repair the shared SQLite database; automatic reset is disabled for global scope.";
|
|
9041
9148
|
}
|
|
9042
|
-
if (!this.
|
|
9149
|
+
if (!this.isProjectOwnedIndexPath()) {
|
|
9043
9150
|
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.";
|
|
9044
9151
|
}
|
|
9045
|
-
return "Index database could not be read. Run index_codebase
|
|
9152
|
+
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.";
|
|
9046
9153
|
}
|
|
9047
9154
|
getReaderFileFingerprint(filePath, identityOnly = false) {
|
|
9048
9155
|
try {
|
|
@@ -9222,7 +9329,7 @@ var Indexer = class _Indexer {
|
|
|
9222
9329
|
const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
|
|
9223
9330
|
if (mode === "writer") {
|
|
9224
9331
|
await fsPromises3.mkdir(this.indexPath, { recursive: true });
|
|
9225
|
-
if (recoveredOwners.length > 0 && this.config.scope === "project" && !this.
|
|
9332
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project" && !this.isProjectOwnedIndexPath()) {
|
|
9226
9333
|
throw new Error(
|
|
9227
9334
|
"Interrupted indexing recovery is unsafe while using an inherited worktree index. Run index_codebase with force=true to create a local project index boundary."
|
|
9228
9335
|
);
|
|
@@ -9323,6 +9430,7 @@ var Indexer = class _Indexer {
|
|
|
9323
9430
|
this.baseBranch = "default";
|
|
9324
9431
|
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
9325
9432
|
}
|
|
9433
|
+
this.refreshRuntimeArtifactPaths();
|
|
9326
9434
|
if (mode === "writer" && recoveredOwners.length > 0) {
|
|
9327
9435
|
await this.recoverFromInterruptedIndexingUnlocked(recoveredOwners);
|
|
9328
9436
|
}
|
|
@@ -9478,6 +9586,14 @@ var Indexer = class _Indexer {
|
|
|
9478
9586
|
}
|
|
9479
9587
|
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
9480
9588
|
}
|
|
9589
|
+
async removeProjectRuntimeStateArtifacts() {
|
|
9590
|
+
if (!existsSync7(this.indexPath)) return;
|
|
9591
|
+
const names = await fsPromises3.readdir(this.indexPath);
|
|
9592
|
+
const runtimeStatePattern = /^(?:file-hashes|failed-batches)(?:\.[a-f0-9]{16})?\.json$/;
|
|
9593
|
+
await Promise.all(
|
|
9594
|
+
names.filter((name) => runtimeStatePattern.test(name)).map((name) => fsPromises3.rm(path14.join(this.indexPath, name), { force: true }))
|
|
9595
|
+
);
|
|
9596
|
+
}
|
|
9481
9597
|
async resetLocalIndexArtifacts() {
|
|
9482
9598
|
this.store = null;
|
|
9483
9599
|
this.invertedIndex = null;
|
|
@@ -9497,11 +9613,10 @@ var Indexer = class _Indexer {
|
|
|
9497
9613
|
path14.join(this.indexPath, "vectors"),
|
|
9498
9614
|
path14.join(this.indexPath, "vectors.usearch"),
|
|
9499
9615
|
path14.join(this.indexPath, "vectors.meta.json"),
|
|
9500
|
-
path14.join(this.indexPath, "inverted-index.json")
|
|
9501
|
-
path14.join(this.indexPath, "file-hashes.json"),
|
|
9502
|
-
path14.join(this.indexPath, "failed-batches.json")
|
|
9616
|
+
path14.join(this.indexPath, "inverted-index.json")
|
|
9503
9617
|
];
|
|
9504
9618
|
await Promise.all(resetPaths.map((targetPath) => fsPromises3.rm(targetPath, { recursive: true, force: true })));
|
|
9619
|
+
await this.removeProjectRuntimeStateArtifacts();
|
|
9505
9620
|
await fsPromises3.mkdir(this.indexPath, { recursive: true });
|
|
9506
9621
|
}
|
|
9507
9622
|
async tryResetCorruptedIndex(stage, error) {
|
|
@@ -9551,12 +9666,20 @@ var Indexer = class _Indexer {
|
|
|
9551
9666
|
}
|
|
9552
9667
|
this.database.addChunksToBranchBatch(this.getBranchCatalogKey(), chunkIds);
|
|
9553
9668
|
}
|
|
9669
|
+
getExpectedPathStorageVersion() {
|
|
9670
|
+
return this.config.scope === "project" ? PROJECT_PATH_STORAGE_VERSION : GLOBAL_PATH_STORAGE_VERSION;
|
|
9671
|
+
}
|
|
9672
|
+
hasStoredIndexData() {
|
|
9673
|
+
const stats = this.database?.getStats();
|
|
9674
|
+
return (this.store?.count() ?? 0) > 0 || (stats?.chunkCount ?? 0) > 0 || (stats?.symbolCount ?? 0) > 0 || this.fileHashCache.size > 0;
|
|
9675
|
+
}
|
|
9554
9676
|
loadIndexMetadata() {
|
|
9555
9677
|
if (!this.database) return null;
|
|
9556
9678
|
const version = this.database.getMetadata("index.version");
|
|
9557
9679
|
if (!version) return null;
|
|
9558
9680
|
return {
|
|
9559
9681
|
indexVersion: version,
|
|
9682
|
+
pathStorageVersion: this.database.getMetadata("index.pathStorageVersion") ?? GLOBAL_PATH_STORAGE_VERSION,
|
|
9560
9683
|
embeddingProvider: this.database.getMetadata("index.embeddingProvider") ?? "",
|
|
9561
9684
|
embeddingModel: this.database.getMetadata("index.embeddingModel") ?? "",
|
|
9562
9685
|
embeddingDimensions: parseInt(this.database.getMetadata("index.embeddingDimensions") ?? "0", 10),
|
|
@@ -9571,6 +9694,7 @@ var Indexer = class _Indexer {
|
|
|
9571
9694
|
const existingCreatedAt = this.database.getMetadata("index.createdAt");
|
|
9572
9695
|
const completeProjectEmbeddingStrategyReset = !this.hasProjectForceReembedPending();
|
|
9573
9696
|
this.database.setMetadata("index.version", INDEX_METADATA_VERSION);
|
|
9697
|
+
this.database.setMetadata("index.pathStorageVersion", this.getExpectedPathStorageVersion());
|
|
9574
9698
|
this.database.setMetadata("index.embeddingProvider", provider.provider);
|
|
9575
9699
|
this.database.setMetadata("index.embeddingModel", provider.modelInfo.model);
|
|
9576
9700
|
this.database.setMetadata("index.embeddingDimensions", provider.modelInfo.dimensions.toString());
|
|
@@ -9593,6 +9717,16 @@ var Indexer = class _Indexer {
|
|
|
9593
9717
|
}
|
|
9594
9718
|
validateIndexCompatibility(provider) {
|
|
9595
9719
|
const storedMetadata = this.loadIndexMetadata();
|
|
9720
|
+
const storedPathStorageVersion = this.database?.getMetadata("index.pathStorageVersion") ?? GLOBAL_PATH_STORAGE_VERSION;
|
|
9721
|
+
const expectedPathStorageVersion = this.getExpectedPathStorageVersion();
|
|
9722
|
+
if (this.hasStoredIndexData() && storedPathStorageVersion !== expectedPathStorageVersion) {
|
|
9723
|
+
return {
|
|
9724
|
+
compatible: false,
|
|
9725
|
+
code: "PATH_STORAGE_MISMATCH" /* PATH_STORAGE_MISMATCH */,
|
|
9726
|
+
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.`,
|
|
9727
|
+
storedMetadata: storedMetadata ?? void 0
|
|
9728
|
+
};
|
|
9729
|
+
}
|
|
9596
9730
|
if (!storedMetadata) {
|
|
9597
9731
|
return { compatible: true };
|
|
9598
9732
|
}
|
|
@@ -9644,6 +9778,7 @@ var Indexer = class _Indexer {
|
|
|
9644
9778
|
return this.indexCompatibility;
|
|
9645
9779
|
}
|
|
9646
9780
|
async ensureInitialized() {
|
|
9781
|
+
this.refreshBranchInfo();
|
|
9647
9782
|
let initializedReader = false;
|
|
9648
9783
|
while (true) {
|
|
9649
9784
|
if (this.initializationPromise) {
|
|
@@ -9751,8 +9886,8 @@ var Indexer = class _Indexer {
|
|
|
9751
9886
|
}
|
|
9752
9887
|
const branchKey = this.getBranchCatalogKey();
|
|
9753
9888
|
const alreadyIndexed = database.getBranchChunkIds(branchKey).length > 0 && database.getBranchSymbolIds(branchKey).length > 0;
|
|
9754
|
-
const
|
|
9755
|
-
if (alreadyIndexed &&
|
|
9889
|
+
const migrationsCurrent = this.areBranchMigrationVersionsCurrent(database);
|
|
9890
|
+
if (alreadyIndexed && migrationsCurrent && this.getStoredBranchCommit(database) === normalizedCommit) {
|
|
9756
9891
|
return { prepared: false };
|
|
9757
9892
|
}
|
|
9758
9893
|
const stats = await this.indexUnlocked(onProgress, [], true);
|
|
@@ -9846,21 +9981,21 @@ var Indexer = class _Indexer {
|
|
|
9846
9981
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
9847
9982
|
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
9848
9983
|
for (const f of files) {
|
|
9849
|
-
const
|
|
9984
|
+
const storedPath = this.toStoredFilePath(f.path);
|
|
9850
9985
|
const currentHash = hashFile(f.path);
|
|
9851
|
-
currentFileHashes.set(
|
|
9852
|
-
const cachedHashMatches = this.fileHashCache.get(
|
|
9853
|
-
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(
|
|
9986
|
+
currentFileHashes.set(storedPath, currentHash);
|
|
9987
|
+
const cachedHashMatches = this.fileHashCache.get(storedPath) === currentHash;
|
|
9988
|
+
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
|
|
9854
9989
|
(chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
|
|
9855
9990
|
);
|
|
9856
|
-
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path14.extname(
|
|
9857
|
-
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path14.extname(
|
|
9991
|
+
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path14.extname(storedPath).toLowerCase() === ".swift";
|
|
9992
|
+
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path14.extname(storedPath).toLowerCase() === ".metal";
|
|
9858
9993
|
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
9859
|
-
unchangedFilePaths.add(
|
|
9994
|
+
unchangedFilePaths.add(storedPath);
|
|
9860
9995
|
this.logger.recordCacheHit();
|
|
9861
9996
|
} else {
|
|
9862
9997
|
const content = await fsPromises3.readFile(f.path, "utf-8");
|
|
9863
|
-
changedFiles.push({ path:
|
|
9998
|
+
changedFiles.push({ path: storedPath, content, hash: currentHash });
|
|
9864
9999
|
this.logger.recordCacheMiss();
|
|
9865
10000
|
}
|
|
9866
10001
|
}
|
|
@@ -9965,8 +10100,7 @@ var Indexer = class _Indexer {
|
|
|
9965
10100
|
for (const parsed of parsedFiles) {
|
|
9966
10101
|
currentFilePaths.add(parsed.path);
|
|
9967
10102
|
if (parsed.chunks.length === 0) {
|
|
9968
|
-
|
|
9969
|
-
stats.parseFailures.push(relativePath);
|
|
10103
|
+
stats.parseFailures.push(path14.isAbsolute(parsed.path) ? path14.relative(this.projectRoot, parsed.path) : parsed.path);
|
|
9970
10104
|
}
|
|
9971
10105
|
let chunksToProcess = parsed.chunks;
|
|
9972
10106
|
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
@@ -10607,6 +10741,43 @@ var Indexer = class _Indexer {
|
|
|
10607
10741
|
const union = a.size + b.size - intersection;
|
|
10608
10742
|
return intersection / union;
|
|
10609
10743
|
}
|
|
10744
|
+
getBranchPrefilterState(database, branchChunkIds) {
|
|
10745
|
+
const hasInitializedBranchCatalog = branchChunkIds !== null && database.getAllBranches().length > 0;
|
|
10746
|
+
return {
|
|
10747
|
+
hasInitializedBranchCatalog,
|
|
10748
|
+
shouldPrefilterByBranch: branchChunkIds !== null && (this.config.scope === "global" || hasInitializedBranchCatalog)
|
|
10749
|
+
};
|
|
10750
|
+
}
|
|
10751
|
+
searchCandidatesWithBranchPrefilter(initialLimit, totalCount, branchChunkIds, shouldPrefilterByBranch, search, getChunkId) {
|
|
10752
|
+
const normalizedLimit = Math.max(0, Math.floor(initialLimit));
|
|
10753
|
+
if (normalizedLimit === 0) return [];
|
|
10754
|
+
if (!shouldPrefilterByBranch || !branchChunkIds) {
|
|
10755
|
+
return search(normalizedLimit);
|
|
10756
|
+
}
|
|
10757
|
+
const targetCount = Math.min(normalizedLimit, branchChunkIds.size);
|
|
10758
|
+
if (targetCount === 0 || totalCount === 0) return [];
|
|
10759
|
+
let requestedLimit = Math.min(normalizedLimit, totalCount);
|
|
10760
|
+
while (true) {
|
|
10761
|
+
const results = search(requestedLimit);
|
|
10762
|
+
const branchResults = results.filter((candidate) => branchChunkIds.has(getChunkId(candidate)));
|
|
10763
|
+
if (branchResults.length >= targetCount || results.length < requestedLimit || requestedLimit >= totalCount) {
|
|
10764
|
+
return branchResults;
|
|
10765
|
+
}
|
|
10766
|
+
const nextLimit = Math.min(totalCount, Math.max(requestedLimit + 1, requestedLimit * 2));
|
|
10767
|
+
if (nextLimit === requestedLimit) return branchResults;
|
|
10768
|
+
requestedLimit = nextLimit;
|
|
10769
|
+
}
|
|
10770
|
+
}
|
|
10771
|
+
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch) {
|
|
10772
|
+
return this.searchCandidatesWithBranchPrefilter(
|
|
10773
|
+
initialLimit,
|
|
10774
|
+
store.count(),
|
|
10775
|
+
branchChunkIds,
|
|
10776
|
+
shouldPrefilterByBranch,
|
|
10777
|
+
(requestedLimit) => store.search(embedding, requestedLimit),
|
|
10778
|
+
(candidate) => candidate.id
|
|
10779
|
+
);
|
|
10780
|
+
}
|
|
10610
10781
|
async search(query, limit, options) {
|
|
10611
10782
|
const { store, provider, invertedIndex, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
10612
10783
|
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
@@ -10651,12 +10822,7 @@ var Indexer = class _Indexer {
|
|
|
10651
10822
|
});
|
|
10652
10823
|
}
|
|
10653
10824
|
const embeddingMs = performance2.now() - embeddingStartTime;
|
|
10654
|
-
const
|
|
10655
|
-
const semanticResults = embedding ? store.search(embedding, maxResults * 4) : [];
|
|
10656
|
-
const vectorMs = performance2.now() - vectorStartTime;
|
|
10657
|
-
const keywordStartTime = performance2.now();
|
|
10658
|
-
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
10659
|
-
const keywordMs = performance2.now() - keywordStartTime;
|
|
10825
|
+
const prefilterStartTime = performance2.now();
|
|
10660
10826
|
let branchChunkIds = null;
|
|
10661
10827
|
let branchSymbolIds = null;
|
|
10662
10828
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
@@ -10664,35 +10830,38 @@ var Indexer = class _Indexer {
|
|
|
10664
10830
|
branchChunkIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchChunkIds(branchKey)));
|
|
10665
10831
|
branchSymbolIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchSymbolIds(branchKey)));
|
|
10666
10832
|
}
|
|
10667
|
-
const
|
|
10668
|
-
const
|
|
10669
|
-
const
|
|
10670
|
-
const
|
|
10671
|
-
|
|
10672
|
-
|
|
10673
|
-
|
|
10833
|
+
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
10834
|
+
const prefilterMs = performance2.now() - prefilterStartTime;
|
|
10835
|
+
const vectorStartTime = performance2.now();
|
|
10836
|
+
const semanticCandidates = embedding ? this.searchSemanticCandidates(
|
|
10837
|
+
store,
|
|
10838
|
+
embedding,
|
|
10839
|
+
maxResults * 4,
|
|
10840
|
+
branchChunkIds,
|
|
10841
|
+
shouldPrefilterByBranch
|
|
10842
|
+
) : [];
|
|
10843
|
+
const vectorMs = performance2.now() - vectorStartTime;
|
|
10844
|
+
const keywordStartTime = performance2.now();
|
|
10845
|
+
const keywordCandidates = await this.keywordSearch(
|
|
10846
|
+
query,
|
|
10847
|
+
maxResults * 4,
|
|
10848
|
+
store,
|
|
10849
|
+
invertedIndex,
|
|
10850
|
+
branchChunkIds,
|
|
10851
|
+
shouldPrefilterByBranch
|
|
10852
|
+
);
|
|
10853
|
+
const keywordMs = performance2.now() - keywordStartTime;
|
|
10674
10854
|
const scopedSemanticCandidates = semanticCandidates.filter(
|
|
10675
|
-
(candidate) => matchesHardSearchFilters(candidate, options)
|
|
10855
|
+
(candidate) => matchesHardSearchFilters(candidate, options, this.projectRoot)
|
|
10676
10856
|
);
|
|
10677
10857
|
const scopedKeywordCandidates = keywordCandidates.filter(
|
|
10678
|
-
(candidate) => matchesHardSearchFilters(candidate, options)
|
|
10858
|
+
(candidate) => matchesHardSearchFilters(candidate, options, this.projectRoot)
|
|
10679
10859
|
);
|
|
10680
|
-
|
|
10681
|
-
if (this.config.scope !== "global" && branchChunkIds && branchChunkIds.size === 0) {
|
|
10860
|
+
if (this.config.scope !== "global" && branchChunkIds && !hasInitializedBranchCatalog) {
|
|
10682
10861
|
this.logger.search("warn", "Branch prefilter skipped because branch catalog is empty", {
|
|
10683
10862
|
branch: this.currentBranch
|
|
10684
10863
|
});
|
|
10685
10864
|
}
|
|
10686
|
-
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
10687
|
-
this.logger.search("warn", "Branch prefilter produced no semantic overlap, using unfiltered semantic candidates", {
|
|
10688
|
-
branch: this.currentBranch
|
|
10689
|
-
});
|
|
10690
|
-
}
|
|
10691
|
-
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && keywordResults.length > 0 && prefilteredKeyword.length === 0) {
|
|
10692
|
-
this.logger.search("warn", "Branch prefilter produced no keyword overlap, using unfiltered keyword candidates", {
|
|
10693
|
-
branch: this.currentBranch
|
|
10694
|
-
});
|
|
10695
|
-
}
|
|
10696
10865
|
const fusionStartTime = performance2.now();
|
|
10697
10866
|
const rankingHybridWeight = embedding === void 0 && fusionStrategy === "weighted" ? 1 : effectiveHybridWeight;
|
|
10698
10867
|
const combined = rankHybridResults(query, scopedSemanticCandidates, scopedKeywordCandidates, {
|
|
@@ -10743,12 +10912,14 @@ var Indexer = class _Indexer {
|
|
|
10743
10912
|
const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
10744
10913
|
const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
|
|
10745
10914
|
const hasCodeHints = extractCodeTermHints(query).length > 0 || identifierHints.length > 0;
|
|
10746
|
-
const baseFiltered = tiered.filter(
|
|
10915
|
+
const baseFiltered = tiered.filter(
|
|
10916
|
+
(r) => matchesSearchFilters(r, options, this.config.search.minScore, this.projectRoot)
|
|
10917
|
+
);
|
|
10747
10918
|
const implementationOnly = baseFiltered.filter(
|
|
10748
10919
|
(r) => isLikelyImplementationPath2(r.metadata.filePath) && isImplementationChunkType(r.metadata.chunkType)
|
|
10749
10920
|
);
|
|
10750
10921
|
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly : baseFiltered).slice(0, maxResults);
|
|
10751
|
-
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) : [];
|
|
10922
|
+
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) : [];
|
|
10752
10923
|
const finalResults = filtered.length > 0 ? filtered : identifierFallback;
|
|
10753
10924
|
const totalSearchMs = performance2.now() - searchStartTime;
|
|
10754
10925
|
this.logger.recordSearch(totalSearchMs, {
|
|
@@ -10773,10 +10944,11 @@ var Indexer = class _Indexer {
|
|
|
10773
10944
|
let content = "";
|
|
10774
10945
|
let contextStartLine = r.metadata.startLine;
|
|
10775
10946
|
let contextEndLine = r.metadata.endLine;
|
|
10947
|
+
const resolvedFilePath = this.resolveStoredFilePath(r.metadata.filePath);
|
|
10776
10948
|
if (!metadataOnly && this.config.search.includeContext) {
|
|
10777
10949
|
try {
|
|
10778
10950
|
const fileContent = await fsPromises3.readFile(
|
|
10779
|
-
|
|
10951
|
+
resolvedFilePath,
|
|
10780
10952
|
"utf-8"
|
|
10781
10953
|
);
|
|
10782
10954
|
const lines = fileContent.split("\n");
|
|
@@ -10789,7 +10961,7 @@ var Indexer = class _Indexer {
|
|
|
10789
10961
|
}
|
|
10790
10962
|
}
|
|
10791
10963
|
return {
|
|
10792
|
-
filePath:
|
|
10964
|
+
filePath: resolvedFilePath,
|
|
10793
10965
|
startLine: contextStartLine,
|
|
10794
10966
|
endLine: contextEndLine,
|
|
10795
10967
|
content,
|
|
@@ -10801,8 +10973,18 @@ var Indexer = class _Indexer {
|
|
|
10801
10973
|
})
|
|
10802
10974
|
);
|
|
10803
10975
|
}
|
|
10804
|
-
async keywordSearch(query, limit, store, invertedIndex) {
|
|
10805
|
-
const
|
|
10976
|
+
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false) {
|
|
10977
|
+
const normalizedLimit = Math.max(0, Math.floor(limit));
|
|
10978
|
+
if (normalizedLimit === 0) return [];
|
|
10979
|
+
const scoreEntries = this.searchCandidatesWithBranchPrefilter(
|
|
10980
|
+
normalizedLimit,
|
|
10981
|
+
invertedIndex.getDocumentCount(),
|
|
10982
|
+
branchChunkIds,
|
|
10983
|
+
shouldPrefilterByBranch,
|
|
10984
|
+
(requestedLimit) => Array.from(invertedIndex.search(query, requestedLimit)),
|
|
10985
|
+
([chunkId]) => chunkId
|
|
10986
|
+
);
|
|
10987
|
+
const scores = new Map(scoreEntries);
|
|
10806
10988
|
if (scores.size === 0) {
|
|
10807
10989
|
return [];
|
|
10808
10990
|
}
|
|
@@ -10816,7 +10998,7 @@ var Indexer = class _Indexer {
|
|
|
10816
10998
|
}
|
|
10817
10999
|
}
|
|
10818
11000
|
results.sort((a, b) => b.score - a.score);
|
|
10819
|
-
return results.slice(0,
|
|
11001
|
+
return results.slice(0, normalizedLimit);
|
|
10820
11002
|
}
|
|
10821
11003
|
async getStatus() {
|
|
10822
11004
|
const { store, configuredProviderInfo, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
@@ -10883,7 +11065,7 @@ var Indexer = class _Indexer {
|
|
|
10883
11065
|
);
|
|
10884
11066
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
10885
11067
|
for (const file of files) {
|
|
10886
|
-
currentFileHashes.set(this.
|
|
11068
|
+
currentFileHashes.set(this.toStoredFilePath(file.path), hashFile(file.path));
|
|
10887
11069
|
}
|
|
10888
11070
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
10889
11071
|
const cachedFileHashes = scopedRoots ? new Map(Array.from(this.fileHashCache).filter(([filePath]) => this.isFileInCurrentScope(filePath, scopedRoots))) : this.fileHashCache;
|
|
@@ -10895,17 +11077,7 @@ var Indexer = class _Indexer {
|
|
|
10895
11077
|
return { readable: true, current: false, reason: "files-changed" };
|
|
10896
11078
|
}
|
|
10897
11079
|
}
|
|
10898
|
-
|
|
10899
|
-
(filePath) => path14.extname(filePath).toLowerCase() === ".swift"
|
|
10900
|
-
);
|
|
10901
|
-
const hasMetalFiles = Array.from(currentFileHashes.keys()).some(
|
|
10902
|
-
(filePath) => path14.extname(filePath).toLowerCase() === ".metal"
|
|
10903
|
-
);
|
|
10904
|
-
const hasCallGraphMigrationFiles = Array.from(currentFileHashes.keys()).some((filePath) => {
|
|
10905
|
-
const extension = path14.extname(filePath).toLowerCase();
|
|
10906
|
-
return extension === ".php" || extension === ".c" || extension === ".cc" || extension === ".cpp" || extension === ".cxx";
|
|
10907
|
-
});
|
|
10908
|
-
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) {
|
|
11080
|
+
if (!this.areBranchMigrationVersionsCurrent(database)) {
|
|
10909
11081
|
return { readable: true, current: false, reason: "migration-required" };
|
|
10910
11082
|
}
|
|
10911
11083
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -10965,6 +11137,7 @@ var Indexer = class _Indexer {
|
|
|
10965
11137
|
this.deleteBranchCommitMetadata(database, clearedBranchKeys2);
|
|
10966
11138
|
this.saveFailedBatches([]);
|
|
10967
11139
|
database.deleteMetadata("index.version");
|
|
11140
|
+
database.deleteMetadata("index.pathStorageVersion");
|
|
10968
11141
|
database.deleteMetadata("index.embeddingProvider");
|
|
10969
11142
|
database.deleteMetadata("index.embeddingModel");
|
|
10970
11143
|
database.deleteMetadata("index.embeddingDimensions");
|
|
@@ -10983,7 +11156,7 @@ var Indexer = class _Indexer {
|
|
|
10983
11156
|
this.indexCompatibility = compatibility;
|
|
10984
11157
|
return;
|
|
10985
11158
|
}
|
|
10986
|
-
if (!this.
|
|
11159
|
+
if (!this.isProjectOwnedIndexPath()) {
|
|
10987
11160
|
throw new Error(
|
|
10988
11161
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
10989
11162
|
);
|
|
@@ -10994,11 +11167,11 @@ var Indexer = class _Indexer {
|
|
|
10994
11167
|
invertedIndex.clear();
|
|
10995
11168
|
this.saveInvertedIndex(invertedIndex);
|
|
10996
11169
|
this.fileHashCache.clear();
|
|
10997
|
-
this.
|
|
11170
|
+
await this.removeProjectRuntimeStateArtifacts();
|
|
10998
11171
|
database.clearAllIndexedData();
|
|
10999
11172
|
this.deleteBranchCommitMetadata(database, clearedBranchKeys);
|
|
11000
|
-
this.saveFailedBatches([]);
|
|
11001
11173
|
database.deleteMetadata("index.version");
|
|
11174
|
+
database.deleteMetadata("index.pathStorageVersion");
|
|
11002
11175
|
database.deleteMetadata("index.embeddingProvider");
|
|
11003
11176
|
database.deleteMetadata("index.embeddingModel");
|
|
11004
11177
|
database.deleteMetadata("index.embeddingDimensions");
|
|
@@ -11026,32 +11199,46 @@ var Indexer = class _Indexer {
|
|
|
11026
11199
|
existing.push(key);
|
|
11027
11200
|
filePathsToChunkKeys.set(metadata.filePath, existing);
|
|
11028
11201
|
}
|
|
11029
|
-
const
|
|
11030
|
-
const
|
|
11202
|
+
const missingStoredFilePaths = [];
|
|
11203
|
+
const missingChunkKeys = [];
|
|
11031
11204
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
11032
11205
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
11033
|
-
if (!existsSync7(filePath)) {
|
|
11206
|
+
if (!existsSync7(this.toMaterializedFilePath(filePath))) {
|
|
11034
11207
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
11035
11208
|
for (const key of chunkKeys) {
|
|
11036
|
-
|
|
11209
|
+
missingChunkKeys.push(key);
|
|
11037
11210
|
}
|
|
11038
|
-
|
|
11211
|
+
missingStoredFilePaths.push(filePath);
|
|
11039
11212
|
}
|
|
11040
11213
|
}
|
|
11214
|
+
const branchCatalogKeys = this.getBranchCatalogKeys();
|
|
11215
|
+
for (const branchKey of branchCatalogKeys) {
|
|
11216
|
+
database.deleteBranchChunksForBranch(branchKey, missingChunkKeys);
|
|
11217
|
+
}
|
|
11218
|
+
const referencedChunkKeys = new Set(database.getReferencedChunkIds(missingChunkKeys));
|
|
11219
|
+
const removedChunkKeys = missingChunkKeys.filter((key) => !referencedChunkKeys.has(key));
|
|
11041
11220
|
if (removedChunkKeys.length > 0) {
|
|
11042
11221
|
this.rebuildVectorStoreExcludingChunkIds(store, database, removedChunkKeys);
|
|
11043
11222
|
for (const key of removedChunkKeys) {
|
|
11044
11223
|
invertedIndex.removeChunk(key);
|
|
11045
11224
|
}
|
|
11225
|
+
database.deleteChunksByIds(removedChunkKeys);
|
|
11046
11226
|
}
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
database.
|
|
11054
|
-
}
|
|
11227
|
+
const missingSymbolIds = Array.from(new Set(
|
|
11228
|
+
missingStoredFilePaths.flatMap(
|
|
11229
|
+
(filePath) => database.getSymbolsByFile(filePath).map((symbol) => symbol.id)
|
|
11230
|
+
)
|
|
11231
|
+
));
|
|
11232
|
+
for (const branchKey of branchCatalogKeys) {
|
|
11233
|
+
database.deleteBranchSymbolsForBranch(branchKey, missingSymbolIds);
|
|
11234
|
+
}
|
|
11235
|
+
const referencedSymbolIds = new Set(database.getReferencedSymbolIds(missingSymbolIds));
|
|
11236
|
+
const removedSymbolIds = missingSymbolIds.filter((symbolId) => !referencedSymbolIds.has(symbolId));
|
|
11237
|
+
database.clearCallEdgeTargetsForSymbols(removedSymbolIds);
|
|
11238
|
+
const removedChunkKeySet = new Set(removedChunkKeys);
|
|
11239
|
+
const removedStoredFilePaths = missingStoredFilePaths.filter(
|
|
11240
|
+
(filePath) => (chunkKeysByRemovedFile.get(filePath) ?? []).some((key) => removedChunkKeySet.has(key))
|
|
11241
|
+
);
|
|
11055
11242
|
const removedCount = removedChunkKeys.length;
|
|
11056
11243
|
if (removedCount > 0) {
|
|
11057
11244
|
store.save();
|
|
@@ -11087,9 +11274,16 @@ var Indexer = class _Indexer {
|
|
|
11087
11274
|
removedStale: removedCount,
|
|
11088
11275
|
orphanEmbeddings: gcOrphanEmbeddings,
|
|
11089
11276
|
orphanChunks: gcOrphanChunks,
|
|
11090
|
-
removedFiles:
|
|
11277
|
+
removedFiles: removedStoredFilePaths.length
|
|
11091
11278
|
});
|
|
11092
|
-
return {
|
|
11279
|
+
return {
|
|
11280
|
+
removed: removedCount,
|
|
11281
|
+
filePaths: removedStoredFilePaths.map((filePath) => this.resolveStoredFilePath(filePath)),
|
|
11282
|
+
gcOrphanEmbeddings,
|
|
11283
|
+
gcOrphanChunks,
|
|
11284
|
+
gcOrphanSymbols,
|
|
11285
|
+
gcOrphanCallEdges
|
|
11286
|
+
};
|
|
11093
11287
|
}
|
|
11094
11288
|
async retryFailedBatches() {
|
|
11095
11289
|
return this.withIndexMutationLease("retry-failed-batches", async (recoveredOwners) => {
|
|
@@ -11284,9 +11478,18 @@ var Indexer = class _Indexer {
|
|
|
11284
11478
|
return this.baseBranch;
|
|
11285
11479
|
}
|
|
11286
11480
|
refreshBranchInfo() {
|
|
11481
|
+
const previousBranch = this.currentBranch;
|
|
11287
11482
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
11288
11483
|
this.currentBranch = this.branchNameOverride ?? getBranchOrDefault(this.materializedProjectRoot);
|
|
11289
11484
|
this.baseBranch = getBaseBranch(this.materializedProjectRoot);
|
|
11485
|
+
} else {
|
|
11486
|
+
this.currentBranch = "default";
|
|
11487
|
+
this.baseBranch = "default";
|
|
11488
|
+
}
|
|
11489
|
+
if (this.currentBranch !== previousBranch) {
|
|
11490
|
+
this.refreshRuntimeArtifactPaths();
|
|
11491
|
+
this.fileHashCache.clear();
|
|
11492
|
+
this.loadFileHashCache();
|
|
11290
11493
|
}
|
|
11291
11494
|
}
|
|
11292
11495
|
async getDatabaseStats() {
|
|
@@ -11311,6 +11514,7 @@ var Indexer = class _Indexer {
|
|
|
11311
11514
|
return [];
|
|
11312
11515
|
}
|
|
11313
11516
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
11517
|
+
const excludedStoredFile = options?.excludeFile ? this.toStoredFilePath(options.excludeFile) : void 0;
|
|
11314
11518
|
this.logger.search("debug", "Starting find similar", {
|
|
11315
11519
|
codeLength: code.length,
|
|
11316
11520
|
limit,
|
|
@@ -11320,31 +11524,29 @@ var Indexer = class _Indexer {
|
|
|
11320
11524
|
const { embedding, tokensUsed } = await provider.embedDocument(code);
|
|
11321
11525
|
const embeddingMs = performance2.now() - embeddingStartTime;
|
|
11322
11526
|
this.logger.recordEmbeddingApiCall(tokensUsed);
|
|
11323
|
-
const
|
|
11324
|
-
const semanticResults = store.search(embedding, limit * 2);
|
|
11325
|
-
const vectorMs = performance2.now() - vectorStartTime;
|
|
11527
|
+
const prefilterStartTime = performance2.now();
|
|
11326
11528
|
let branchChunkIds = null;
|
|
11327
11529
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
11328
11530
|
branchChunkIds = new Set(
|
|
11329
11531
|
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
11330
11532
|
);
|
|
11331
11533
|
}
|
|
11332
|
-
const
|
|
11333
|
-
const shouldPrefilterByBranch = branchChunkIds !== null && (this.config.scope === "global" || branchChunkIds.size > 0);
|
|
11334
|
-
const allowBranchPrefilterFallback = this.config.scope !== "global";
|
|
11335
|
-
const prefilteredSemantic = shouldPrefilterByBranch && branchChunkIds ? semanticResults.filter((r) => branchChunkIds.has(r.id)) : semanticResults;
|
|
11336
|
-
const semanticCandidates = allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0 ? semanticResults : prefilteredSemantic;
|
|
11534
|
+
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
11337
11535
|
const prefilterMs = performance2.now() - prefilterStartTime;
|
|
11338
|
-
|
|
11536
|
+
const vectorStartTime = performance2.now();
|
|
11537
|
+
const semanticCandidates = this.searchSemanticCandidates(
|
|
11538
|
+
store,
|
|
11539
|
+
embedding,
|
|
11540
|
+
limit * 2,
|
|
11541
|
+
branchChunkIds,
|
|
11542
|
+
shouldPrefilterByBranch
|
|
11543
|
+
);
|
|
11544
|
+
const vectorMs = performance2.now() - vectorStartTime;
|
|
11545
|
+
if (this.config.scope !== "global" && branchChunkIds && !hasInitializedBranchCatalog) {
|
|
11339
11546
|
this.logger.search("warn", "Branch prefilter skipped because branch catalog is empty", {
|
|
11340
11547
|
branch: this.currentBranch
|
|
11341
11548
|
});
|
|
11342
11549
|
}
|
|
11343
|
-
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
11344
|
-
this.logger.search("warn", "Branch prefilter produced no semantic overlap, using unfiltered semantic candidates", {
|
|
11345
|
-
branch: this.currentBranch
|
|
11346
|
-
});
|
|
11347
|
-
}
|
|
11348
11550
|
const rerankTopN = this.config.search.rerankTopN;
|
|
11349
11551
|
const ranked = rankSemanticOnlyResults(code, semanticCandidates, {
|
|
11350
11552
|
rerankTopN,
|
|
@@ -11353,21 +11555,10 @@ var Indexer = class _Indexer {
|
|
|
11353
11555
|
});
|
|
11354
11556
|
const filtered = ranked.filter((r) => {
|
|
11355
11557
|
if (r.score < this.config.search.minScore) return false;
|
|
11356
|
-
if (
|
|
11357
|
-
if (r.metadata.filePath ===
|
|
11358
|
-
}
|
|
11359
|
-
if (options?.fileType) {
|
|
11360
|
-
const ext = r.metadata.filePath.split(".").pop()?.toLowerCase();
|
|
11361
|
-
if (ext !== options.fileType.toLowerCase().replace(/^\./, "")) return false;
|
|
11558
|
+
if (excludedStoredFile) {
|
|
11559
|
+
if (r.metadata.filePath === excludedStoredFile) return false;
|
|
11362
11560
|
}
|
|
11363
|
-
|
|
11364
|
-
const normalizedDir = options.directory.replace(/^\/|\/$/g, "");
|
|
11365
|
-
if (!r.metadata.filePath.includes(`/${normalizedDir}/`) && !r.metadata.filePath.includes(`${normalizedDir}/`)) return false;
|
|
11366
|
-
}
|
|
11367
|
-
if (options?.chunkType) {
|
|
11368
|
-
if (r.metadata.chunkType !== options.chunkType) return false;
|
|
11369
|
-
}
|
|
11370
|
-
return true;
|
|
11561
|
+
return matchesHardSearchFilters(r, options, this.projectRoot);
|
|
11371
11562
|
}).slice(0, limit);
|
|
11372
11563
|
const totalSearchMs = performance2.now() - searchStartTime;
|
|
11373
11564
|
this.logger.recordSearch(totalSearchMs, {
|
|
@@ -11387,10 +11578,11 @@ var Indexer = class _Indexer {
|
|
|
11387
11578
|
return Promise.all(
|
|
11388
11579
|
filtered.map(async (r) => {
|
|
11389
11580
|
let content = "";
|
|
11581
|
+
const resolvedFilePath = this.resolveStoredFilePath(r.metadata.filePath);
|
|
11390
11582
|
if (this.config.search.includeContext) {
|
|
11391
11583
|
try {
|
|
11392
11584
|
const fileContent = await fsPromises3.readFile(
|
|
11393
|
-
|
|
11585
|
+
resolvedFilePath,
|
|
11394
11586
|
"utf-8"
|
|
11395
11587
|
);
|
|
11396
11588
|
const lines = fileContent.split("\n");
|
|
@@ -11400,7 +11592,7 @@ var Indexer = class _Indexer {
|
|
|
11400
11592
|
}
|
|
11401
11593
|
}
|
|
11402
11594
|
return {
|
|
11403
|
-
filePath:
|
|
11595
|
+
filePath: resolvedFilePath,
|
|
11404
11596
|
startLine: r.metadata.startLine,
|
|
11405
11597
|
endLine: r.metadata.endLine,
|
|
11406
11598
|
content,
|
|
@@ -11421,7 +11613,7 @@ var Indexer = class _Indexer {
|
|
|
11421
11613
|
for (const edge of database.getCallersWithContext(targetName, branchKey, callTypeFilter)) {
|
|
11422
11614
|
if (!seen.has(edge.id)) {
|
|
11423
11615
|
seen.add(edge.id);
|
|
11424
|
-
results.push(edge);
|
|
11616
|
+
results.push(this.resolveCallEdgeFilePath(edge));
|
|
11425
11617
|
}
|
|
11426
11618
|
}
|
|
11427
11619
|
}
|
|
@@ -11440,7 +11632,7 @@ var Indexer = class _Indexer {
|
|
|
11440
11632
|
const safelyMatchesUnresolvedSymbol = includeUnresolved && !edge.toSymbolId;
|
|
11441
11633
|
if (!matchesResolvedSymbol && !safelyMatchesUnresolvedSymbol || seen.has(edge.id)) continue;
|
|
11442
11634
|
seen.add(edge.id);
|
|
11443
|
-
results.push(edge);
|
|
11635
|
+
results.push(this.resolveCallEdgeFilePath(edge));
|
|
11444
11636
|
}
|
|
11445
11637
|
}
|
|
11446
11638
|
return results;
|
|
@@ -11454,7 +11646,7 @@ var Indexer = class _Indexer {
|
|
|
11454
11646
|
for (const edge of database.getCallees(symbolId, branchKey, callTypeFilter)) {
|
|
11455
11647
|
if (!seen.has(edge.id)) {
|
|
11456
11648
|
seen.add(edge.id);
|
|
11457
|
-
results.push(edge);
|
|
11649
|
+
results.push(this.resolveCallEdgeFilePath(edge));
|
|
11458
11650
|
}
|
|
11459
11651
|
}
|
|
11460
11652
|
}
|
|
@@ -11470,7 +11662,7 @@ var Indexer = class _Indexer {
|
|
|
11470
11662
|
shortest = path30;
|
|
11471
11663
|
}
|
|
11472
11664
|
}
|
|
11473
|
-
return shortest;
|
|
11665
|
+
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
11474
11666
|
}
|
|
11475
11667
|
async findCallPathBySymbolIds(fromSymbolId, toSymbolId, maxDepth = 10) {
|
|
11476
11668
|
const { database, readIssues } = await this.ensureInitialized();
|
|
@@ -11536,7 +11728,7 @@ var Indexer = class _Indexer {
|
|
|
11536
11728
|
shortest = path30;
|
|
11537
11729
|
}
|
|
11538
11730
|
}
|
|
11539
|
-
return shortest;
|
|
11731
|
+
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
11540
11732
|
}
|
|
11541
11733
|
async getCallGraphSymbols() {
|
|
11542
11734
|
const { database, readIssues } = await this.ensureInitialized();
|
|
@@ -11544,7 +11736,7 @@ var Indexer = class _Indexer {
|
|
|
11544
11736
|
const symbols = /* @__PURE__ */ new Map();
|
|
11545
11737
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
11546
11738
|
for (const symbol of database.getSymbolsForBranch(branchKey)) {
|
|
11547
|
-
symbols.set(symbol.id, symbol);
|
|
11739
|
+
symbols.set(symbol.id, this.resolveFilePathRecord(symbol));
|
|
11548
11740
|
}
|
|
11549
11741
|
}
|
|
11550
11742
|
return [...symbols.values()];
|
|
@@ -11553,31 +11745,32 @@ var Indexer = class _Indexer {
|
|
|
11553
11745
|
const { database, readIssues } = await this.ensureInitialized();
|
|
11554
11746
|
this.requireReadableComponents(readIssues, "database");
|
|
11555
11747
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
11556
|
-
return database.getSymbolsForBranch(resolvedBranch);
|
|
11748
|
+
return database.getSymbolsForBranch(resolvedBranch).map((symbol) => this.resolveFilePathRecord(symbol));
|
|
11557
11749
|
}
|
|
11558
11750
|
async getSymbolsForFiles(filePaths, branch) {
|
|
11559
11751
|
const { database, readIssues } = await this.ensureInitialized();
|
|
11560
11752
|
this.requireReadableComponents(readIssues, "database");
|
|
11561
11753
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
11562
|
-
|
|
11754
|
+
const storedFilePaths = filePaths.map((filePath) => this.toStoredFilePath(filePath));
|
|
11755
|
+
return database.getSymbolsForFiles(storedFilePaths, resolvedBranch).map((symbol) => this.resolveFilePathRecord(symbol));
|
|
11563
11756
|
}
|
|
11564
11757
|
async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
|
|
11565
11758
|
const { database, readIssues } = await this.ensureInitialized();
|
|
11566
11759
|
this.requireReadableComponents(readIssues, "database");
|
|
11567
11760
|
const branch = this.getBranchCatalogKey();
|
|
11568
|
-
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
|
|
11761
|
+
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth).map((entry) => this.resolveFilePathRecord(entry));
|
|
11569
11762
|
}
|
|
11570
11763
|
async detectCommunities(branch, symbolIds) {
|
|
11571
11764
|
const { database, readIssues } = await this.ensureInitialized();
|
|
11572
11765
|
this.requireReadableComponents(readIssues, "database");
|
|
11573
11766
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
11574
|
-
return database.detectCommunities(resolvedBranch, symbolIds);
|
|
11767
|
+
return database.detectCommunities(resolvedBranch, symbolIds).map((entry) => this.resolveFilePathRecord(entry));
|
|
11575
11768
|
}
|
|
11576
11769
|
async computeCentrality(branch) {
|
|
11577
11770
|
const { database, readIssues } = await this.ensureInitialized();
|
|
11578
11771
|
this.requireReadableComponents(readIssues, "database");
|
|
11579
11772
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
11580
|
-
return database.computeCentrality(resolvedBranch);
|
|
11773
|
+
return database.computeCentrality(resolvedBranch).map((entry) => this.resolveFilePathRecord(entry));
|
|
11581
11774
|
}
|
|
11582
11775
|
async getPrImpact(opts, onPreparationProgress) {
|
|
11583
11776
|
const initialState = await this.ensureInitialized();
|
|
@@ -11613,10 +11806,8 @@ var Indexer = class _Indexer {
|
|
|
11613
11806
|
const requestedRef = opts.pr !== void 0 ? expectedCommit : headRefName ?? resolvedBranch;
|
|
11614
11807
|
const storedCommit = this.getStoredBranchCommit(database, catalogIdentity);
|
|
11615
11808
|
const catalogIdentityMatches = storedCommit === expectedCommit;
|
|
11616
|
-
const
|
|
11617
|
-
|
|
11618
|
-
) === SYMBOL_EXTRACTOR_VERSION;
|
|
11619
|
-
if (branchSymbols.length === 0 || !catalogIdentityMatches || !symbolsCurrent) {
|
|
11809
|
+
const migrationsCurrent = this.areBranchMigrationVersionsCurrent(database, catalogIdentity);
|
|
11810
|
+
if (branchSymbols.length === 0 || !catalogIdentityMatches || !migrationsCurrent) {
|
|
11620
11811
|
if (!resolvedBranch || resolvedBranch === "default") {
|
|
11621
11812
|
throw new Error("Run index_codebase first to build the call graph and symbol index for this project.");
|
|
11622
11813
|
}
|
|
@@ -11672,8 +11863,9 @@ var Indexer = class _Indexer {
|
|
|
11672
11863
|
);
|
|
11673
11864
|
}
|
|
11674
11865
|
}
|
|
11675
|
-
const
|
|
11676
|
-
const
|
|
11866
|
+
const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(path14.resolve(this.projectRoot, filePath)));
|
|
11867
|
+
const storedChangedFiles = toStoredChangedFiles(changedFiles);
|
|
11868
|
+
const directSymbols = database.getSymbolsForFiles(storedChangedFiles, branchKey);
|
|
11677
11869
|
const directIds = directSymbols.map((s) => s.id);
|
|
11678
11870
|
const direction = opts.direction ?? "both";
|
|
11679
11871
|
const maxDepth = opts.maxDepth ?? 5;
|
|
@@ -11715,7 +11907,7 @@ var Indexer = class _Indexer {
|
|
|
11715
11907
|
id: c.symbolId,
|
|
11716
11908
|
name: c.symbolName,
|
|
11717
11909
|
callerCount: c.callerCount,
|
|
11718
|
-
filePath: c.filePath
|
|
11910
|
+
filePath: this.resolveStoredFilePath(c.filePath)
|
|
11719
11911
|
}));
|
|
11720
11912
|
const totalAffected = allAffectedIds.length;
|
|
11721
11913
|
let riskLevel;
|
|
@@ -11755,9 +11947,9 @@ var Indexer = class _Indexer {
|
|
|
11755
11947
|
projectRoot: this.projectRoot,
|
|
11756
11948
|
baseBranch: this.baseBranch
|
|
11757
11949
|
});
|
|
11758
|
-
const
|
|
11950
|
+
const otherStored = toStoredChangedFiles(otherChanged.files);
|
|
11759
11951
|
const prBranchKey = this.getBranchCatalogKeyFor(otherChanged.catalogIdentity);
|
|
11760
|
-
const otherSymbols = database.getSymbolsForFiles(
|
|
11952
|
+
const otherSymbols = database.getSymbolsForFiles(otherStored, prBranchKey);
|
|
11761
11953
|
const otherLabels = /* @__PURE__ */ new Set();
|
|
11762
11954
|
for (const sym of otherSymbols) {
|
|
11763
11955
|
const label = symbolToCommunity.get(structuralKey(sym.filePath, sym.name));
|
|
@@ -11788,12 +11980,12 @@ var Indexer = class _Indexer {
|
|
|
11788
11980
|
id: s.id,
|
|
11789
11981
|
name: s.name,
|
|
11790
11982
|
kind: s.kind,
|
|
11791
|
-
filePath: s.filePath
|
|
11983
|
+
filePath: this.resolveStoredFilePath(s.filePath)
|
|
11792
11984
|
})),
|
|
11793
11985
|
transitiveCallers: transitiveCallers.map((c) => ({
|
|
11794
11986
|
id: c.symbolId,
|
|
11795
11987
|
name: c.symbolName,
|
|
11796
|
-
filePath: c.filePath,
|
|
11988
|
+
filePath: this.resolveStoredFilePath(c.filePath),
|
|
11797
11989
|
depth: c.depth
|
|
11798
11990
|
})),
|
|
11799
11991
|
totalAffected,
|
|
@@ -11823,7 +12015,7 @@ var Indexer = class _Indexer {
|
|
|
11823
12015
|
const absoluteDirectoryFilter = directory ? path14.resolve(this.projectRoot, directory) : void 0;
|
|
11824
12016
|
for (const filePath of filePaths) {
|
|
11825
12017
|
if (directory) {
|
|
11826
|
-
const absoluteFilePath =
|
|
12018
|
+
const absoluteFilePath = this.resolveStoredFilePath(filePath);
|
|
11827
12019
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
11828
12020
|
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path14.sep));
|
|
11829
12021
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
@@ -11832,14 +12024,14 @@ var Indexer = class _Indexer {
|
|
|
11832
12024
|
}
|
|
11833
12025
|
for (const sym of database.getSymbolsByFile(filePath)) {
|
|
11834
12026
|
if (symbolIdSet.has(sym.id) && !seenSymbols.has(sym.id)) {
|
|
11835
|
-
seenSymbols.set(sym.id, sym);
|
|
12027
|
+
seenSymbols.set(sym.id, this.resolveFilePathRecord(sym));
|
|
11836
12028
|
}
|
|
11837
12029
|
}
|
|
11838
12030
|
}
|
|
11839
12031
|
for (const symbolId of seenSymbols.keys()) {
|
|
11840
12032
|
for (const edge of database.getCallees(symbolId, branchKey)) {
|
|
11841
12033
|
if (!seenEdges.has(edge.id)) {
|
|
11842
|
-
seenEdges.set(edge.id, edge);
|
|
12034
|
+
seenEdges.set(edge.id, this.resolveCallEdgeFilePath(edge));
|
|
11843
12035
|
}
|
|
11844
12036
|
}
|
|
11845
12037
|
}
|
|
@@ -12111,11 +12303,11 @@ function formatIndexStats(stats, verbose = false) {
|
|
|
12111
12303
|
} else if (stats.indexedChunks === 0) {
|
|
12112
12304
|
lines.push(`${stats.totalFiles} files, removed ${stats.removedChunks} stale chunks, ${stats.existingChunks} chunks remain.`);
|
|
12113
12305
|
} else {
|
|
12114
|
-
let
|
|
12306
|
+
let main = `${stats.totalFiles} files processed, ${stats.indexedChunks} new chunks embedded.`;
|
|
12115
12307
|
if (stats.existingChunks > 0) {
|
|
12116
|
-
|
|
12308
|
+
main += ` ${stats.existingChunks} unchanged chunks skipped.`;
|
|
12117
12309
|
}
|
|
12118
|
-
lines.push(
|
|
12310
|
+
lines.push(main);
|
|
12119
12311
|
if (stats.removedChunks > 0) {
|
|
12120
12312
|
lines.push(`Removed ${stats.removedChunks} stale chunks.`);
|
|
12121
12313
|
}
|
|
@@ -13289,10 +13481,10 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
|
|
|
13289
13481
|
}
|
|
13290
13482
|
coordinatorKeysByProject.set(projectKey, key);
|
|
13291
13483
|
}
|
|
13292
|
-
function startAutoIndex(projectRoot, host
|
|
13484
|
+
function startAutoIndex(projectRoot, host, source = "startup") {
|
|
13293
13485
|
return getCoordinator(projectRoot, host)?.start(source) ?? null;
|
|
13294
13486
|
}
|
|
13295
|
-
function requestBackgroundIndex(projectRoot, host
|
|
13487
|
+
function requestBackgroundIndex(projectRoot, host) {
|
|
13296
13488
|
return getCoordinator(projectRoot, host)?.request({
|
|
13297
13489
|
checkFreshness: false,
|
|
13298
13490
|
force: false,
|
|
@@ -13307,14 +13499,14 @@ function runCoordinatedIndex(projectRoot, host, force, onProgress) {
|
|
|
13307
13499
|
source: "manual"
|
|
13308
13500
|
}) ?? null;
|
|
13309
13501
|
}
|
|
13310
|
-
function getAutoIndexStatus(projectRoot, host
|
|
13502
|
+
function getAutoIndexStatus(projectRoot, host) {
|
|
13311
13503
|
return getCoordinator(projectRoot, host)?.snapshot() ?? {
|
|
13312
13504
|
enabled: false,
|
|
13313
13505
|
state: "idle",
|
|
13314
13506
|
updatedAt: now()
|
|
13315
13507
|
};
|
|
13316
13508
|
}
|
|
13317
|
-
async function waitForAutoIndexForRetrieval(projectRoot, host
|
|
13509
|
+
async function waitForAutoIndexForRetrieval(projectRoot, host) {
|
|
13318
13510
|
const coordinator = getCoordinator(projectRoot, host);
|
|
13319
13511
|
if (!coordinator) return { ready: true };
|
|
13320
13512
|
const initial = coordinator.snapshot();
|
|
@@ -13361,7 +13553,7 @@ async function waitForAutoIndexForRetrieval(projectRoot, host = "opencode") {
|
|
|
13361
13553
|
text: `Automatic indexing is ${status.state}. Retry shortly or call index_status for progress. You can also run index_codebase explicitly.`
|
|
13362
13554
|
};
|
|
13363
13555
|
}
|
|
13364
|
-
async function stopAutoIndex(projectRoot, host
|
|
13556
|
+
async function stopAutoIndex(projectRoot, host) {
|
|
13365
13557
|
await getCoordinator(projectRoot, host)?.stop();
|
|
13366
13558
|
}
|
|
13367
13559
|
async function hasReadableCurrentIndex(coordinator) {
|
|
@@ -13508,7 +13700,7 @@ function loadJsonFile(filePath) {
|
|
|
13508
13700
|
function loadConfigFile(filePath) {
|
|
13509
13701
|
return loadJsonFile(filePath);
|
|
13510
13702
|
}
|
|
13511
|
-
function loadProjectConfigLayer(projectRoot, host
|
|
13703
|
+
function loadProjectConfigLayer(projectRoot, host) {
|
|
13512
13704
|
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
13513
13705
|
const projectConfig = loadJsonFile(projectConfigPath);
|
|
13514
13706
|
if (!projectConfig) {
|
|
@@ -13525,7 +13717,7 @@ function loadProjectConfigLayer(projectRoot, host = "opencode") {
|
|
|
13525
13717
|
}
|
|
13526
13718
|
return normalizedConfig;
|
|
13527
13719
|
}
|
|
13528
|
-
function loadMergedConfig(projectRoot, host
|
|
13720
|
+
function loadMergedConfig(projectRoot, host) {
|
|
13529
13721
|
const globalConfigPath = resolveGlobalConfigPath(host);
|
|
13530
13722
|
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
13531
13723
|
let globalConfig = null;
|
|
@@ -13594,7 +13786,7 @@ function toConfigRecord(rawConfig) {
|
|
|
13594
13786
|
}
|
|
13595
13787
|
return { ...rawConfig };
|
|
13596
13788
|
}
|
|
13597
|
-
function loadRuntimeConfig(projectRoot, host
|
|
13789
|
+
function loadRuntimeConfig(projectRoot, host) {
|
|
13598
13790
|
return normalizeKnowledgeBasePaths(toConfigRecord(loadMergedConfig(projectRoot, host)), projectRoot);
|
|
13599
13791
|
}
|
|
13600
13792
|
|
|
@@ -13677,14 +13869,14 @@ function getOrCreateIndexer(projectRoot, host) {
|
|
|
13677
13869
|
configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
|
|
13678
13870
|
return indexer;
|
|
13679
13871
|
}
|
|
13680
|
-
function initializeTools(projectRoot, config, host
|
|
13872
|
+
function initializeTools(projectRoot, config, host) {
|
|
13681
13873
|
defaultProjectRoots.set(host, projectRoot);
|
|
13682
13874
|
const key = getIndexerCacheKey(projectRoot, host);
|
|
13683
13875
|
configCache.set(key, config);
|
|
13684
13876
|
indexerCache.set(key, new Indexer(projectRoot, config, host));
|
|
13685
13877
|
configureAutoIndex(projectRoot, host, config, () => getOrCreateIndexer(projectRoot, host));
|
|
13686
13878
|
}
|
|
13687
|
-
function getIndexerForProject(projectRoot, host
|
|
13879
|
+
function getIndexerForProject(projectRoot, host) {
|
|
13688
13880
|
const root = getProjectRoot(projectRoot, host);
|
|
13689
13881
|
return getOrCreateIndexer(root, host);
|
|
13690
13882
|
}
|
|
@@ -13692,7 +13884,7 @@ function recordToolEffectiveness(projectRoot, host, event) {
|
|
|
13692
13884
|
if (!isToolEffectivenessEnabled(projectRoot, host)) return;
|
|
13693
13885
|
safelyRecordToolEffectiveness(event);
|
|
13694
13886
|
}
|
|
13695
|
-
function refreshIndexerForDirectory(projectRoot, host
|
|
13887
|
+
function refreshIndexerForDirectory(projectRoot, host, config = parseConfig(loadRuntimeConfig(projectRoot, host))) {
|
|
13696
13888
|
const key = getIndexerCacheKey(projectRoot, host);
|
|
13697
13889
|
configCache.set(key, config);
|
|
13698
13890
|
indexerCache.set(key, new Indexer(projectRoot, config, host));
|
|
@@ -15226,7 +15418,7 @@ function loadRawConfig(projectRoot, configPath) {
|
|
|
15226
15418
|
fromPath
|
|
15227
15419
|
);
|
|
15228
15420
|
}
|
|
15229
|
-
const projectConfig = resolveProjectConfigPath(projectRoot);
|
|
15421
|
+
const projectConfig = resolveProjectConfigPath(projectRoot, "opencode");
|
|
15230
15422
|
if (existsSync12(projectConfig)) {
|
|
15231
15423
|
return normalizeEvalConfigKnowledgeBases(
|
|
15232
15424
|
parseJsonConfigFile(projectConfig),
|
|
@@ -15241,7 +15433,7 @@ function loadRawConfig(projectRoot, configPath) {
|
|
|
15241
15433
|
return {};
|
|
15242
15434
|
}
|
|
15243
15435
|
function getIndexRootPath(projectRoot, scope) {
|
|
15244
|
-
return scope === "global" ? getGlobalIndexPath() : resolveProjectIndexPath(projectRoot, scope);
|
|
15436
|
+
return scope === "global" ? getGlobalIndexPath("opencode") : resolveProjectIndexPath(projectRoot, scope, "opencode");
|
|
15245
15437
|
}
|
|
15246
15438
|
function getLocalProjectIndexRoot(projectRoot) {
|
|
15247
15439
|
return path21.join(projectRoot, ".opencode", "index");
|
|
@@ -15257,7 +15449,7 @@ function clearIndexRoot(projectRoot, scope) {
|
|
|
15257
15449
|
}
|
|
15258
15450
|
function ensureLocalEvalProjectConfig(projectRoot, configPath) {
|
|
15259
15451
|
const localConfigPath = getLocalProjectConfigPath(projectRoot);
|
|
15260
|
-
const resolvedConfigPath = configPath ? toAbsolute(projectRoot, configPath) : resolveProjectConfigPath(projectRoot);
|
|
15452
|
+
const resolvedConfigPath = configPath ? toAbsolute(projectRoot, configPath) : resolveProjectConfigPath(projectRoot, "opencode");
|
|
15261
15453
|
if (!configPath && existsSync12(localConfigPath)) {
|
|
15262
15454
|
return localConfigPath;
|
|
15263
15455
|
}
|
|
@@ -15692,7 +15884,7 @@ async function runEvaluation(options) {
|
|
|
15692
15884
|
if (options.reindex) {
|
|
15693
15885
|
clearIndexRoot(options.projectRoot, effectiveConfig.scope);
|
|
15694
15886
|
}
|
|
15695
|
-
const indexer = new Indexer(options.projectRoot, effectiveConfig);
|
|
15887
|
+
const indexer = new Indexer(options.projectRoot, effectiveConfig, "opencode");
|
|
15696
15888
|
try {
|
|
15697
15889
|
await indexer.index();
|
|
15698
15890
|
const perQuery = [];
|
|
@@ -15895,9 +16087,9 @@ import * as path23 from "path";
|
|
|
15895
16087
|
function printUsage() {
|
|
15896
16088
|
console.log(`
|
|
15897
16089
|
Usage:
|
|
15898
|
-
|
|
15899
|
-
|
|
15900
|
-
|
|
16090
|
+
${MCP_BINARY_CURRENT_NAME} eval run [options]
|
|
16091
|
+
${MCP_BINARY_CURRENT_NAME} eval compare --against <summary.json> [options]
|
|
16092
|
+
${MCP_BINARY_CURRENT_NAME} eval diff --current <summary.json> --against <summary.json> [options]
|
|
15901
16093
|
|
|
15902
16094
|
Options:
|
|
15903
16095
|
--project <path> Project root (default: cwd)
|
|
@@ -16186,11 +16378,20 @@ async function handleEvalCommand(args, cwd) {
|
|
|
16186
16378
|
throw new Error(`Unknown eval subcommand: ${subcommand}`);
|
|
16187
16379
|
}
|
|
16188
16380
|
|
|
16189
|
-
// src/mcp
|
|
16381
|
+
// src/adapters/mcp/server.ts
|
|
16190
16382
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
16383
|
+
|
|
16384
|
+
// src/package-metadata.ts
|
|
16191
16385
|
import { readFileSync as readFileSync11 } from "fs";
|
|
16386
|
+
function getPackageVersion() {
|
|
16387
|
+
const raw = JSON.parse(readFileSync11(new URL("../package.json", import.meta.url), "utf-8"));
|
|
16388
|
+
if (raw && typeof raw === "object" && "version" in raw && typeof raw.version === "string") {
|
|
16389
|
+
return raw.version;
|
|
16390
|
+
}
|
|
16391
|
+
return "0.0.0";
|
|
16392
|
+
}
|
|
16192
16393
|
|
|
16193
|
-
// src/mcp
|
|
16394
|
+
// src/adapters/mcp/register-prompts.ts
|
|
16194
16395
|
import { z } from "zod";
|
|
16195
16396
|
function registerMcpPrompts(server) {
|
|
16196
16397
|
server.prompt(
|
|
@@ -16280,7 +16481,7 @@ Use the implementation_lookup tool to find where this symbol is defined. This pr
|
|
|
16280
16481
|
);
|
|
16281
16482
|
}
|
|
16282
16483
|
|
|
16283
|
-
// src/mcp
|
|
16484
|
+
// src/adapters/mcp/register-tools.ts
|
|
16284
16485
|
import { z as z2 } from "zod";
|
|
16285
16486
|
|
|
16286
16487
|
// src/tools/contracts.ts
|
|
@@ -16420,13 +16621,89 @@ function formatPrImpact(result) {
|
|
|
16420
16621
|
return lines.join("\n");
|
|
16421
16622
|
}
|
|
16422
16623
|
|
|
16423
|
-
// src/
|
|
16624
|
+
// src/tools/tool-names.ts
|
|
16625
|
+
var TOOL_NAME = {
|
|
16626
|
+
CODEBASE_CONTEXT: "codebase_context",
|
|
16627
|
+
CODEBASE_SEARCH: "codebase_search",
|
|
16628
|
+
CODEBASE_PEEK: "codebase_peek",
|
|
16629
|
+
FIND_SIMILAR: "find_similar",
|
|
16630
|
+
IMPLEMENTATION_LOOKUP: "implementation_lookup",
|
|
16631
|
+
INDEX_CODEBASE: "index_codebase",
|
|
16632
|
+
INDEX_STATUS: "index_status",
|
|
16633
|
+
INDEX_HEALTH_CHECK: "index_health_check",
|
|
16634
|
+
INDEX_METRICS: "index_metrics",
|
|
16635
|
+
INDEX_LOGS: "index_logs",
|
|
16636
|
+
CALL_GRAPH: "call_graph",
|
|
16637
|
+
CALL_GRAPH_PATH: "call_graph_path",
|
|
16638
|
+
PR_IMPACT: "pr_impact",
|
|
16639
|
+
ADD_KNOWLEDGE_BASE: "add_knowledge_base",
|
|
16640
|
+
LIST_KNOWLEDGE_BASES: "list_knowledge_bases",
|
|
16641
|
+
REMOVE_KNOWLEDGE_BASE: "remove_knowledge_base",
|
|
16642
|
+
PI_KNOWLEDGE_BASE_ADD: "knowledge_base_add",
|
|
16643
|
+
PI_KNOWLEDGE_BASE_LIST: "knowledge_base_list",
|
|
16644
|
+
PI_KNOWLEDGE_BASE_REMOVE: "knowledge_base_remove",
|
|
16645
|
+
INDEX_VISUALIZE: "index_visualize"
|
|
16646
|
+
};
|
|
16647
|
+
var PORTABLE_TOOL_NAMES = [
|
|
16648
|
+
TOOL_NAME.CODEBASE_CONTEXT,
|
|
16649
|
+
TOOL_NAME.CODEBASE_SEARCH,
|
|
16650
|
+
TOOL_NAME.CODEBASE_PEEK,
|
|
16651
|
+
TOOL_NAME.INDEX_CODEBASE,
|
|
16652
|
+
TOOL_NAME.INDEX_STATUS,
|
|
16653
|
+
TOOL_NAME.INDEX_HEALTH_CHECK,
|
|
16654
|
+
TOOL_NAME.INDEX_METRICS,
|
|
16655
|
+
TOOL_NAME.INDEX_LOGS,
|
|
16656
|
+
TOOL_NAME.FIND_SIMILAR,
|
|
16657
|
+
TOOL_NAME.IMPLEMENTATION_LOOKUP,
|
|
16658
|
+
TOOL_NAME.CALL_GRAPH,
|
|
16659
|
+
TOOL_NAME.CALL_GRAPH_PATH,
|
|
16660
|
+
TOOL_NAME.PR_IMPACT
|
|
16661
|
+
];
|
|
16662
|
+
var OPENCODE_TOOL_NAMES = [
|
|
16663
|
+
TOOL_NAME.CODEBASE_CONTEXT,
|
|
16664
|
+
TOOL_NAME.CODEBASE_SEARCH,
|
|
16665
|
+
TOOL_NAME.CODEBASE_PEEK,
|
|
16666
|
+
TOOL_NAME.INDEX_CODEBASE,
|
|
16667
|
+
TOOL_NAME.INDEX_STATUS,
|
|
16668
|
+
TOOL_NAME.INDEX_HEALTH_CHECK,
|
|
16669
|
+
TOOL_NAME.INDEX_METRICS,
|
|
16670
|
+
TOOL_NAME.INDEX_LOGS,
|
|
16671
|
+
TOOL_NAME.FIND_SIMILAR,
|
|
16672
|
+
TOOL_NAME.CALL_GRAPH,
|
|
16673
|
+
TOOL_NAME.CALL_GRAPH_PATH,
|
|
16674
|
+
TOOL_NAME.IMPLEMENTATION_LOOKUP,
|
|
16675
|
+
TOOL_NAME.ADD_KNOWLEDGE_BASE,
|
|
16676
|
+
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
16677
|
+
TOOL_NAME.REMOVE_KNOWLEDGE_BASE,
|
|
16678
|
+
TOOL_NAME.PR_IMPACT,
|
|
16679
|
+
TOOL_NAME.INDEX_VISUALIZE
|
|
16680
|
+
];
|
|
16681
|
+
var PI_TOOL_NAMES = [
|
|
16682
|
+
TOOL_NAME.CODEBASE_CONTEXT,
|
|
16683
|
+
TOOL_NAME.CODEBASE_SEARCH,
|
|
16684
|
+
TOOL_NAME.CODEBASE_PEEK,
|
|
16685
|
+
TOOL_NAME.FIND_SIMILAR,
|
|
16686
|
+
TOOL_NAME.IMPLEMENTATION_LOOKUP,
|
|
16687
|
+
TOOL_NAME.INDEX_CODEBASE,
|
|
16688
|
+
TOOL_NAME.INDEX_STATUS,
|
|
16689
|
+
TOOL_NAME.INDEX_HEALTH_CHECK,
|
|
16690
|
+
TOOL_NAME.INDEX_METRICS,
|
|
16691
|
+
TOOL_NAME.INDEX_LOGS,
|
|
16692
|
+
TOOL_NAME.CALL_GRAPH,
|
|
16693
|
+
TOOL_NAME.CALL_GRAPH_PATH,
|
|
16694
|
+
TOOL_NAME.PR_IMPACT,
|
|
16695
|
+
TOOL_NAME.PI_KNOWLEDGE_BASE_LIST,
|
|
16696
|
+
TOOL_NAME.PI_KNOWLEDGE_BASE_ADD,
|
|
16697
|
+
TOOL_NAME.PI_KNOWLEDGE_BASE_REMOVE
|
|
16698
|
+
];
|
|
16699
|
+
|
|
16700
|
+
// src/adapters/mcp/register-tools.ts
|
|
16424
16701
|
function allowNullAsUndefined(schema) {
|
|
16425
16702
|
return z2.preprocess((value) => value === null ? void 0 : value, schema);
|
|
16426
16703
|
}
|
|
16427
16704
|
function registerMcpTools(server, runtime) {
|
|
16428
16705
|
server.tool(
|
|
16429
|
-
|
|
16706
|
+
TOOL_NAME.CODEBASE_CONTEXT,
|
|
16430
16707
|
"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.",
|
|
16431
16708
|
{
|
|
16432
16709
|
query: z2.string().describe("The codebase question or behavior to locate. Always provide the user's repository question here."),
|
|
@@ -16453,7 +16730,7 @@ function registerMcpTools(server, runtime) {
|
|
|
16453
16730
|
}
|
|
16454
16731
|
);
|
|
16455
16732
|
server.tool(
|
|
16456
|
-
|
|
16733
|
+
TOOL_NAME.CODEBASE_SEARCH,
|
|
16457
16734
|
"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.",
|
|
16458
16735
|
{
|
|
16459
16736
|
query: z2.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
|
|
@@ -16485,7 +16762,7 @@ ${formatSearchResults(results, "score")}`;
|
|
|
16485
16762
|
}
|
|
16486
16763
|
);
|
|
16487
16764
|
server.tool(
|
|
16488
|
-
|
|
16765
|
+
TOOL_NAME.CODEBASE_PEEK,
|
|
16489
16766
|
"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.",
|
|
16490
16767
|
{
|
|
16491
16768
|
query: z2.string().describe("Natural language description of what code you're looking for."),
|
|
@@ -16516,7 +16793,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16516
16793
|
}
|
|
16517
16794
|
);
|
|
16518
16795
|
server.tool(
|
|
16519
|
-
|
|
16796
|
+
TOOL_NAME.INDEX_CODEBASE,
|
|
16520
16797
|
"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.",
|
|
16521
16798
|
{
|
|
16522
16799
|
force: allowNullAsUndefined(z2.boolean().optional().default(false)).describe("Force reindex even if already indexed"),
|
|
@@ -16529,7 +16806,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16529
16806
|
}
|
|
16530
16807
|
);
|
|
16531
16808
|
server.tool(
|
|
16532
|
-
|
|
16809
|
+
TOOL_NAME.INDEX_STATUS,
|
|
16533
16810
|
"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.",
|
|
16534
16811
|
{},
|
|
16535
16812
|
async () => {
|
|
@@ -16538,7 +16815,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16538
16815
|
}
|
|
16539
16816
|
);
|
|
16540
16817
|
server.tool(
|
|
16541
|
-
|
|
16818
|
+
TOOL_NAME.INDEX_HEALTH_CHECK,
|
|
16542
16819
|
"Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
|
|
16543
16820
|
{},
|
|
16544
16821
|
async () => {
|
|
@@ -16547,7 +16824,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16547
16824
|
}
|
|
16548
16825
|
);
|
|
16549
16826
|
server.tool(
|
|
16550
|
-
|
|
16827
|
+
TOOL_NAME.INDEX_METRICS,
|
|
16551
16828
|
"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.",
|
|
16552
16829
|
{
|
|
16553
16830
|
reset: z2.boolean().optional().default(false).describe("Reset in-memory operational and effectiveness metrics before returning the snapshot")
|
|
@@ -16558,7 +16835,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16558
16835
|
}
|
|
16559
16836
|
);
|
|
16560
16837
|
server.tool(
|
|
16561
|
-
|
|
16838
|
+
TOOL_NAME.INDEX_LOGS,
|
|
16562
16839
|
"Get recent debug logs from the codebase indexer. Requires debug.enabled=true in config.",
|
|
16563
16840
|
{
|
|
16564
16841
|
limit: allowNullAsUndefined(z2.number().optional().default(20)).describe("Maximum number of log entries to return"),
|
|
@@ -16575,7 +16852,7 @@ ${formatCodebasePeek(results)}`;
|
|
|
16575
16852
|
}
|
|
16576
16853
|
);
|
|
16577
16854
|
server.tool(
|
|
16578
|
-
|
|
16855
|
+
TOOL_NAME.FIND_SIMILAR,
|
|
16579
16856
|
"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.",
|
|
16580
16857
|
{
|
|
16581
16858
|
code: z2.string().describe("The code snippet to find similar code for"),
|
|
@@ -16602,7 +16879,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
16602
16879
|
}
|
|
16603
16880
|
);
|
|
16604
16881
|
server.tool(
|
|
16605
|
-
|
|
16882
|
+
TOOL_NAME.IMPLEMENTATION_LOOKUP,
|
|
16606
16883
|
"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.",
|
|
16607
16884
|
{
|
|
16608
16885
|
query: z2.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
|
|
@@ -16616,7 +16893,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
16616
16893
|
}
|
|
16617
16894
|
);
|
|
16618
16895
|
server.tool(
|
|
16619
|
-
|
|
16896
|
+
TOOL_NAME.CALL_GRAPH,
|
|
16620
16897
|
"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.",
|
|
16621
16898
|
{
|
|
16622
16899
|
name: z2.string().describe("Function or method name to query"),
|
|
@@ -16635,7 +16912,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
16635
16912
|
}
|
|
16636
16913
|
);
|
|
16637
16914
|
server.tool(
|
|
16638
|
-
|
|
16915
|
+
TOOL_NAME.CALL_GRAPH_PATH,
|
|
16639
16916
|
"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.",
|
|
16640
16917
|
{
|
|
16641
16918
|
from: z2.string().describe("Source function/method name (starting point)"),
|
|
@@ -16650,7 +16927,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
16650
16927
|
}
|
|
16651
16928
|
);
|
|
16652
16929
|
server.tool(
|
|
16653
|
-
|
|
16930
|
+
TOOL_NAME.PR_IMPACT,
|
|
16654
16931
|
"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.",
|
|
16655
16932
|
{
|
|
16656
16933
|
pr: allowNullAsUndefined(z2.number().optional()).describe("Pull request number to analyze"),
|
|
@@ -16681,21 +16958,14 @@ ${formatSearchResults(results)}` }] };
|
|
|
16681
16958
|
);
|
|
16682
16959
|
}
|
|
16683
16960
|
|
|
16684
|
-
// src/mcp
|
|
16961
|
+
// src/adapters/mcp/server.ts
|
|
16685
16962
|
function getServerInstructions(host) {
|
|
16686
16963
|
const hostText = `host ${host}`;
|
|
16687
16964
|
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.`;
|
|
16688
16965
|
}
|
|
16689
|
-
function
|
|
16690
|
-
const raw = JSON.parse(readFileSync11(new URL("../package.json", import.meta.url), "utf-8"));
|
|
16691
|
-
if (raw && typeof raw === "object" && "version" in raw && typeof raw.version === "string") {
|
|
16692
|
-
return raw.version;
|
|
16693
|
-
}
|
|
16694
|
-
return "0.0.0";
|
|
16695
|
-
}
|
|
16696
|
-
function createMcpServer(projectRoot, config, host = "opencode") {
|
|
16966
|
+
function createMcpServer(projectRoot, config, host) {
|
|
16697
16967
|
const server = new McpServer({
|
|
16698
|
-
name:
|
|
16968
|
+
name: MCP_SERVER_CURRENT_NAME,
|
|
16699
16969
|
version: getPackageVersion()
|
|
16700
16970
|
}, {
|
|
16701
16971
|
instructions: getServerInstructions(host)
|
|
@@ -16730,6 +17000,9 @@ function createMcpServer(projectRoot, config, host = "opencode") {
|
|
|
16730
17000
|
return server;
|
|
16731
17001
|
}
|
|
16732
17002
|
|
|
17003
|
+
// src/watcher/file-watcher.ts
|
|
17004
|
+
import { existsSync as existsSync14 } from "fs";
|
|
17005
|
+
|
|
16733
17006
|
// node_modules/chokidar/index.js
|
|
16734
17007
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
16735
17008
|
import { stat as statcb, Stats } from "fs";
|
|
@@ -17274,10 +17547,10 @@ var foreach = (val, fn) => {
|
|
|
17274
17547
|
fn(val);
|
|
17275
17548
|
}
|
|
17276
17549
|
};
|
|
17277
|
-
var addAndConvert = (
|
|
17278
|
-
let container =
|
|
17550
|
+
var addAndConvert = (main, prop, item) => {
|
|
17551
|
+
let container = main[prop];
|
|
17279
17552
|
if (!(container instanceof Set)) {
|
|
17280
|
-
|
|
17553
|
+
main[prop] = container = /* @__PURE__ */ new Set([container]);
|
|
17281
17554
|
}
|
|
17282
17555
|
container.add(item);
|
|
17283
17556
|
};
|
|
@@ -17289,12 +17562,12 @@ var clearItem = (cont) => (key) => {
|
|
|
17289
17562
|
delete cont[key];
|
|
17290
17563
|
}
|
|
17291
17564
|
};
|
|
17292
|
-
var delFromSet = (
|
|
17293
|
-
const container =
|
|
17565
|
+
var delFromSet = (main, prop, item) => {
|
|
17566
|
+
const container = main[prop];
|
|
17294
17567
|
if (container instanceof Set) {
|
|
17295
17568
|
container.delete(item);
|
|
17296
17569
|
} else if (container === item) {
|
|
17297
|
-
delete
|
|
17570
|
+
delete main[prop];
|
|
17298
17571
|
}
|
|
17299
17572
|
};
|
|
17300
17573
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
@@ -17475,9 +17748,9 @@ var NodeFsHandler = class {
|
|
|
17475
17748
|
if (this.fsw.closed) {
|
|
17476
17749
|
return;
|
|
17477
17750
|
}
|
|
17478
|
-
const
|
|
17751
|
+
const dirname14 = sp.dirname(file);
|
|
17479
17752
|
const basename7 = sp.basename(file);
|
|
17480
|
-
const parent = this.fsw._getWatchedDir(
|
|
17753
|
+
const parent = this.fsw._getWatchedDir(dirname14);
|
|
17481
17754
|
let prevStats = stats;
|
|
17482
17755
|
if (parent.has(basename7))
|
|
17483
17756
|
return;
|
|
@@ -17504,7 +17777,7 @@ var NodeFsHandler = class {
|
|
|
17504
17777
|
prevStats = newStats2;
|
|
17505
17778
|
}
|
|
17506
17779
|
} catch (error) {
|
|
17507
|
-
this.fsw._remove(
|
|
17780
|
+
this.fsw._remove(dirname14, basename7);
|
|
17508
17781
|
}
|
|
17509
17782
|
} else if (parent.has(basename7)) {
|
|
17510
17783
|
const at = newStats.atimeMs;
|
|
@@ -18468,8 +18741,8 @@ var FileWatcher = class {
|
|
|
18468
18741
|
watcher = null;
|
|
18469
18742
|
projectRoot;
|
|
18470
18743
|
config;
|
|
18471
|
-
host;
|
|
18472
18744
|
configPath;
|
|
18745
|
+
projectConfigPaths;
|
|
18473
18746
|
pendingChanges = /* @__PURE__ */ new Map();
|
|
18474
18747
|
debounceTimer = null;
|
|
18475
18748
|
debounceMs = 1e3;
|
|
@@ -18478,11 +18751,11 @@ var FileWatcher = class {
|
|
|
18478
18751
|
resolveReady = null;
|
|
18479
18752
|
pollingFallbackAttempted = false;
|
|
18480
18753
|
pendingClose = null;
|
|
18481
|
-
constructor(projectRoot, config, host
|
|
18754
|
+
constructor(projectRoot, config, host, options = {}) {
|
|
18482
18755
|
this.projectRoot = projectRoot;
|
|
18483
18756
|
this.config = config;
|
|
18484
|
-
this.host = host;
|
|
18485
18757
|
this.configPath = options.configPath;
|
|
18758
|
+
this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
|
|
18486
18759
|
}
|
|
18487
18760
|
start(handler) {
|
|
18488
18761
|
if (this.watcher) {
|
|
@@ -18500,7 +18773,19 @@ var FileWatcher = class {
|
|
|
18500
18773
|
}
|
|
18501
18774
|
createWatcher(usePolling = false) {
|
|
18502
18775
|
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
18503
|
-
|
|
18776
|
+
let watchTargets = this.projectRoot;
|
|
18777
|
+
if (this.configPath) {
|
|
18778
|
+
watchTargets = [this.projectRoot, this.configPath];
|
|
18779
|
+
} else {
|
|
18780
|
+
const externalConfigTargets = this.projectConfigPaths.filter((projectConfigPath) => {
|
|
18781
|
+
const relativeConfigPath = path25.relative(this.projectRoot, projectConfigPath);
|
|
18782
|
+
return this.isOutsideProjectPath(relativeConfigPath);
|
|
18783
|
+
}).map((projectConfigPath) => existsSync14(projectConfigPath) ? projectConfigPath : this.getNearestExistingDirectory(path25.dirname(projectConfigPath)));
|
|
18784
|
+
const uniqueExternalConfigTargets = [...new Set(externalConfigTargets)];
|
|
18785
|
+
if (uniqueExternalConfigTargets.length > 0) {
|
|
18786
|
+
watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
|
|
18787
|
+
}
|
|
18788
|
+
}
|
|
18504
18789
|
const watcherOptions = {
|
|
18505
18790
|
ignored: (filePath) => {
|
|
18506
18791
|
const relativePath = path25.relative(this.projectRoot, filePath);
|
|
@@ -18508,6 +18793,9 @@ var FileWatcher = class {
|
|
|
18508
18793
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
18509
18794
|
return false;
|
|
18510
18795
|
}
|
|
18796
|
+
if (this.isOutsideProjectPath(relativePath)) {
|
|
18797
|
+
return true;
|
|
18798
|
+
}
|
|
18511
18799
|
if (hasFilteredPathSegment(relativePath, path25.sep)) {
|
|
18512
18800
|
return true;
|
|
18513
18801
|
}
|
|
@@ -18610,14 +18898,22 @@ var FileWatcher = class {
|
|
|
18610
18898
|
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path25.sep}`)
|
|
18611
18899
|
);
|
|
18612
18900
|
}
|
|
18613
|
-
|
|
18614
|
-
|
|
18615
|
-
|
|
18901
|
+
isOutsideProjectPath(relativePath) {
|
|
18902
|
+
return relativePath === ".." || relativePath.startsWith(`..${path25.sep}`) || path25.isAbsolute(relativePath);
|
|
18903
|
+
}
|
|
18904
|
+
getNearestExistingDirectory(directoryPath) {
|
|
18905
|
+
let candidate = directoryPath;
|
|
18906
|
+
while (!existsSync14(candidate)) {
|
|
18907
|
+
const parent = path25.dirname(candidate);
|
|
18908
|
+
if (parent === candidate) break;
|
|
18909
|
+
candidate = parent;
|
|
18616
18910
|
}
|
|
18617
|
-
return
|
|
18618
|
-
|
|
18619
|
-
|
|
18620
|
-
|
|
18911
|
+
return candidate;
|
|
18912
|
+
}
|
|
18913
|
+
getProjectConfigRelativePaths() {
|
|
18914
|
+
return this.projectConfigPaths.map(
|
|
18915
|
+
(configPath) => path25.normalize(path25.relative(this.projectRoot, configPath))
|
|
18916
|
+
);
|
|
18621
18917
|
}
|
|
18622
18918
|
scheduleFlush() {
|
|
18623
18919
|
if (this.debounceTimer) {
|
|
@@ -18744,8 +19040,9 @@ var GitHeadWatcher = class {
|
|
|
18744
19040
|
};
|
|
18745
19041
|
|
|
18746
19042
|
// src/watcher/index.ts
|
|
18747
|
-
function createWatcherWithIndexer(getIndexer, projectRoot, config, host
|
|
19043
|
+
function createWatcherWithIndexer(getIndexer, projectRoot, config, host, options = {}) {
|
|
18748
19044
|
const fileWatcher = new FileWatcher(projectRoot, config, host, options);
|
|
19045
|
+
const configPaths = getConfigPaths(projectRoot, host, options);
|
|
18749
19046
|
configureAutoIndex(projectRoot, host, parseConfig(config), getIndexer);
|
|
18750
19047
|
let stopped = false;
|
|
18751
19048
|
const requestReindex = () => {
|
|
@@ -18762,7 +19059,6 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
18762
19059
|
);
|
|
18763
19060
|
const hasDelete = changes.some((c) => c.type === "unlink");
|
|
18764
19061
|
if (hasAddOrChange || hasDelete) {
|
|
18765
|
-
const configPaths = getConfigPaths(projectRoot, host, options);
|
|
18766
19062
|
if (changes.some((change) => configPaths.includes(pathNormalize(change.path)))) {
|
|
18767
19063
|
const parsedConfig = options.configPath ? parseConfig(loadConfigFile(options.configPath)) : void 0;
|
|
18768
19064
|
const refreshedConfig = refreshIndexerForDirectory(projectRoot, host, parsedConfig);
|
|
@@ -18777,7 +19073,9 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
18777
19073
|
if (isGitRepo(projectRoot)) {
|
|
18778
19074
|
gitWatcher = new GitHeadWatcher(projectRoot);
|
|
18779
19075
|
gitWatcher.start(async (oldBranch, newBranch) => {
|
|
18780
|
-
getIndexer()
|
|
19076
|
+
const indexer = getIndexer();
|
|
19077
|
+
indexer.refreshBranchInfo();
|
|
19078
|
+
indexer.getLogger().branch("info", "Branch changed", {
|
|
18781
19079
|
oldBranch,
|
|
18782
19080
|
newBranch
|
|
18783
19081
|
});
|
|
@@ -18803,10 +19101,9 @@ function getConfigPaths(projectRoot, host, options) {
|
|
|
18803
19101
|
if (options.configPath) {
|
|
18804
19102
|
return [pathNormalize(options.configPath)];
|
|
18805
19103
|
}
|
|
18806
|
-
return
|
|
18807
|
-
|
|
18808
|
-
|
|
18809
|
-
].map((configPath) => pathNormalize(configPath));
|
|
19104
|
+
return getProjectConfigCandidatePaths(projectRoot, host).map(
|
|
19105
|
+
(configPath) => pathNormalize(configPath)
|
|
19106
|
+
);
|
|
18810
19107
|
}
|
|
18811
19108
|
|
|
18812
19109
|
// src/tools/visualize/activity.ts
|
|
@@ -19512,7 +19809,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
19512
19809
|
};
|
|
19513
19810
|
}
|
|
19514
19811
|
|
|
19515
|
-
// src/cli.ts
|
|
19812
|
+
// src/adapters/mcp/cli.ts
|
|
19516
19813
|
function parseArgs(argv) {
|
|
19517
19814
|
let project = process.cwd();
|
|
19518
19815
|
let config;
|
|
@@ -19563,8 +19860,8 @@ function parseVisualizeArgs(argv, cwd) {
|
|
|
19563
19860
|
async function handleVisualizeCommand(argv, cwd) {
|
|
19564
19861
|
try {
|
|
19565
19862
|
const args = parseVisualizeArgs(argv, cwd);
|
|
19566
|
-
const config = parseConfig(loadMergedConfig(args.project));
|
|
19567
|
-
const indexer = new Indexer(args.project, config);
|
|
19863
|
+
const config = parseConfig(loadMergedConfig(args.project, "opencode"));
|
|
19864
|
+
const indexer = new Indexer(args.project, config, "opencode");
|
|
19568
19865
|
const rawData = await indexer.getVisualizationData({
|
|
19569
19866
|
directory: args.directory
|
|
19570
19867
|
});
|
|
@@ -19595,16 +19892,16 @@ async function handleVisualizeCommand(argv, cwd) {
|
|
|
19595
19892
|
return 1;
|
|
19596
19893
|
}
|
|
19597
19894
|
}
|
|
19598
|
-
async function
|
|
19599
|
-
if (
|
|
19600
|
-
const exitCode = await handleEvalCommand(
|
|
19895
|
+
async function runMcpCli(argv) {
|
|
19896
|
+
if (argv[2] === "eval") {
|
|
19897
|
+
const exitCode = await handleEvalCommand(argv.slice(3), process.cwd());
|
|
19601
19898
|
process.exit(exitCode);
|
|
19602
19899
|
}
|
|
19603
|
-
if (
|
|
19604
|
-
const exitCode = await handleVisualizeCommand(
|
|
19900
|
+
if (argv[2] === "visualize") {
|
|
19901
|
+
const exitCode = await handleVisualizeCommand(argv.slice(3), process.cwd());
|
|
19605
19902
|
process.exit(exitCode);
|
|
19606
19903
|
}
|
|
19607
|
-
const args = parseArgs(
|
|
19904
|
+
const args = parseArgs(argv);
|
|
19608
19905
|
const rawConfig = loadCliRawConfig(args);
|
|
19609
19906
|
const config = parseConfig(rawConfig);
|
|
19610
19907
|
const server = createMcpServer(args.project, config, args.host);
|
|
@@ -19655,8 +19952,10 @@ function handleMainError(error) {
|
|
|
19655
19952
|
console.error("Fatal: failed to start MCP server");
|
|
19656
19953
|
process.exit(1);
|
|
19657
19954
|
}
|
|
19955
|
+
|
|
19956
|
+
// src/cli.ts
|
|
19658
19957
|
if (isCliEntrypoint(import.meta.url, process.argv[1])) {
|
|
19659
|
-
|
|
19958
|
+
runMcpCli(process.argv).catch(handleMainError);
|
|
19660
19959
|
}
|
|
19661
19960
|
export {
|
|
19662
19961
|
isCliEntrypoint,
|