opencode-codebase-index 0.17.1 → 0.18.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 +45 -8
- package/THIRD_PARTY_LICENSES.md +64 -0
- package/dist/cli.cjs +576 -346
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +582 -352
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +342 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +356 -128
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +735 -505
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +697 -467
- 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 +2 -1
package/dist/cli.cjs
CHANGED
|
@@ -685,7 +685,8 @@ var DEFAULT_INCLUDE = [
|
|
|
685
685
|
"**/*.{sh,bash,zsh}",
|
|
686
686
|
"**/*.{txt,html,htm}",
|
|
687
687
|
"**/*.zig",
|
|
688
|
-
"**/*.gd"
|
|
688
|
+
"**/*.gd",
|
|
689
|
+
"**/*.metal"
|
|
689
690
|
];
|
|
690
691
|
var DEFAULT_EXCLUDE = [
|
|
691
692
|
"**/node_modules/**",
|
|
@@ -876,6 +877,9 @@ function isValidProvider(value) {
|
|
|
876
877
|
return typeof value === "string" && Object.keys(EMBEDDING_MODELS).includes(value);
|
|
877
878
|
}
|
|
878
879
|
function isValidModel(value, provider) {
|
|
880
|
+
if (typeof value === "string" && provider === "ollama" && value.trim().length > 0) {
|
|
881
|
+
return true;
|
|
882
|
+
}
|
|
879
883
|
return typeof value === "string" && Object.keys(EMBEDDING_MODELS[provider]).includes(value);
|
|
880
884
|
}
|
|
881
885
|
function isValidScope(value) {
|
|
@@ -2458,6 +2462,9 @@ function loadOpenCodeAuth() {
|
|
|
2458
2462
|
return {};
|
|
2459
2463
|
}
|
|
2460
2464
|
async function detectEmbeddingProvider(preferredProvider, model) {
|
|
2465
|
+
if (preferredProvider === "ollama") {
|
|
2466
|
+
return detectOllamaProvider(model);
|
|
2467
|
+
}
|
|
2461
2468
|
const credentials = await getProviderCredentials(preferredProvider);
|
|
2462
2469
|
if (credentials) {
|
|
2463
2470
|
if (!model) {
|
|
@@ -2473,10 +2480,16 @@ async function detectEmbeddingProvider(preferredProvider, model) {
|
|
|
2473
2480
|
);
|
|
2474
2481
|
}
|
|
2475
2482
|
const providerModels = EMBEDDING_MODELS[preferredProvider];
|
|
2483
|
+
const modelInfo = Object.values(providerModels).find((candidate) => candidate.model === model);
|
|
2484
|
+
if (!modelInfo) {
|
|
2485
|
+
throw new Error(
|
|
2486
|
+
`Model '${model}' is not supported by provider '${preferredProvider}'`
|
|
2487
|
+
);
|
|
2488
|
+
}
|
|
2476
2489
|
return {
|
|
2477
2490
|
provider: preferredProvider,
|
|
2478
2491
|
credentials,
|
|
2479
|
-
modelInfo
|
|
2492
|
+
modelInfo
|
|
2480
2493
|
};
|
|
2481
2494
|
}
|
|
2482
2495
|
throw new Error(
|
|
@@ -2485,6 +2498,13 @@ async function detectEmbeddingProvider(preferredProvider, model) {
|
|
|
2485
2498
|
}
|
|
2486
2499
|
async function tryDetectProvider() {
|
|
2487
2500
|
for (const provider of autoDetectProviders) {
|
|
2501
|
+
if (provider === "ollama") {
|
|
2502
|
+
const ollamaProvider = await tryDetectOllamaProvider();
|
|
2503
|
+
if (ollamaProvider) {
|
|
2504
|
+
return ollamaProvider;
|
|
2505
|
+
}
|
|
2506
|
+
continue;
|
|
2507
|
+
}
|
|
2488
2508
|
const credentials = await getProviderCredentials(provider);
|
|
2489
2509
|
if (credentials) {
|
|
2490
2510
|
return {
|
|
@@ -2552,32 +2572,105 @@ function getGoogleCredentials() {
|
|
|
2552
2572
|
}
|
|
2553
2573
|
return null;
|
|
2554
2574
|
}
|
|
2555
|
-
async function
|
|
2556
|
-
const
|
|
2575
|
+
async function fetchOllama(url, init) {
|
|
2576
|
+
const controller = new AbortController();
|
|
2577
|
+
const timeoutId = setTimeout(() => controller.abort(), 2e3);
|
|
2557
2578
|
try {
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
const response = await fetch(`${baseUrl}/api/tags`, {
|
|
2561
|
-
signal: controller.signal
|
|
2562
|
-
});
|
|
2579
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
2580
|
+
} finally {
|
|
2563
2581
|
clearTimeout(timeoutId);
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
async function getOllamaCredentials() {
|
|
2585
|
+
const baseUrl = (process.env.OLLAMA_HOST || "http://localhost:11434").replace(/\/+$/, "");
|
|
2586
|
+
try {
|
|
2587
|
+
const response = await fetchOllama(`${baseUrl}/api/tags`);
|
|
2564
2588
|
if (response.ok) {
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
if (hasEmbeddingModel) {
|
|
2570
|
-
return {
|
|
2571
|
-
provider: "ollama",
|
|
2572
|
-
baseUrl
|
|
2573
|
-
};
|
|
2574
|
-
}
|
|
2589
|
+
return {
|
|
2590
|
+
provider: "ollama",
|
|
2591
|
+
baseUrl
|
|
2592
|
+
};
|
|
2575
2593
|
}
|
|
2576
2594
|
} catch {
|
|
2577
2595
|
return null;
|
|
2578
2596
|
}
|
|
2579
2597
|
return null;
|
|
2580
2598
|
}
|
|
2599
|
+
function findCatalogOllamaModel(model) {
|
|
2600
|
+
const stableName = model.endsWith(":latest") ? model.slice(0, -":latest".length) : model;
|
|
2601
|
+
return Object.values(EMBEDDING_MODELS.ollama).find((candidate) => candidate.model === stableName) ?? null;
|
|
2602
|
+
}
|
|
2603
|
+
function getPositiveIntegerMetadata(modelInfo, suffix) {
|
|
2604
|
+
const values = Object.entries(modelInfo).filter(([key]) => key.endsWith(suffix)).map(([, value]) => value).filter((value) => typeof value === "number" && Number.isInteger(value) && value > 0);
|
|
2605
|
+
return values.length === 1 ? values[0] : null;
|
|
2606
|
+
}
|
|
2607
|
+
async function fetchOllamaModelInfo(credentials, model) {
|
|
2608
|
+
const response = await fetchOllama(`${credentials.baseUrl}/api/show`, {
|
|
2609
|
+
method: "POST",
|
|
2610
|
+
headers: { "Content-Type": "application/json" },
|
|
2611
|
+
body: JSON.stringify({ model })
|
|
2612
|
+
});
|
|
2613
|
+
if (!response.ok) {
|
|
2614
|
+
return null;
|
|
2615
|
+
}
|
|
2616
|
+
const data = await response.json();
|
|
2617
|
+
if (!data.capabilities?.includes("embedding") || !data.model_info) {
|
|
2618
|
+
return null;
|
|
2619
|
+
}
|
|
2620
|
+
const dimensions = getPositiveIntegerMetadata(data.model_info, ".embedding_length");
|
|
2621
|
+
const maxTokens = getPositiveIntegerMetadata(data.model_info, ".context_length");
|
|
2622
|
+
if (!dimensions || !maxTokens) {
|
|
2623
|
+
return null;
|
|
2624
|
+
}
|
|
2625
|
+
return {
|
|
2626
|
+
provider: "ollama",
|
|
2627
|
+
model,
|
|
2628
|
+
dimensions,
|
|
2629
|
+
maxTokens,
|
|
2630
|
+
costPer1MTokens: 0
|
|
2631
|
+
};
|
|
2632
|
+
}
|
|
2633
|
+
async function listOllamaModels(credentials) {
|
|
2634
|
+
const response = await fetchOllama(`${credentials.baseUrl}/api/tags`);
|
|
2635
|
+
if (!response.ok) {
|
|
2636
|
+
return [];
|
|
2637
|
+
}
|
|
2638
|
+
const data = await response.json();
|
|
2639
|
+
return [...new Set((data.models ?? []).map((entry) => entry.name ?? entry.model).filter((name) => typeof name === "string" && name.trim().length > 0))];
|
|
2640
|
+
}
|
|
2641
|
+
async function detectOllamaProvider(model) {
|
|
2642
|
+
const credentials = await getOllamaCredentials();
|
|
2643
|
+
if (!credentials) {
|
|
2644
|
+
throw new Error("Preferred provider 'ollama' is not configured or authenticated");
|
|
2645
|
+
}
|
|
2646
|
+
const requestedModel = model?.trim();
|
|
2647
|
+
if (requestedModel) {
|
|
2648
|
+
const catalogModel = findCatalogOllamaModel(requestedModel);
|
|
2649
|
+
if (catalogModel) {
|
|
2650
|
+
return { provider: "ollama", credentials, modelInfo: catalogModel };
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
const candidates = requestedModel ? [requestedModel] : await listOllamaModels(credentials);
|
|
2654
|
+
for (const candidate of candidates) {
|
|
2655
|
+
const modelInfo = await fetchOllamaModelInfo(credentials, candidate);
|
|
2656
|
+
if (modelInfo) {
|
|
2657
|
+
return {
|
|
2658
|
+
provider: "ollama",
|
|
2659
|
+
credentials,
|
|
2660
|
+
modelInfo: findCatalogOllamaModel(candidate) ?? modelInfo
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
const detail = model ? `Model '${model}' is not installed or is not embedding-capable` : "No installed embedding-capable Ollama model was found";
|
|
2665
|
+
throw new Error(detail);
|
|
2666
|
+
}
|
|
2667
|
+
async function tryDetectOllamaProvider() {
|
|
2668
|
+
try {
|
|
2669
|
+
return await detectOllamaProvider();
|
|
2670
|
+
} catch {
|
|
2671
|
+
return null;
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2581
2674
|
function getProviderDisplayName(provider) {
|
|
2582
2675
|
switch (provider) {
|
|
2583
2676
|
case "github-copilot":
|
|
@@ -2917,6 +3010,7 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
2917
3010
|
// src/embeddings/providers/ollama.ts
|
|
2918
3011
|
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
|
|
2919
3012
|
static MIN_TRUNCATION_CHARS = 512;
|
|
3013
|
+
static REQUEST_TIMEOUT_MS = 12e4;
|
|
2920
3014
|
constructor(credentials, modelInfo) {
|
|
2921
3015
|
super(credentials, modelInfo);
|
|
2922
3016
|
}
|
|
@@ -2991,22 +3085,45 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
2991
3085
|
}
|
|
2992
3086
|
}
|
|
2993
3087
|
async embedSingle(text) {
|
|
2994
|
-
const
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3088
|
+
const controller = new AbortController();
|
|
3089
|
+
const timeout = setTimeout(
|
|
3090
|
+
() => controller.abort(),
|
|
3091
|
+
_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS
|
|
3092
|
+
);
|
|
3093
|
+
let response;
|
|
3094
|
+
try {
|
|
3095
|
+
response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
3096
|
+
method: "POST",
|
|
3097
|
+
headers: {
|
|
3098
|
+
"Content-Type": "application/json"
|
|
3099
|
+
},
|
|
3100
|
+
body: JSON.stringify({
|
|
3101
|
+
model: this.modelInfo.model,
|
|
3102
|
+
prompt: text,
|
|
3103
|
+
truncate: false
|
|
3104
|
+
}),
|
|
3105
|
+
signal: controller.signal
|
|
3106
|
+
});
|
|
3107
|
+
} catch (error) {
|
|
3108
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3109
|
+
throw new Error(
|
|
3110
|
+
`Ollama embedding request timed out after ${_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS}ms`
|
|
3111
|
+
);
|
|
3112
|
+
}
|
|
3113
|
+
throw error;
|
|
3114
|
+
} finally {
|
|
3115
|
+
clearTimeout(timeout);
|
|
3116
|
+
}
|
|
3005
3117
|
if (!response.ok) {
|
|
3006
3118
|
const error = (await response.text()).slice(0, 500);
|
|
3007
3119
|
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
3008
3120
|
}
|
|
3009
3121
|
const data = await response.json();
|
|
3122
|
+
if (!Array.isArray(data.embedding) || data.embedding.length !== this.modelInfo.dimensions || data.embedding.some((value) => typeof value !== "number" || !Number.isFinite(value))) {
|
|
3123
|
+
throw new Error(
|
|
3124
|
+
`Ollama returned an invalid embedding; expected ${this.modelInfo.dimensions} finite dimensions`
|
|
3125
|
+
);
|
|
3126
|
+
}
|
|
3010
3127
|
return {
|
|
3011
3128
|
embedding: data.embedding,
|
|
3012
3129
|
tokensUsed: this.estimateTokens(text)
|
|
@@ -3918,7 +4035,9 @@ function mapChunk(c) {
|
|
|
3918
4035
|
return {
|
|
3919
4036
|
content: c.content,
|
|
3920
4037
|
startLine: c.startLine ?? c.start_line,
|
|
4038
|
+
startCol: c.startCol ?? c.start_col,
|
|
3921
4039
|
endLine: c.endLine ?? c.end_line,
|
|
4040
|
+
endCol: c.endCol ?? c.end_col,
|
|
3922
4041
|
chunkType: c.chunkType ?? c.chunk_type,
|
|
3923
4042
|
name: c.name ?? void 0,
|
|
3924
4043
|
language: c.language
|
|
@@ -4039,6 +4158,7 @@ function getEmbeddingHeaderParts(chunk, filePath) {
|
|
|
4039
4158
|
javascript: "JavaScript",
|
|
4040
4159
|
python: "Python",
|
|
4041
4160
|
rust: "Rust",
|
|
4161
|
+
swift: "Swift",
|
|
4042
4162
|
go: "Go",
|
|
4043
4163
|
java: "Java"
|
|
4044
4164
|
};
|
|
@@ -4047,7 +4167,16 @@ function getEmbeddingHeaderParts(chunk, filePath) {
|
|
|
4047
4167
|
function: "function",
|
|
4048
4168
|
arrow_function: "arrow function",
|
|
4049
4169
|
method_definition: "method",
|
|
4170
|
+
method_declaration: "method",
|
|
4171
|
+
protocol_function_declaration: "protocol requirement",
|
|
4172
|
+
init_declaration: "initializer",
|
|
4173
|
+
deinit_declaration: "deinitializer",
|
|
4174
|
+
subscript_declaration: "subscript",
|
|
4050
4175
|
class_declaration: "class",
|
|
4176
|
+
actor_declaration: "actor",
|
|
4177
|
+
extension_declaration: "extension",
|
|
4178
|
+
protocol_declaration: "protocol",
|
|
4179
|
+
struct_declaration: "struct",
|
|
4051
4180
|
interface_declaration: "interface",
|
|
4052
4181
|
type_alias_declaration: "type alias",
|
|
4053
4182
|
enum_declaration: "enum",
|
|
@@ -4741,16 +4870,6 @@ function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
|
4741
4870
|
const fallbackPath = path8.join(mainRepoRoot, relativePath);
|
|
4742
4871
|
return (0, import_fs6.existsSync)(fallbackPath) ? fallbackPath : null;
|
|
4743
4872
|
}
|
|
4744
|
-
function resolveWorktreeFallbackProjectIndexPath(projectRoot, host) {
|
|
4745
|
-
const inheritedHostPath = resolveWorktreeFallbackPath(projectRoot, getProjectIndexRelativePath(host));
|
|
4746
|
-
if (inheritedHostPath) {
|
|
4747
|
-
return inheritedHostPath;
|
|
4748
|
-
}
|
|
4749
|
-
if (host !== "opencode") {
|
|
4750
|
-
return resolveWorktreeFallbackPath(projectRoot, OPENCODE_PROJECT_INDEX_RELATIVE_PATH);
|
|
4751
|
-
}
|
|
4752
|
-
return null;
|
|
4753
|
-
}
|
|
4754
4873
|
function getHostProjectIndexRelativePath(host) {
|
|
4755
4874
|
return getProjectIndexRelativePath(host);
|
|
4756
4875
|
}
|
|
@@ -4852,6 +4971,9 @@ function resolveProjectIndexPath(projectRoot, scope, host = "opencode") {
|
|
|
4852
4971
|
if (hasHostProjectConfig(projectRoot, host)) {
|
|
4853
4972
|
return localIndexPath;
|
|
4854
4973
|
}
|
|
4974
|
+
if (resolveWorktreeMainRepoRoot(projectRoot)) {
|
|
4975
|
+
return localIndexPath;
|
|
4976
|
+
}
|
|
4855
4977
|
const hostFallback = resolveWorktreeFallbackPath(projectRoot, getProjectIndexRelativePath(host));
|
|
4856
4978
|
if (hostFallback) {
|
|
4857
4979
|
return hostFallback;
|
|
@@ -5376,40 +5498,6 @@ function releaseIndexLock(lease) {
|
|
|
5376
5498
|
}
|
|
5377
5499
|
return true;
|
|
5378
5500
|
}
|
|
5379
|
-
async function withIndexLock(indexPath, operation, callback, options = {}) {
|
|
5380
|
-
const lease = acquireIndexLock(indexPath, operation);
|
|
5381
|
-
let result;
|
|
5382
|
-
let callbackError;
|
|
5383
|
-
let callbackFailed = false;
|
|
5384
|
-
try {
|
|
5385
|
-
result = await callback(lease);
|
|
5386
|
-
} catch (error) {
|
|
5387
|
-
callbackFailed = true;
|
|
5388
|
-
callbackError = error;
|
|
5389
|
-
}
|
|
5390
|
-
if (!callbackFailed && options.completeRecoveries !== false) {
|
|
5391
|
-
try {
|
|
5392
|
-
completeLeaseRecovery(lease);
|
|
5393
|
-
} catch (error) {
|
|
5394
|
-
callbackFailed = true;
|
|
5395
|
-
callbackError = error;
|
|
5396
|
-
}
|
|
5397
|
-
}
|
|
5398
|
-
let releaseError;
|
|
5399
|
-
try {
|
|
5400
|
-
if (!releaseIndexLock(lease)) {
|
|
5401
|
-
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
5402
|
-
}
|
|
5403
|
-
} catch (error) {
|
|
5404
|
-
releaseError = error;
|
|
5405
|
-
}
|
|
5406
|
-
if (releaseError !== void 0) {
|
|
5407
|
-
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
5408
|
-
throw releaseError;
|
|
5409
|
-
}
|
|
5410
|
-
if (callbackFailed) throw callbackError;
|
|
5411
|
-
return result;
|
|
5412
|
-
}
|
|
5413
5501
|
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
5414
5502
|
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
5415
5503
|
temporaryCounter += 1;
|
|
@@ -5444,8 +5532,18 @@ function completeLeaseRecovery(lease) {
|
|
|
5444
5532
|
}
|
|
5445
5533
|
|
|
5446
5534
|
// src/indexer/index.ts
|
|
5447
|
-
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab", "bash"]);
|
|
5448
|
-
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
5535
|
+
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "swift", "php", "apex", "zig", "gdscript", "matlab", "bash", "c", "cpp", "metal"]);
|
|
5536
|
+
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex", "php"]);
|
|
5537
|
+
var CALL_GRAPH_RESOLUTION_VERSION = "4";
|
|
5538
|
+
var PHP_FUNCTION_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
5539
|
+
"function_declaration",
|
|
5540
|
+
"function",
|
|
5541
|
+
"function_definition"
|
|
5542
|
+
]);
|
|
5543
|
+
var PHP_CLASS_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
5544
|
+
"class_declaration",
|
|
5545
|
+
"class_definition"
|
|
5546
|
+
]);
|
|
5449
5547
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
5450
5548
|
"function_declaration",
|
|
5451
5549
|
"function",
|
|
@@ -5458,6 +5556,9 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
5458
5556
|
"enum_declaration",
|
|
5459
5557
|
"function_definition",
|
|
5460
5558
|
"class_definition",
|
|
5559
|
+
"class_specifier",
|
|
5560
|
+
"struct_specifier",
|
|
5561
|
+
"namespace_definition",
|
|
5461
5562
|
"decorated_definition",
|
|
5462
5563
|
"method_declaration",
|
|
5463
5564
|
"type_declaration",
|
|
@@ -5473,6 +5574,14 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
5473
5574
|
"test_declaration",
|
|
5474
5575
|
"struct_declaration",
|
|
5475
5576
|
"union_declaration",
|
|
5577
|
+
// Synthetic Swift declarations or declarations specific to tree-sitter-swift.
|
|
5578
|
+
"actor_declaration",
|
|
5579
|
+
"extension_declaration",
|
|
5580
|
+
"protocol_declaration",
|
|
5581
|
+
"protocol_function_declaration",
|
|
5582
|
+
"init_declaration",
|
|
5583
|
+
"deinit_declaration",
|
|
5584
|
+
"subscript_declaration",
|
|
5476
5585
|
// GDScript declarations whose names participate in the call graph.
|
|
5477
5586
|
// `function_definition` and `class_definition` are already in the set
|
|
5478
5587
|
// above (shared with Python/C/Bash and Python, respectively).
|
|
@@ -5482,6 +5591,53 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
5482
5591
|
"const_statement",
|
|
5483
5592
|
"class_name_statement"
|
|
5484
5593
|
]);
|
|
5594
|
+
var C_FAMILY_TYPE_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set(["class_specifier", "struct_specifier"]);
|
|
5595
|
+
function isCompatibleCFamilyCallTarget(language, callType, symbolKind) {
|
|
5596
|
+
if (language !== "c" && language !== "cpp") return true;
|
|
5597
|
+
if (symbolKind === "namespace_definition") return callType === "Import";
|
|
5598
|
+
const isTypeSymbol = C_FAMILY_TYPE_SYMBOL_CHUNK_TYPES.has(symbolKind);
|
|
5599
|
+
if (callType === "Constructor" || callType === "Inherits" || callType === "Implements") {
|
|
5600
|
+
return isTypeSymbol;
|
|
5601
|
+
}
|
|
5602
|
+
return !isTypeSymbol;
|
|
5603
|
+
}
|
|
5604
|
+
var EXECUTABLE_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
5605
|
+
"function_declaration",
|
|
5606
|
+
"function",
|
|
5607
|
+
"arrow_function",
|
|
5608
|
+
"method_definition",
|
|
5609
|
+
"function_definition",
|
|
5610
|
+
"method_declaration",
|
|
5611
|
+
"function_item",
|
|
5612
|
+
"protocol_function_declaration",
|
|
5613
|
+
"init_declaration",
|
|
5614
|
+
"deinit_declaration",
|
|
5615
|
+
"subscript_declaration",
|
|
5616
|
+
"constructor_definition",
|
|
5617
|
+
"trigger_declaration",
|
|
5618
|
+
"test_declaration"
|
|
5619
|
+
]);
|
|
5620
|
+
function findEnclosingSymbol(symbols, line, column) {
|
|
5621
|
+
let best;
|
|
5622
|
+
for (const symbol of symbols) {
|
|
5623
|
+
if (line < symbol.startLine || line > symbol.endLine) continue;
|
|
5624
|
+
if (column !== void 0 && (line === symbol.startLine && column < symbol.startCol || line === symbol.endLine && column >= symbol.endCol)) {
|
|
5625
|
+
continue;
|
|
5626
|
+
}
|
|
5627
|
+
if (!best) {
|
|
5628
|
+
best = symbol;
|
|
5629
|
+
continue;
|
|
5630
|
+
}
|
|
5631
|
+
const span = symbol.endLine - symbol.startLine;
|
|
5632
|
+
const bestSpan = best.endLine - best.startLine;
|
|
5633
|
+
const isNarrowerPositionRange = column !== void 0 && span === bestSpan && symbol.startLine === best.startLine && symbol.endLine === best.endLine && symbol.startCol >= best.startCol && symbol.endCol <= best.endCol && (symbol.startCol > best.startCol || symbol.endCol < best.endCol);
|
|
5634
|
+
const isMoreSpecificTie = span === bestSpan && symbol.startLine === best.startLine && EXECUTABLE_SYMBOL_CHUNK_TYPES.has(symbol.kind) && !EXECUTABLE_SYMBOL_CHUNK_TYPES.has(best.kind);
|
|
5635
|
+
if (span < bestSpan || span === bestSpan && symbol.startLine > best.startLine || isNarrowerPositionRange || isMoreSpecificTie) {
|
|
5636
|
+
best = symbol;
|
|
5637
|
+
}
|
|
5638
|
+
}
|
|
5639
|
+
return best;
|
|
5640
|
+
}
|
|
5485
5641
|
function float32ArrayToBuffer(arr) {
|
|
5486
5642
|
const float32 = new Float32Array(arr);
|
|
5487
5643
|
return Buffer.from(float32.buffer);
|
|
@@ -5566,6 +5722,8 @@ function hasBlameMetadata(metadata) {
|
|
|
5566
5722
|
}
|
|
5567
5723
|
var INDEX_METADATA_VERSION = "1";
|
|
5568
5724
|
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
5725
|
+
var SWIFT_PARSER_VERSION = "1";
|
|
5726
|
+
var METAL_PARSER_VERSION = "1";
|
|
5569
5727
|
var RANKING_TOKEN_CACHE_LIMIT = 4096;
|
|
5570
5728
|
var RANK_HYBRID_CACHE_LIMIT = 256;
|
|
5571
5729
|
function createPendingChunkStorageText(texts) {
|
|
@@ -5888,15 +6046,25 @@ function chunkTypeBoost(chunkType) {
|
|
|
5888
6046
|
case "function_declaration":
|
|
5889
6047
|
case "method":
|
|
5890
6048
|
case "method_definition":
|
|
6049
|
+
case "method_declaration":
|
|
6050
|
+
case "protocol_function_declaration":
|
|
6051
|
+
case "init_declaration":
|
|
6052
|
+
case "deinit_declaration":
|
|
6053
|
+
case "subscript_declaration":
|
|
5891
6054
|
case "class":
|
|
5892
6055
|
case "class_declaration":
|
|
6056
|
+
case "actor_declaration":
|
|
6057
|
+
case "extension_declaration":
|
|
5893
6058
|
return 0.2;
|
|
5894
6059
|
case "interface":
|
|
5895
6060
|
case "type":
|
|
5896
6061
|
case "enum":
|
|
6062
|
+
case "enum_declaration":
|
|
5897
6063
|
case "struct":
|
|
6064
|
+
case "struct_declaration":
|
|
5898
6065
|
case "impl":
|
|
5899
6066
|
case "trait":
|
|
6067
|
+
case "protocol_declaration":
|
|
5900
6068
|
case "module":
|
|
5901
6069
|
return 0.1;
|
|
5902
6070
|
default:
|
|
@@ -6003,11 +6171,21 @@ function isImplementationChunkType(chunkType) {
|
|
|
6003
6171
|
"function_declaration",
|
|
6004
6172
|
"method",
|
|
6005
6173
|
"method_definition",
|
|
6174
|
+
"method_declaration",
|
|
6175
|
+
"protocol_function_declaration",
|
|
6176
|
+
"init_declaration",
|
|
6177
|
+
"deinit_declaration",
|
|
6178
|
+
"subscript_declaration",
|
|
6006
6179
|
"class",
|
|
6007
6180
|
"class_declaration",
|
|
6181
|
+
"actor_declaration",
|
|
6182
|
+
"extension_declaration",
|
|
6008
6183
|
"interface",
|
|
6184
|
+
"protocol_declaration",
|
|
6009
6185
|
"type",
|
|
6010
6186
|
"enum",
|
|
6187
|
+
"enum_declaration",
|
|
6188
|
+
"struct_declaration",
|
|
6011
6189
|
"module"
|
|
6012
6190
|
].includes(chunkType);
|
|
6013
6191
|
}
|
|
@@ -7010,6 +7188,29 @@ var Indexer = class {
|
|
|
7010
7188
|
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7011
7189
|
return `index.forceReembed.${projectHash}`;
|
|
7012
7190
|
}
|
|
7191
|
+
getCallGraphResolutionMetadataKey() {
|
|
7192
|
+
if (this.config.scope !== "global") {
|
|
7193
|
+
return "index.callGraphResolutionVersion";
|
|
7194
|
+
}
|
|
7195
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7196
|
+
return `index.callGraphResolutionVersion.${projectHash}`;
|
|
7197
|
+
}
|
|
7198
|
+
getSwiftParserVersionMetadataKey() {
|
|
7199
|
+
const key = "index.parser.swiftVersion";
|
|
7200
|
+
if (this.config.scope !== "global") {
|
|
7201
|
+
return key;
|
|
7202
|
+
}
|
|
7203
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7204
|
+
return `${key}.${projectHash}`;
|
|
7205
|
+
}
|
|
7206
|
+
getMetalParserVersionMetadataKey() {
|
|
7207
|
+
const key = "index.parser.metalVersion";
|
|
7208
|
+
if (this.config.scope !== "global") {
|
|
7209
|
+
return key;
|
|
7210
|
+
}
|
|
7211
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7212
|
+
return `${key}.${projectHash}`;
|
|
7213
|
+
}
|
|
7013
7214
|
hasProjectForceReembedPending() {
|
|
7014
7215
|
return this.config.scope === "global" && this.database?.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
7015
7216
|
}
|
|
@@ -8170,6 +8371,7 @@ var Indexer = class {
|
|
|
8170
8371
|
this.database.setMetadata("index.embeddingProvider", provider.provider);
|
|
8171
8372
|
this.database.setMetadata("index.embeddingModel", provider.modelInfo.model);
|
|
8172
8373
|
this.database.setMetadata("index.embeddingDimensions", provider.modelInfo.dimensions.toString());
|
|
8374
|
+
this.database.setMetadata(this.getCallGraphResolutionMetadataKey(), CALL_GRAPH_RESOLUTION_VERSION);
|
|
8173
8375
|
if (this.config.scope === "global") {
|
|
8174
8376
|
if (completeProjectEmbeddingStrategyReset) {
|
|
8175
8377
|
this.database.setMetadata(this.getProjectEmbeddingStrategyMetadataKey(), EMBEDDING_STRATEGY_VERSION);
|
|
@@ -8357,6 +8559,20 @@ var Indexer = class {
|
|
|
8357
8559
|
totalChunks: 0
|
|
8358
8560
|
});
|
|
8359
8561
|
this.loadFileHashCache();
|
|
8562
|
+
const swiftParserMetadataKey = this.getSwiftParserVersionMetadataKey();
|
|
8563
|
+
const reparseCachedSwiftFiles = database.getMetadata(swiftParserMetadataKey) !== SWIFT_PARSER_VERSION;
|
|
8564
|
+
const metalParserMetadataKey = this.getMetalParserVersionMetadataKey();
|
|
8565
|
+
const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
|
|
8566
|
+
if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some(
|
|
8567
|
+
(filePath) => path12.extname(filePath).toLowerCase() === ".swift"
|
|
8568
|
+
)) {
|
|
8569
|
+
this.logger.info("Reindexing cached Swift files for parser support");
|
|
8570
|
+
}
|
|
8571
|
+
if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some(
|
|
8572
|
+
(filePath) => path12.extname(filePath).toLowerCase() === ".metal"
|
|
8573
|
+
)) {
|
|
8574
|
+
this.logger.info("Reindexing cached Metal files for parser support");
|
|
8575
|
+
}
|
|
8360
8576
|
const includePatterns = [...this.config.include, ...this.config.additionalInclude];
|
|
8361
8577
|
const { files, skipped } = await collectFiles(
|
|
8362
8578
|
this.projectRoot,
|
|
@@ -8376,10 +8592,17 @@ var Indexer = class {
|
|
|
8376
8592
|
const changedFiles = [];
|
|
8377
8593
|
const unchangedFilePaths = /* @__PURE__ */ new Set();
|
|
8378
8594
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
8595
|
+
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
8379
8596
|
for (const f of files) {
|
|
8380
8597
|
const currentHash = hashFile(f.path);
|
|
8381
8598
|
currentFileHashes.set(f.path, currentHash);
|
|
8382
|
-
|
|
8599
|
+
const cachedHashMatches = this.fileHashCache.get(f.path) === currentHash;
|
|
8600
|
+
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(f.path).some(
|
|
8601
|
+
(chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
|
|
8602
|
+
);
|
|
8603
|
+
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path12.extname(f.path).toLowerCase() === ".swift";
|
|
8604
|
+
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path12.extname(f.path).toLowerCase() === ".metal";
|
|
8605
|
+
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade) {
|
|
8383
8606
|
unchangedFilePaths.add(f.path);
|
|
8384
8607
|
this.logger.recordCacheHit();
|
|
8385
8608
|
} else {
|
|
@@ -8581,16 +8804,28 @@ var Indexer = class {
|
|
|
8581
8804
|
const fileSymbols = [];
|
|
8582
8805
|
for (const chunk of parsed.chunks) {
|
|
8583
8806
|
if (!chunk.name || !CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(chunk.chunkType)) continue;
|
|
8584
|
-
const
|
|
8807
|
+
const existingMetalSymbol = chunk.language === "metal" ? fileSymbols.find(
|
|
8808
|
+
(symbol2) => symbol2.name === chunk.name && symbol2.kind === chunk.chunkType && symbol2.startLine <= chunk.endLine && chunk.startLine <= symbol2.endLine
|
|
8809
|
+
) : void 0;
|
|
8810
|
+
if (existingMetalSymbol) {
|
|
8811
|
+
existingMetalSymbol.startLine = Math.min(existingMetalSymbol.startLine, chunk.startLine);
|
|
8812
|
+
existingMetalSymbol.endLine = Math.max(existingMetalSymbol.endLine, chunk.endLine);
|
|
8813
|
+
existingMetalSymbol.startCol = Math.min(existingMetalSymbol.startCol, chunk.startCol ?? 0);
|
|
8814
|
+
existingMetalSymbol.endCol = Math.max(existingMetalSymbol.endCol, chunk.endCol ?? 0);
|
|
8815
|
+
continue;
|
|
8816
|
+
}
|
|
8817
|
+
const symbolId = `sym_${hashContent(
|
|
8818
|
+
parsed.path + ":" + chunk.name + ":" + chunk.chunkType + ":" + chunk.startLine + ":" + (chunk.startCol ?? 0)
|
|
8819
|
+
).slice(0, 16)}`;
|
|
8585
8820
|
const symbol = {
|
|
8586
8821
|
id: symbolId,
|
|
8587
8822
|
filePath: parsed.path,
|
|
8588
8823
|
name: chunk.name,
|
|
8589
8824
|
kind: chunk.chunkType,
|
|
8590
8825
|
startLine: chunk.startLine,
|
|
8591
|
-
startCol: 0,
|
|
8826
|
+
startCol: chunk.startCol ?? 0,
|
|
8592
8827
|
endLine: chunk.endLine,
|
|
8593
|
-
endCol: 0,
|
|
8828
|
+
endCol: chunk.endCol ?? 0,
|
|
8594
8829
|
language: chunk.language
|
|
8595
8830
|
};
|
|
8596
8831
|
fileSymbols.push(symbol);
|
|
@@ -8615,8 +8850,10 @@ var Indexer = class {
|
|
|
8615
8850
|
if (callSites.length === 0) continue;
|
|
8616
8851
|
const edges = [];
|
|
8617
8852
|
for (const site of callSites) {
|
|
8618
|
-
const enclosingSymbol =
|
|
8619
|
-
|
|
8853
|
+
const enclosingSymbol = findEnclosingSymbol(
|
|
8854
|
+
fileSymbols,
|
|
8855
|
+
site.line,
|
|
8856
|
+
site.column
|
|
8620
8857
|
);
|
|
8621
8858
|
if (!enclosingSymbol) continue;
|
|
8622
8859
|
const edgeId = `edge_${hashContent(enclosingSymbol.id + ":" + site.calleeName + ":" + site.line + ":" + site.column).slice(0, 16)}`;
|
|
@@ -8635,7 +8872,21 @@ var Indexer = class {
|
|
|
8635
8872
|
if (edges.length > 0) {
|
|
8636
8873
|
database.upsertCallEdgesBatch(edges);
|
|
8637
8874
|
for (const edge of edges) {
|
|
8638
|
-
|
|
8875
|
+
let candidates = symbolsByName.get(normalizeSymbolKey(edge.targetName));
|
|
8876
|
+
if (fileLanguage === "php" && candidates) {
|
|
8877
|
+
if (edge.callType === "Constructor") {
|
|
8878
|
+
candidates = candidates.filter(
|
|
8879
|
+
(candidate) => PHP_CLASS_SYMBOL_CHUNK_TYPES.has(candidate.kind)
|
|
8880
|
+
);
|
|
8881
|
+
} else if (edge.callType === "Call") {
|
|
8882
|
+
candidates = candidates.filter(
|
|
8883
|
+
(candidate) => PHP_FUNCTION_SYMBOL_CHUNK_TYPES.has(candidate.kind)
|
|
8884
|
+
);
|
|
8885
|
+
}
|
|
8886
|
+
}
|
|
8887
|
+
candidates = candidates?.filter(
|
|
8888
|
+
(symbol) => isCompatibleCFamilyCallTarget(fileLanguage, edge.callType, symbol.kind)
|
|
8889
|
+
);
|
|
8639
8890
|
if (candidates && candidates.length === 1) {
|
|
8640
8891
|
database.resolveCallEdge(edge.id, candidates[0].id);
|
|
8641
8892
|
}
|
|
@@ -8690,6 +8941,8 @@ var Indexer = class {
|
|
|
8690
8941
|
this.saveFileHashCache();
|
|
8691
8942
|
this.saveFailedBatches([]);
|
|
8692
8943
|
}
|
|
8944
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
8945
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
8693
8946
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
8694
8947
|
this.indexCompatibility = { compatible: true };
|
|
8695
8948
|
stats.durationMs = Date.now() - startTime;
|
|
@@ -8717,6 +8970,8 @@ var Indexer = class {
|
|
|
8717
8970
|
this.saveFileHashCache();
|
|
8718
8971
|
this.saveFailedBatches([]);
|
|
8719
8972
|
}
|
|
8973
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
8974
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
8720
8975
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
8721
8976
|
this.indexCompatibility = { compatible: true };
|
|
8722
8977
|
stats.durationMs = Date.now() - startTime;
|
|
@@ -8987,6 +9242,8 @@ var Indexer = class {
|
|
|
8987
9242
|
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
8988
9243
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
8989
9244
|
}
|
|
9245
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
9246
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
8990
9247
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
8991
9248
|
this.indexCompatibility = { compatible: true };
|
|
8992
9249
|
this.logger.recordIndexingEnd();
|
|
@@ -10097,251 +10354,45 @@ var Indexer = class {
|
|
|
10097
10354
|
var import_fs11 = require("fs");
|
|
10098
10355
|
var path17 = __toESM(require("path"), 1);
|
|
10099
10356
|
|
|
10100
|
-
// src/
|
|
10101
|
-
var import_fs9 = require("fs");
|
|
10102
|
-
var path14 = __toESM(require("path"), 1);
|
|
10103
|
-
|
|
10104
|
-
// src/config/rebase.ts
|
|
10357
|
+
// src/tools/knowledge-base-paths.ts
|
|
10105
10358
|
var path13 = __toESM(require("path"), 1);
|
|
10106
|
-
function
|
|
10107
|
-
const
|
|
10108
|
-
|
|
10109
|
-
|
|
10110
|
-
function rebasePathEntries(values, fromDir, toDir) {
|
|
10111
|
-
if (!Array.isArray(values)) {
|
|
10112
|
-
return [];
|
|
10113
|
-
}
|
|
10114
|
-
return values.filter((value) => typeof value === "string").map((value) => {
|
|
10115
|
-
const trimmed = value.trim();
|
|
10116
|
-
if (!trimmed || path13.isAbsolute(trimmed)) {
|
|
10117
|
-
return trimmed;
|
|
10118
|
-
}
|
|
10119
|
-
return normalizePathSeparators(path13.normalize(path13.relative(toDir, path13.resolve(fromDir, trimmed))));
|
|
10120
|
-
}).filter(Boolean);
|
|
10121
|
-
}
|
|
10122
|
-
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
10123
|
-
if (!Array.isArray(values)) {
|
|
10124
|
-
return [];
|
|
10359
|
+
function resolveConfigPathValue(value, baseDir) {
|
|
10360
|
+
const trimmed = value.trim();
|
|
10361
|
+
if (!trimmed) {
|
|
10362
|
+
return trimmed;
|
|
10125
10363
|
}
|
|
10126
|
-
|
|
10127
|
-
|
|
10128
|
-
if (!trimmed) {
|
|
10129
|
-
return trimmed;
|
|
10130
|
-
}
|
|
10131
|
-
if (path13.isAbsolute(trimmed)) {
|
|
10132
|
-
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
10133
|
-
return normalizePathSeparators(path13.normalize(path13.relative(sourceRoot, trimmed) || "."));
|
|
10134
|
-
}
|
|
10135
|
-
return path13.normalize(trimmed);
|
|
10136
|
-
}
|
|
10137
|
-
const resolvedFromSource = path13.resolve(sourceRoot, trimmed);
|
|
10138
|
-
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
10139
|
-
return normalizePathSeparators(path13.normalize(trimmed));
|
|
10140
|
-
}
|
|
10141
|
-
return normalizePathSeparators(path13.normalize(path13.relative(targetRoot, resolvedFromSource)));
|
|
10142
|
-
}).filter(Boolean);
|
|
10364
|
+
const absolutePath = path13.isAbsolute(trimmed) ? trimmed : path13.resolve(baseDir, trimmed);
|
|
10365
|
+
return path13.normalize(absolutePath);
|
|
10143
10366
|
}
|
|
10144
10367
|
|
|
10145
|
-
// src/
|
|
10146
|
-
var
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
"search",
|
|
10155
|
-
"debug",
|
|
10156
|
-
"scope"
|
|
10157
|
-
];
|
|
10158
|
-
var MERGE_ARRAY_KEYS = ["knowledgeBases", "additionalInclude"];
|
|
10159
|
-
function isRecord(value) {
|
|
10160
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10161
|
-
}
|
|
10162
|
-
function isStringArray2(value) {
|
|
10163
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
10164
|
-
}
|
|
10165
|
-
function applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key) {
|
|
10166
|
-
if (key in normalizedProjectConfig) {
|
|
10167
|
-
merged[key] = normalizedProjectConfig[key];
|
|
10168
|
-
return;
|
|
10169
|
-
}
|
|
10170
|
-
if (key in globalConfig) {
|
|
10171
|
-
merged[key] = globalConfig[key];
|
|
10368
|
+
// src/tools/utils.ts
|
|
10369
|
+
var import_tiktoken = require("tiktoken");
|
|
10370
|
+
var MIN_CONTEXT_PACK_TOKEN_BUDGET = 128;
|
|
10371
|
+
var MAX_CONTEXT_PACK_TOKEN_BUDGET = 4e3;
|
|
10372
|
+
var DEFAULT_CONTEXT_PACK_TOKEN_BUDGET = 1200;
|
|
10373
|
+
var CONTEXT_TOKENIZER = (0, import_tiktoken.get_encoding)("cl100k_base");
|
|
10374
|
+
function clampContextPackTokenBudget(tokenBudget) {
|
|
10375
|
+
if (tokenBudget === void 0 || !Number.isFinite(tokenBudget)) {
|
|
10376
|
+
return DEFAULT_CONTEXT_PACK_TOKEN_BUDGET;
|
|
10172
10377
|
}
|
|
10378
|
+
return Math.min(
|
|
10379
|
+
MAX_CONTEXT_PACK_TOKEN_BUDGET,
|
|
10380
|
+
Math.max(MIN_CONTEXT_PACK_TOKEN_BUDGET, Math.floor(tokenBudget))
|
|
10381
|
+
);
|
|
10173
10382
|
}
|
|
10174
|
-
function
|
|
10175
|
-
return
|
|
10383
|
+
function countContextTokens(text) {
|
|
10384
|
+
return CONTEXT_TOKENIZER.encode(text).length;
|
|
10176
10385
|
}
|
|
10177
|
-
function
|
|
10178
|
-
|
|
10179
|
-
const
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
10186
|
-
|
|
10187
|
-
}
|
|
10188
|
-
function validateConfigLayerShape(rawConfig, filePath) {
|
|
10189
|
-
if (!isRecord(rawConfig)) {
|
|
10190
|
-
throw new Error(`Config file ${filePath} must contain a JSON object at the root.`);
|
|
10191
|
-
}
|
|
10192
|
-
if (rawConfig.knowledgeBases !== void 0 && !isStringArray2(rawConfig.knowledgeBases)) {
|
|
10193
|
-
throw new Error(`Config file ${filePath} field 'knowledgeBases' must be an array of strings.`);
|
|
10194
|
-
}
|
|
10195
|
-
if (rawConfig.additionalInclude !== void 0 && !isStringArray2(rawConfig.additionalInclude)) {
|
|
10196
|
-
throw new Error(`Config file ${filePath} field 'additionalInclude' must be an array of strings.`);
|
|
10197
|
-
}
|
|
10198
|
-
if (rawConfig.include !== void 0 && !isStringArray2(rawConfig.include)) {
|
|
10199
|
-
throw new Error(`Config file ${filePath} field 'include' must be an array of strings.`);
|
|
10200
|
-
}
|
|
10201
|
-
if (rawConfig.exclude !== void 0 && !isStringArray2(rawConfig.exclude)) {
|
|
10202
|
-
throw new Error(`Config file ${filePath} field 'exclude' must be an array of strings.`);
|
|
10203
|
-
}
|
|
10204
|
-
for (const section of ["customProvider", "indexing", "search", "debug", "reranker"]) {
|
|
10205
|
-
const value = rawConfig[section];
|
|
10206
|
-
if (value !== void 0 && !isRecord(value)) {
|
|
10207
|
-
throw new Error(`Config file ${filePath} field '${section}' must be an object.`);
|
|
10208
|
-
}
|
|
10209
|
-
}
|
|
10210
|
-
return rawConfig;
|
|
10211
|
-
}
|
|
10212
|
-
function loadJsonFile(filePath) {
|
|
10213
|
-
if (!(0, import_fs9.existsSync)(filePath)) {
|
|
10214
|
-
return null;
|
|
10215
|
-
}
|
|
10216
|
-
try {
|
|
10217
|
-
const content = (0, import_fs9.readFileSync)(filePath, "utf-8");
|
|
10218
|
-
return validateConfigLayerShape(JSON.parse(content), filePath);
|
|
10219
|
-
} catch (error) {
|
|
10220
|
-
if (error instanceof Error && error.message.startsWith("Config file ")) {
|
|
10221
|
-
throw error;
|
|
10222
|
-
}
|
|
10223
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
10224
|
-
throw new Error(`Failed to load config file ${filePath}: ${message}`);
|
|
10225
|
-
}
|
|
10226
|
-
}
|
|
10227
|
-
function loadConfigFile(filePath) {
|
|
10228
|
-
return loadJsonFile(filePath);
|
|
10229
|
-
}
|
|
10230
|
-
function materializeLocalProjectConfig(projectRoot, config, host = "opencode") {
|
|
10231
|
-
const localConfigPath = resolveWritableProjectConfigPath(projectRoot, host);
|
|
10232
|
-
(0, import_fs9.mkdirSync)(path14.dirname(localConfigPath), { recursive: true });
|
|
10233
|
-
(0, import_fs9.writeFileSync)(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
10234
|
-
return localConfigPath;
|
|
10235
|
-
}
|
|
10236
|
-
function loadProjectConfigLayer(projectRoot, host = "opencode") {
|
|
10237
|
-
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
10238
|
-
const projectConfig = loadJsonFile(projectConfigPath);
|
|
10239
|
-
if (!projectConfig) {
|
|
10240
|
-
return {};
|
|
10241
|
-
}
|
|
10242
|
-
const normalizedConfig = { ...projectConfig };
|
|
10243
|
-
const projectConfigBaseDir = path14.dirname(path14.dirname(projectConfigPath));
|
|
10244
|
-
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
10245
|
-
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
10246
|
-
normalizedConfig.knowledgeBases,
|
|
10247
|
-
projectConfigBaseDir,
|
|
10248
|
-
projectRoot
|
|
10249
|
-
);
|
|
10250
|
-
}
|
|
10251
|
-
return normalizedConfig;
|
|
10252
|
-
}
|
|
10253
|
-
function loadMergedConfig(projectRoot, host = "opencode") {
|
|
10254
|
-
const globalConfigPath = resolveGlobalConfigPath(host);
|
|
10255
|
-
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
10256
|
-
let globalConfig = null;
|
|
10257
|
-
let globalConfigError = null;
|
|
10258
|
-
try {
|
|
10259
|
-
globalConfig = loadJsonFile(globalConfigPath);
|
|
10260
|
-
} catch (error) {
|
|
10261
|
-
globalConfigError = error instanceof Error ? error : new Error(String(error));
|
|
10262
|
-
}
|
|
10263
|
-
const projectConfig = loadJsonFile(projectConfigPath);
|
|
10264
|
-
const normalizedProjectConfig = loadProjectConfigLayer(projectRoot, host);
|
|
10265
|
-
if (globalConfigError) {
|
|
10266
|
-
if (!projectConfig) {
|
|
10267
|
-
throw globalConfigError;
|
|
10268
|
-
}
|
|
10269
|
-
globalConfig = null;
|
|
10270
|
-
}
|
|
10271
|
-
if (!globalConfig && !projectConfig) {
|
|
10272
|
-
return {};
|
|
10273
|
-
}
|
|
10274
|
-
if (!projectConfig && globalConfig) {
|
|
10275
|
-
return globalConfig;
|
|
10276
|
-
}
|
|
10277
|
-
if (!globalConfig && projectConfig) {
|
|
10278
|
-
return normalizedProjectConfig;
|
|
10279
|
-
}
|
|
10280
|
-
if (!globalConfig || !projectConfig) {
|
|
10281
|
-
return globalConfig ?? normalizedProjectConfig;
|
|
10282
|
-
}
|
|
10283
|
-
const merged = { ...globalConfig };
|
|
10284
|
-
for (const key of PROJECT_OVERRIDE_KEYS) {
|
|
10285
|
-
applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key);
|
|
10286
|
-
}
|
|
10287
|
-
if (projectConfig) {
|
|
10288
|
-
for (const key of Object.keys(projectConfig)) {
|
|
10289
|
-
if (PROJECT_OVERRIDE_KEYS.includes(key) || MERGE_ARRAY_KEYS.includes(key)) {
|
|
10290
|
-
continue;
|
|
10291
|
-
}
|
|
10292
|
-
merged[key] = normalizedProjectConfig[key];
|
|
10293
|
-
}
|
|
10294
|
-
}
|
|
10295
|
-
const globalKbs = globalConfig && Array.isArray(globalConfig.knowledgeBases) ? globalConfig.knowledgeBases : [];
|
|
10296
|
-
const projectKbs = projectConfig ? Array.isArray(normalizedProjectConfig.knowledgeBases) ? normalizedProjectConfig.knowledgeBases : [] : [];
|
|
10297
|
-
const allKbs = [...globalKbs, ...projectKbs];
|
|
10298
|
-
merged.knowledgeBases = mergeKnowledgeBasePaths(allKbs);
|
|
10299
|
-
const globalAdditional = globalConfig && Array.isArray(globalConfig.additionalInclude) ? globalConfig.additionalInclude : [];
|
|
10300
|
-
const projectAdditional = projectConfig && Array.isArray(projectConfig.additionalInclude) ? projectConfig.additionalInclude : [];
|
|
10301
|
-
const allAdditional = [...globalAdditional, ...projectAdditional];
|
|
10302
|
-
merged.additionalInclude = mergeUniqueStringArray(allAdditional);
|
|
10303
|
-
return merged;
|
|
10304
|
-
}
|
|
10305
|
-
|
|
10306
|
-
// src/tools/knowledge-base-paths.ts
|
|
10307
|
-
var path15 = __toESM(require("path"), 1);
|
|
10308
|
-
function resolveConfigPathValue(value, baseDir) {
|
|
10309
|
-
const trimmed = value.trim();
|
|
10310
|
-
if (!trimmed) {
|
|
10311
|
-
return trimmed;
|
|
10312
|
-
}
|
|
10313
|
-
const absolutePath = path15.isAbsolute(trimmed) ? trimmed : path15.resolve(baseDir, trimmed);
|
|
10314
|
-
return path15.normalize(absolutePath);
|
|
10315
|
-
}
|
|
10316
|
-
|
|
10317
|
-
// src/tools/utils.ts
|
|
10318
|
-
var import_tiktoken = require("tiktoken");
|
|
10319
|
-
var MIN_CONTEXT_PACK_TOKEN_BUDGET = 128;
|
|
10320
|
-
var MAX_CONTEXT_PACK_TOKEN_BUDGET = 4e3;
|
|
10321
|
-
var DEFAULT_CONTEXT_PACK_TOKEN_BUDGET = 1200;
|
|
10322
|
-
var CONTEXT_TOKENIZER = (0, import_tiktoken.get_encoding)("cl100k_base");
|
|
10323
|
-
function clampContextPackTokenBudget(tokenBudget) {
|
|
10324
|
-
if (tokenBudget === void 0 || !Number.isFinite(tokenBudget)) {
|
|
10325
|
-
return DEFAULT_CONTEXT_PACK_TOKEN_BUDGET;
|
|
10326
|
-
}
|
|
10327
|
-
return Math.min(
|
|
10328
|
-
MAX_CONTEXT_PACK_TOKEN_BUDGET,
|
|
10329
|
-
Math.max(MIN_CONTEXT_PACK_TOKEN_BUDGET, Math.floor(tokenBudget))
|
|
10330
|
-
);
|
|
10331
|
-
}
|
|
10332
|
-
function countContextTokens(text) {
|
|
10333
|
-
return CONTEXT_TOKENIZER.encode(text).length;
|
|
10334
|
-
}
|
|
10335
|
-
function fitTextToContextBudget(text, tokenBudget) {
|
|
10336
|
-
const normalizedBudget = clampContextPackTokenBudget(tokenBudget);
|
|
10337
|
-
const tokenEstimate = countContextTokens(text);
|
|
10338
|
-
if (tokenEstimate <= normalizedBudget) {
|
|
10339
|
-
return {
|
|
10340
|
-
text,
|
|
10341
|
-
tokenBudget: normalizedBudget,
|
|
10342
|
-
tokenEstimate,
|
|
10343
|
-
truncated: false
|
|
10344
|
-
};
|
|
10386
|
+
function fitTextToContextBudget(text, tokenBudget) {
|
|
10387
|
+
const normalizedBudget = clampContextPackTokenBudget(tokenBudget);
|
|
10388
|
+
const tokenEstimate = countContextTokens(text);
|
|
10389
|
+
if (tokenEstimate <= normalizedBudget) {
|
|
10390
|
+
return {
|
|
10391
|
+
text,
|
|
10392
|
+
tokenBudget: normalizedBudget,
|
|
10393
|
+
tokenEstimate,
|
|
10394
|
+
truncated: false
|
|
10395
|
+
};
|
|
10345
10396
|
}
|
|
10346
10397
|
const suffix = "\n...[truncated to context token budget]";
|
|
10347
10398
|
const codePoints = Array.from(text);
|
|
@@ -10723,6 +10774,208 @@ ${truncateContent(r.content)}
|
|
|
10723
10774
|
// src/tools/config-state.ts
|
|
10724
10775
|
var import_fs10 = require("fs");
|
|
10725
10776
|
var path16 = __toESM(require("path"), 1);
|
|
10777
|
+
|
|
10778
|
+
// src/config/merger.ts
|
|
10779
|
+
var import_fs9 = require("fs");
|
|
10780
|
+
var path15 = __toESM(require("path"), 1);
|
|
10781
|
+
|
|
10782
|
+
// src/config/rebase.ts
|
|
10783
|
+
var path14 = __toESM(require("path"), 1);
|
|
10784
|
+
function isWithinRoot(rootDir, targetPath) {
|
|
10785
|
+
const relativePath = path14.relative(rootDir, targetPath);
|
|
10786
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path14.isAbsolute(relativePath);
|
|
10787
|
+
}
|
|
10788
|
+
function rebasePathEntries(values, fromDir, toDir) {
|
|
10789
|
+
if (!Array.isArray(values)) {
|
|
10790
|
+
return [];
|
|
10791
|
+
}
|
|
10792
|
+
return values.filter((value) => typeof value === "string").map((value) => {
|
|
10793
|
+
const trimmed = value.trim();
|
|
10794
|
+
if (!trimmed || path14.isAbsolute(trimmed)) {
|
|
10795
|
+
return trimmed;
|
|
10796
|
+
}
|
|
10797
|
+
return normalizePathSeparators(path14.normalize(path14.relative(toDir, path14.resolve(fromDir, trimmed))));
|
|
10798
|
+
}).filter(Boolean);
|
|
10799
|
+
}
|
|
10800
|
+
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
10801
|
+
if (!Array.isArray(values)) {
|
|
10802
|
+
return [];
|
|
10803
|
+
}
|
|
10804
|
+
return values.filter((value) => typeof value === "string").map((value) => {
|
|
10805
|
+
const trimmed = value.trim();
|
|
10806
|
+
if (!trimmed) {
|
|
10807
|
+
return trimmed;
|
|
10808
|
+
}
|
|
10809
|
+
if (path14.isAbsolute(trimmed)) {
|
|
10810
|
+
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
10811
|
+
return normalizePathSeparators(path14.normalize(path14.relative(sourceRoot, trimmed) || "."));
|
|
10812
|
+
}
|
|
10813
|
+
return path14.normalize(trimmed);
|
|
10814
|
+
}
|
|
10815
|
+
const resolvedFromSource = path14.resolve(sourceRoot, trimmed);
|
|
10816
|
+
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
10817
|
+
return normalizePathSeparators(path14.normalize(trimmed));
|
|
10818
|
+
}
|
|
10819
|
+
return normalizePathSeparators(path14.normalize(path14.relative(targetRoot, resolvedFromSource)));
|
|
10820
|
+
}).filter(Boolean);
|
|
10821
|
+
}
|
|
10822
|
+
|
|
10823
|
+
// src/config/merger.ts
|
|
10824
|
+
var PROJECT_OVERRIDE_KEYS = [
|
|
10825
|
+
"embeddingProvider",
|
|
10826
|
+
"customProvider",
|
|
10827
|
+
"embeddingModel",
|
|
10828
|
+
"reranker",
|
|
10829
|
+
"include",
|
|
10830
|
+
"exclude",
|
|
10831
|
+
"indexing",
|
|
10832
|
+
"search",
|
|
10833
|
+
"debug",
|
|
10834
|
+
"scope"
|
|
10835
|
+
];
|
|
10836
|
+
var MERGE_ARRAY_KEYS = ["knowledgeBases", "additionalInclude"];
|
|
10837
|
+
function isRecord(value) {
|
|
10838
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10839
|
+
}
|
|
10840
|
+
function isStringArray2(value) {
|
|
10841
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
10842
|
+
}
|
|
10843
|
+
function applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key) {
|
|
10844
|
+
if (key in normalizedProjectConfig) {
|
|
10845
|
+
merged[key] = normalizedProjectConfig[key];
|
|
10846
|
+
return;
|
|
10847
|
+
}
|
|
10848
|
+
if (key in globalConfig) {
|
|
10849
|
+
merged[key] = globalConfig[key];
|
|
10850
|
+
}
|
|
10851
|
+
}
|
|
10852
|
+
function mergeUniqueStringArray(values) {
|
|
10853
|
+
return [...new Set(values.map((value) => String(value).trim()))];
|
|
10854
|
+
}
|
|
10855
|
+
function normalizeKnowledgeBasePath(value) {
|
|
10856
|
+
let normalized = path15.normalize(String(value).trim());
|
|
10857
|
+
const root = path15.parse(normalized).root;
|
|
10858
|
+
while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
|
|
10859
|
+
normalized = normalized.slice(0, -1);
|
|
10860
|
+
}
|
|
10861
|
+
return normalized;
|
|
10862
|
+
}
|
|
10863
|
+
function mergeKnowledgeBasePaths(values) {
|
|
10864
|
+
return [...new Set(values.map((value) => normalizeKnowledgeBasePath(value)).filter((value) => value.length > 0))];
|
|
10865
|
+
}
|
|
10866
|
+
function validateConfigLayerShape(rawConfig, filePath) {
|
|
10867
|
+
if (!isRecord(rawConfig)) {
|
|
10868
|
+
throw new Error(`Config file ${filePath} must contain a JSON object at the root.`);
|
|
10869
|
+
}
|
|
10870
|
+
if (rawConfig.knowledgeBases !== void 0 && !isStringArray2(rawConfig.knowledgeBases)) {
|
|
10871
|
+
throw new Error(`Config file ${filePath} field 'knowledgeBases' must be an array of strings.`);
|
|
10872
|
+
}
|
|
10873
|
+
if (rawConfig.additionalInclude !== void 0 && !isStringArray2(rawConfig.additionalInclude)) {
|
|
10874
|
+
throw new Error(`Config file ${filePath} field 'additionalInclude' must be an array of strings.`);
|
|
10875
|
+
}
|
|
10876
|
+
if (rawConfig.include !== void 0 && !isStringArray2(rawConfig.include)) {
|
|
10877
|
+
throw new Error(`Config file ${filePath} field 'include' must be an array of strings.`);
|
|
10878
|
+
}
|
|
10879
|
+
if (rawConfig.exclude !== void 0 && !isStringArray2(rawConfig.exclude)) {
|
|
10880
|
+
throw new Error(`Config file ${filePath} field 'exclude' must be an array of strings.`);
|
|
10881
|
+
}
|
|
10882
|
+
for (const section of ["customProvider", "indexing", "search", "debug", "reranker"]) {
|
|
10883
|
+
const value = rawConfig[section];
|
|
10884
|
+
if (value !== void 0 && !isRecord(value)) {
|
|
10885
|
+
throw new Error(`Config file ${filePath} field '${section}' must be an object.`);
|
|
10886
|
+
}
|
|
10887
|
+
}
|
|
10888
|
+
return rawConfig;
|
|
10889
|
+
}
|
|
10890
|
+
function loadJsonFile(filePath) {
|
|
10891
|
+
if (!(0, import_fs9.existsSync)(filePath)) {
|
|
10892
|
+
return null;
|
|
10893
|
+
}
|
|
10894
|
+
try {
|
|
10895
|
+
const content = (0, import_fs9.readFileSync)(filePath, "utf-8");
|
|
10896
|
+
return validateConfigLayerShape(JSON.parse(content), filePath);
|
|
10897
|
+
} catch (error) {
|
|
10898
|
+
if (error instanceof Error && error.message.startsWith("Config file ")) {
|
|
10899
|
+
throw error;
|
|
10900
|
+
}
|
|
10901
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10902
|
+
throw new Error(`Failed to load config file ${filePath}: ${message}`);
|
|
10903
|
+
}
|
|
10904
|
+
}
|
|
10905
|
+
function loadConfigFile(filePath) {
|
|
10906
|
+
return loadJsonFile(filePath);
|
|
10907
|
+
}
|
|
10908
|
+
function loadProjectConfigLayer(projectRoot, host = "opencode") {
|
|
10909
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
10910
|
+
const projectConfig = loadJsonFile(projectConfigPath);
|
|
10911
|
+
if (!projectConfig) {
|
|
10912
|
+
return {};
|
|
10913
|
+
}
|
|
10914
|
+
const normalizedConfig = { ...projectConfig };
|
|
10915
|
+
const projectConfigBaseDir = path15.dirname(path15.dirname(projectConfigPath));
|
|
10916
|
+
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
10917
|
+
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
10918
|
+
normalizedConfig.knowledgeBases,
|
|
10919
|
+
projectConfigBaseDir,
|
|
10920
|
+
projectRoot
|
|
10921
|
+
);
|
|
10922
|
+
}
|
|
10923
|
+
return normalizedConfig;
|
|
10924
|
+
}
|
|
10925
|
+
function loadMergedConfig(projectRoot, host = "opencode") {
|
|
10926
|
+
const globalConfigPath = resolveGlobalConfigPath(host);
|
|
10927
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
10928
|
+
let globalConfig = null;
|
|
10929
|
+
let globalConfigError = null;
|
|
10930
|
+
try {
|
|
10931
|
+
globalConfig = loadJsonFile(globalConfigPath);
|
|
10932
|
+
} catch (error) {
|
|
10933
|
+
globalConfigError = error instanceof Error ? error : new Error(String(error));
|
|
10934
|
+
}
|
|
10935
|
+
const projectConfig = loadJsonFile(projectConfigPath);
|
|
10936
|
+
const normalizedProjectConfig = loadProjectConfigLayer(projectRoot, host);
|
|
10937
|
+
if (globalConfigError) {
|
|
10938
|
+
if (!projectConfig) {
|
|
10939
|
+
throw globalConfigError;
|
|
10940
|
+
}
|
|
10941
|
+
globalConfig = null;
|
|
10942
|
+
}
|
|
10943
|
+
if (!globalConfig && !projectConfig) {
|
|
10944
|
+
return {};
|
|
10945
|
+
}
|
|
10946
|
+
if (!projectConfig && globalConfig) {
|
|
10947
|
+
return globalConfig;
|
|
10948
|
+
}
|
|
10949
|
+
if (!globalConfig && projectConfig) {
|
|
10950
|
+
return normalizedProjectConfig;
|
|
10951
|
+
}
|
|
10952
|
+
if (!globalConfig || !projectConfig) {
|
|
10953
|
+
return globalConfig ?? normalizedProjectConfig;
|
|
10954
|
+
}
|
|
10955
|
+
const merged = { ...globalConfig };
|
|
10956
|
+
for (const key of PROJECT_OVERRIDE_KEYS) {
|
|
10957
|
+
applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key);
|
|
10958
|
+
}
|
|
10959
|
+
if (projectConfig) {
|
|
10960
|
+
for (const key of Object.keys(projectConfig)) {
|
|
10961
|
+
if (PROJECT_OVERRIDE_KEYS.includes(key) || MERGE_ARRAY_KEYS.includes(key)) {
|
|
10962
|
+
continue;
|
|
10963
|
+
}
|
|
10964
|
+
merged[key] = normalizedProjectConfig[key];
|
|
10965
|
+
}
|
|
10966
|
+
}
|
|
10967
|
+
const globalKbs = globalConfig && Array.isArray(globalConfig.knowledgeBases) ? globalConfig.knowledgeBases : [];
|
|
10968
|
+
const projectKbs = projectConfig ? Array.isArray(normalizedProjectConfig.knowledgeBases) ? normalizedProjectConfig.knowledgeBases : [] : [];
|
|
10969
|
+
const allKbs = [...globalKbs, ...projectKbs];
|
|
10970
|
+
merged.knowledgeBases = mergeKnowledgeBasePaths(allKbs);
|
|
10971
|
+
const globalAdditional = globalConfig && Array.isArray(globalConfig.additionalInclude) ? globalConfig.additionalInclude : [];
|
|
10972
|
+
const projectAdditional = projectConfig && Array.isArray(projectConfig.additionalInclude) ? projectConfig.additionalInclude : [];
|
|
10973
|
+
const allAdditional = [...globalAdditional, ...projectAdditional];
|
|
10974
|
+
merged.additionalInclude = mergeUniqueStringArray(allAdditional);
|
|
10975
|
+
return merged;
|
|
10976
|
+
}
|
|
10977
|
+
|
|
10978
|
+
// src/tools/config-state.ts
|
|
10726
10979
|
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
10727
10980
|
const normalized = { ...config };
|
|
10728
10981
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
@@ -10806,15 +11059,6 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
|
|
|
10806
11059
|
indexerCache.set(key, indexer);
|
|
10807
11060
|
configCache.set(key, config);
|
|
10808
11061
|
}
|
|
10809
|
-
function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
|
|
10810
|
-
const root = getProjectRoot(projectRoot, host);
|
|
10811
|
-
const localIndexPath = path17.join(root, getHostProjectIndexRelativePath(host));
|
|
10812
|
-
if ((0, import_fs11.existsSync)(localIndexPath)) {
|
|
10813
|
-
return false;
|
|
10814
|
-
}
|
|
10815
|
-
const inheritedIndexPath = resolveWorktreeFallbackProjectIndexPath(root, host);
|
|
10816
|
-
return inheritedIndexPath !== null;
|
|
10817
|
-
}
|
|
10818
11062
|
async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
10819
11063
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
10820
11064
|
return indexer.search(query, options.limit, {
|
|
@@ -10864,9 +11108,7 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth) {
|
|
|
10864
11108
|
}
|
|
10865
11109
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
10866
11110
|
const root = getProjectRoot(projectRoot, host);
|
|
10867
|
-
const
|
|
10868
|
-
let indexer = getIndexerForProject(root, host);
|
|
10869
|
-
const runtimeConfig = configCache.get(key);
|
|
11111
|
+
const indexer = getIndexerForProject(root, host);
|
|
10870
11112
|
try {
|
|
10871
11113
|
if (args.estimateOnly) {
|
|
10872
11114
|
return { kind: "estimate", estimate: await indexer.estimateCost() };
|
|
@@ -10887,19 +11129,7 @@ async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
|
10887
11129
|
return Promise.resolve();
|
|
10888
11130
|
});
|
|
10889
11131
|
};
|
|
10890
|
-
|
|
10891
|
-
if (args.force && shouldForceLocalizeProjectIndex(root, host)) {
|
|
10892
|
-
const inheritedIndexPath = resolveProjectIndexPath(root, runtimeConfig.scope, host);
|
|
10893
|
-
stats = await withIndexLock(inheritedIndexPath, "force-index", async () => {
|
|
10894
|
-
materializeLocalProjectConfig(root, loadProjectConfigLayer(root, host), host);
|
|
10895
|
-
refreshIndexerForDirectory(root, host, runtimeConfig);
|
|
10896
|
-
indexer = getIndexerForProject(root, host);
|
|
10897
|
-
return runIndex(indexer);
|
|
10898
|
-
}, { completeRecoveries: false });
|
|
10899
|
-
} else {
|
|
10900
|
-
stats = await runIndex(indexer);
|
|
10901
|
-
}
|
|
10902
|
-
return { kind: "stats", stats };
|
|
11132
|
+
return { kind: "stats", stats: await runIndex(indexer) };
|
|
10903
11133
|
} catch (error) {
|
|
10904
11134
|
const busyResult = getIndexBusyResult(error);
|
|
10905
11135
|
if (!busyResult) throw error;
|