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.js
CHANGED
|
@@ -653,7 +653,7 @@ var require_ignore = __commonJS({
|
|
|
653
653
|
|
|
654
654
|
// src/cli.ts
|
|
655
655
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
656
|
-
import { realpathSync as realpathSync3, writeFileSync as
|
|
656
|
+
import { realpathSync as realpathSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
657
657
|
import * as os6 from "os";
|
|
658
658
|
import * as path26 from "path";
|
|
659
659
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -673,7 +673,8 @@ var DEFAULT_INCLUDE = [
|
|
|
673
673
|
"**/*.{sh,bash,zsh}",
|
|
674
674
|
"**/*.{txt,html,htm}",
|
|
675
675
|
"**/*.zig",
|
|
676
|
-
"**/*.gd"
|
|
676
|
+
"**/*.gd",
|
|
677
|
+
"**/*.metal"
|
|
677
678
|
];
|
|
678
679
|
var DEFAULT_EXCLUDE = [
|
|
679
680
|
"**/node_modules/**",
|
|
@@ -864,6 +865,9 @@ function isValidProvider(value) {
|
|
|
864
865
|
return typeof value === "string" && Object.keys(EMBEDDING_MODELS).includes(value);
|
|
865
866
|
}
|
|
866
867
|
function isValidModel(value, provider) {
|
|
868
|
+
if (typeof value === "string" && provider === "ollama" && value.trim().length > 0) {
|
|
869
|
+
return true;
|
|
870
|
+
}
|
|
867
871
|
return typeof value === "string" && Object.keys(EMBEDDING_MODELS[provider]).includes(value);
|
|
868
872
|
}
|
|
869
873
|
function isValidScope(value) {
|
|
@@ -2446,6 +2450,9 @@ function loadOpenCodeAuth() {
|
|
|
2446
2450
|
return {};
|
|
2447
2451
|
}
|
|
2448
2452
|
async function detectEmbeddingProvider(preferredProvider, model) {
|
|
2453
|
+
if (preferredProvider === "ollama") {
|
|
2454
|
+
return detectOllamaProvider(model);
|
|
2455
|
+
}
|
|
2449
2456
|
const credentials = await getProviderCredentials(preferredProvider);
|
|
2450
2457
|
if (credentials) {
|
|
2451
2458
|
if (!model) {
|
|
@@ -2461,10 +2468,16 @@ async function detectEmbeddingProvider(preferredProvider, model) {
|
|
|
2461
2468
|
);
|
|
2462
2469
|
}
|
|
2463
2470
|
const providerModels = EMBEDDING_MODELS[preferredProvider];
|
|
2471
|
+
const modelInfo = Object.values(providerModels).find((candidate) => candidate.model === model);
|
|
2472
|
+
if (!modelInfo) {
|
|
2473
|
+
throw new Error(
|
|
2474
|
+
`Model '${model}' is not supported by provider '${preferredProvider}'`
|
|
2475
|
+
);
|
|
2476
|
+
}
|
|
2464
2477
|
return {
|
|
2465
2478
|
provider: preferredProvider,
|
|
2466
2479
|
credentials,
|
|
2467
|
-
modelInfo
|
|
2480
|
+
modelInfo
|
|
2468
2481
|
};
|
|
2469
2482
|
}
|
|
2470
2483
|
throw new Error(
|
|
@@ -2473,6 +2486,13 @@ async function detectEmbeddingProvider(preferredProvider, model) {
|
|
|
2473
2486
|
}
|
|
2474
2487
|
async function tryDetectProvider() {
|
|
2475
2488
|
for (const provider of autoDetectProviders) {
|
|
2489
|
+
if (provider === "ollama") {
|
|
2490
|
+
const ollamaProvider = await tryDetectOllamaProvider();
|
|
2491
|
+
if (ollamaProvider) {
|
|
2492
|
+
return ollamaProvider;
|
|
2493
|
+
}
|
|
2494
|
+
continue;
|
|
2495
|
+
}
|
|
2476
2496
|
const credentials = await getProviderCredentials(provider);
|
|
2477
2497
|
if (credentials) {
|
|
2478
2498
|
return {
|
|
@@ -2540,32 +2560,105 @@ function getGoogleCredentials() {
|
|
|
2540
2560
|
}
|
|
2541
2561
|
return null;
|
|
2542
2562
|
}
|
|
2543
|
-
async function
|
|
2544
|
-
const
|
|
2563
|
+
async function fetchOllama(url, init) {
|
|
2564
|
+
const controller = new AbortController();
|
|
2565
|
+
const timeoutId = setTimeout(() => controller.abort(), 2e3);
|
|
2545
2566
|
try {
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
const response = await fetch(`${baseUrl}/api/tags`, {
|
|
2549
|
-
signal: controller.signal
|
|
2550
|
-
});
|
|
2567
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
2568
|
+
} finally {
|
|
2551
2569
|
clearTimeout(timeoutId);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
async function getOllamaCredentials() {
|
|
2573
|
+
const baseUrl = (process.env.OLLAMA_HOST || "http://localhost:11434").replace(/\/+$/, "");
|
|
2574
|
+
try {
|
|
2575
|
+
const response = await fetchOllama(`${baseUrl}/api/tags`);
|
|
2552
2576
|
if (response.ok) {
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
if (hasEmbeddingModel) {
|
|
2558
|
-
return {
|
|
2559
|
-
provider: "ollama",
|
|
2560
|
-
baseUrl
|
|
2561
|
-
};
|
|
2562
|
-
}
|
|
2577
|
+
return {
|
|
2578
|
+
provider: "ollama",
|
|
2579
|
+
baseUrl
|
|
2580
|
+
};
|
|
2563
2581
|
}
|
|
2564
2582
|
} catch {
|
|
2565
2583
|
return null;
|
|
2566
2584
|
}
|
|
2567
2585
|
return null;
|
|
2568
2586
|
}
|
|
2587
|
+
function findCatalogOllamaModel(model) {
|
|
2588
|
+
const stableName = model.endsWith(":latest") ? model.slice(0, -":latest".length) : model;
|
|
2589
|
+
return Object.values(EMBEDDING_MODELS.ollama).find((candidate) => candidate.model === stableName) ?? null;
|
|
2590
|
+
}
|
|
2591
|
+
function getPositiveIntegerMetadata(modelInfo, suffix) {
|
|
2592
|
+
const values = Object.entries(modelInfo).filter(([key]) => key.endsWith(suffix)).map(([, value]) => value).filter((value) => typeof value === "number" && Number.isInteger(value) && value > 0);
|
|
2593
|
+
return values.length === 1 ? values[0] : null;
|
|
2594
|
+
}
|
|
2595
|
+
async function fetchOllamaModelInfo(credentials, model) {
|
|
2596
|
+
const response = await fetchOllama(`${credentials.baseUrl}/api/show`, {
|
|
2597
|
+
method: "POST",
|
|
2598
|
+
headers: { "Content-Type": "application/json" },
|
|
2599
|
+
body: JSON.stringify({ model })
|
|
2600
|
+
});
|
|
2601
|
+
if (!response.ok) {
|
|
2602
|
+
return null;
|
|
2603
|
+
}
|
|
2604
|
+
const data = await response.json();
|
|
2605
|
+
if (!data.capabilities?.includes("embedding") || !data.model_info) {
|
|
2606
|
+
return null;
|
|
2607
|
+
}
|
|
2608
|
+
const dimensions = getPositiveIntegerMetadata(data.model_info, ".embedding_length");
|
|
2609
|
+
const maxTokens = getPositiveIntegerMetadata(data.model_info, ".context_length");
|
|
2610
|
+
if (!dimensions || !maxTokens) {
|
|
2611
|
+
return null;
|
|
2612
|
+
}
|
|
2613
|
+
return {
|
|
2614
|
+
provider: "ollama",
|
|
2615
|
+
model,
|
|
2616
|
+
dimensions,
|
|
2617
|
+
maxTokens,
|
|
2618
|
+
costPer1MTokens: 0
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
2621
|
+
async function listOllamaModels(credentials) {
|
|
2622
|
+
const response = await fetchOllama(`${credentials.baseUrl}/api/tags`);
|
|
2623
|
+
if (!response.ok) {
|
|
2624
|
+
return [];
|
|
2625
|
+
}
|
|
2626
|
+
const data = await response.json();
|
|
2627
|
+
return [...new Set((data.models ?? []).map((entry) => entry.name ?? entry.model).filter((name) => typeof name === "string" && name.trim().length > 0))];
|
|
2628
|
+
}
|
|
2629
|
+
async function detectOllamaProvider(model) {
|
|
2630
|
+
const credentials = await getOllamaCredentials();
|
|
2631
|
+
if (!credentials) {
|
|
2632
|
+
throw new Error("Preferred provider 'ollama' is not configured or authenticated");
|
|
2633
|
+
}
|
|
2634
|
+
const requestedModel = model?.trim();
|
|
2635
|
+
if (requestedModel) {
|
|
2636
|
+
const catalogModel = findCatalogOllamaModel(requestedModel);
|
|
2637
|
+
if (catalogModel) {
|
|
2638
|
+
return { provider: "ollama", credentials, modelInfo: catalogModel };
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
const candidates = requestedModel ? [requestedModel] : await listOllamaModels(credentials);
|
|
2642
|
+
for (const candidate of candidates) {
|
|
2643
|
+
const modelInfo = await fetchOllamaModelInfo(credentials, candidate);
|
|
2644
|
+
if (modelInfo) {
|
|
2645
|
+
return {
|
|
2646
|
+
provider: "ollama",
|
|
2647
|
+
credentials,
|
|
2648
|
+
modelInfo: findCatalogOllamaModel(candidate) ?? modelInfo
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
const detail = model ? `Model '${model}' is not installed or is not embedding-capable` : "No installed embedding-capable Ollama model was found";
|
|
2653
|
+
throw new Error(detail);
|
|
2654
|
+
}
|
|
2655
|
+
async function tryDetectOllamaProvider() {
|
|
2656
|
+
try {
|
|
2657
|
+
return await detectOllamaProvider();
|
|
2658
|
+
} catch {
|
|
2659
|
+
return null;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2569
2662
|
function getProviderDisplayName(provider) {
|
|
2570
2663
|
switch (provider) {
|
|
2571
2664
|
case "github-copilot":
|
|
@@ -2905,6 +2998,7 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
2905
2998
|
// src/embeddings/providers/ollama.ts
|
|
2906
2999
|
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
|
|
2907
3000
|
static MIN_TRUNCATION_CHARS = 512;
|
|
3001
|
+
static REQUEST_TIMEOUT_MS = 12e4;
|
|
2908
3002
|
constructor(credentials, modelInfo) {
|
|
2909
3003
|
super(credentials, modelInfo);
|
|
2910
3004
|
}
|
|
@@ -2979,22 +3073,45 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
2979
3073
|
}
|
|
2980
3074
|
}
|
|
2981
3075
|
async embedSingle(text) {
|
|
2982
|
-
const
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
3076
|
+
const controller = new AbortController();
|
|
3077
|
+
const timeout = setTimeout(
|
|
3078
|
+
() => controller.abort(),
|
|
3079
|
+
_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS
|
|
3080
|
+
);
|
|
3081
|
+
let response;
|
|
3082
|
+
try {
|
|
3083
|
+
response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
3084
|
+
method: "POST",
|
|
3085
|
+
headers: {
|
|
3086
|
+
"Content-Type": "application/json"
|
|
3087
|
+
},
|
|
3088
|
+
body: JSON.stringify({
|
|
3089
|
+
model: this.modelInfo.model,
|
|
3090
|
+
prompt: text,
|
|
3091
|
+
truncate: false
|
|
3092
|
+
}),
|
|
3093
|
+
signal: controller.signal
|
|
3094
|
+
});
|
|
3095
|
+
} catch (error) {
|
|
3096
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3097
|
+
throw new Error(
|
|
3098
|
+
`Ollama embedding request timed out after ${_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS}ms`
|
|
3099
|
+
);
|
|
3100
|
+
}
|
|
3101
|
+
throw error;
|
|
3102
|
+
} finally {
|
|
3103
|
+
clearTimeout(timeout);
|
|
3104
|
+
}
|
|
2993
3105
|
if (!response.ok) {
|
|
2994
3106
|
const error = (await response.text()).slice(0, 500);
|
|
2995
3107
|
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
2996
3108
|
}
|
|
2997
3109
|
const data = await response.json();
|
|
3110
|
+
if (!Array.isArray(data.embedding) || data.embedding.length !== this.modelInfo.dimensions || data.embedding.some((value) => typeof value !== "number" || !Number.isFinite(value))) {
|
|
3111
|
+
throw new Error(
|
|
3112
|
+
`Ollama returned an invalid embedding; expected ${this.modelInfo.dimensions} finite dimensions`
|
|
3113
|
+
);
|
|
3114
|
+
}
|
|
2998
3115
|
return {
|
|
2999
3116
|
embedding: data.embedding,
|
|
3000
3117
|
tokensUsed: this.estimateTokens(text)
|
|
@@ -3905,7 +4022,9 @@ function mapChunk(c) {
|
|
|
3905
4022
|
return {
|
|
3906
4023
|
content: c.content,
|
|
3907
4024
|
startLine: c.startLine ?? c.start_line,
|
|
4025
|
+
startCol: c.startCol ?? c.start_col,
|
|
3908
4026
|
endLine: c.endLine ?? c.end_line,
|
|
4027
|
+
endCol: c.endCol ?? c.end_col,
|
|
3909
4028
|
chunkType: c.chunkType ?? c.chunk_type,
|
|
3910
4029
|
name: c.name ?? void 0,
|
|
3911
4030
|
language: c.language
|
|
@@ -4026,6 +4145,7 @@ function getEmbeddingHeaderParts(chunk, filePath) {
|
|
|
4026
4145
|
javascript: "JavaScript",
|
|
4027
4146
|
python: "Python",
|
|
4028
4147
|
rust: "Rust",
|
|
4148
|
+
swift: "Swift",
|
|
4029
4149
|
go: "Go",
|
|
4030
4150
|
java: "Java"
|
|
4031
4151
|
};
|
|
@@ -4034,7 +4154,16 @@ function getEmbeddingHeaderParts(chunk, filePath) {
|
|
|
4034
4154
|
function: "function",
|
|
4035
4155
|
arrow_function: "arrow function",
|
|
4036
4156
|
method_definition: "method",
|
|
4157
|
+
method_declaration: "method",
|
|
4158
|
+
protocol_function_declaration: "protocol requirement",
|
|
4159
|
+
init_declaration: "initializer",
|
|
4160
|
+
deinit_declaration: "deinitializer",
|
|
4161
|
+
subscript_declaration: "subscript",
|
|
4037
4162
|
class_declaration: "class",
|
|
4163
|
+
actor_declaration: "actor",
|
|
4164
|
+
extension_declaration: "extension",
|
|
4165
|
+
protocol_declaration: "protocol",
|
|
4166
|
+
struct_declaration: "struct",
|
|
4038
4167
|
interface_declaration: "interface",
|
|
4039
4168
|
type_alias_declaration: "type alias",
|
|
4040
4169
|
enum_declaration: "enum",
|
|
@@ -4728,16 +4857,6 @@ function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
|
4728
4857
|
const fallbackPath = path8.join(mainRepoRoot, relativePath);
|
|
4729
4858
|
return existsSync5(fallbackPath) ? fallbackPath : null;
|
|
4730
4859
|
}
|
|
4731
|
-
function resolveWorktreeFallbackProjectIndexPath(projectRoot, host) {
|
|
4732
|
-
const inheritedHostPath = resolveWorktreeFallbackPath(projectRoot, getProjectIndexRelativePath(host));
|
|
4733
|
-
if (inheritedHostPath) {
|
|
4734
|
-
return inheritedHostPath;
|
|
4735
|
-
}
|
|
4736
|
-
if (host !== "opencode") {
|
|
4737
|
-
return resolveWorktreeFallbackPath(projectRoot, OPENCODE_PROJECT_INDEX_RELATIVE_PATH);
|
|
4738
|
-
}
|
|
4739
|
-
return null;
|
|
4740
|
-
}
|
|
4741
4860
|
function getHostProjectIndexRelativePath(host) {
|
|
4742
4861
|
return getProjectIndexRelativePath(host);
|
|
4743
4862
|
}
|
|
@@ -4839,6 +4958,9 @@ function resolveProjectIndexPath(projectRoot, scope, host = "opencode") {
|
|
|
4839
4958
|
if (hasHostProjectConfig(projectRoot, host)) {
|
|
4840
4959
|
return localIndexPath;
|
|
4841
4960
|
}
|
|
4961
|
+
if (resolveWorktreeMainRepoRoot(projectRoot)) {
|
|
4962
|
+
return localIndexPath;
|
|
4963
|
+
}
|
|
4842
4964
|
const hostFallback = resolveWorktreeFallbackPath(projectRoot, getProjectIndexRelativePath(host));
|
|
4843
4965
|
if (hostFallback) {
|
|
4844
4966
|
return hostFallback;
|
|
@@ -5373,40 +5495,6 @@ function releaseIndexLock(lease) {
|
|
|
5373
5495
|
}
|
|
5374
5496
|
return true;
|
|
5375
5497
|
}
|
|
5376
|
-
async function withIndexLock(indexPath, operation, callback, options = {}) {
|
|
5377
|
-
const lease = acquireIndexLock(indexPath, operation);
|
|
5378
|
-
let result;
|
|
5379
|
-
let callbackError;
|
|
5380
|
-
let callbackFailed = false;
|
|
5381
|
-
try {
|
|
5382
|
-
result = await callback(lease);
|
|
5383
|
-
} catch (error) {
|
|
5384
|
-
callbackFailed = true;
|
|
5385
|
-
callbackError = error;
|
|
5386
|
-
}
|
|
5387
|
-
if (!callbackFailed && options.completeRecoveries !== false) {
|
|
5388
|
-
try {
|
|
5389
|
-
completeLeaseRecovery(lease);
|
|
5390
|
-
} catch (error) {
|
|
5391
|
-
callbackFailed = true;
|
|
5392
|
-
callbackError = error;
|
|
5393
|
-
}
|
|
5394
|
-
}
|
|
5395
|
-
let releaseError;
|
|
5396
|
-
try {
|
|
5397
|
-
if (!releaseIndexLock(lease)) {
|
|
5398
|
-
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
5399
|
-
}
|
|
5400
|
-
} catch (error) {
|
|
5401
|
-
releaseError = error;
|
|
5402
|
-
}
|
|
5403
|
-
if (releaseError !== void 0) {
|
|
5404
|
-
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
5405
|
-
throw releaseError;
|
|
5406
|
-
}
|
|
5407
|
-
if (callbackFailed) throw callbackError;
|
|
5408
|
-
return result;
|
|
5409
|
-
}
|
|
5410
5498
|
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
5411
5499
|
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
5412
5500
|
temporaryCounter += 1;
|
|
@@ -5441,8 +5529,18 @@ function completeLeaseRecovery(lease) {
|
|
|
5441
5529
|
}
|
|
5442
5530
|
|
|
5443
5531
|
// src/indexer/index.ts
|
|
5444
|
-
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab", "bash"]);
|
|
5445
|
-
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
5532
|
+
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "swift", "php", "apex", "zig", "gdscript", "matlab", "bash", "c", "cpp", "metal"]);
|
|
5533
|
+
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex", "php"]);
|
|
5534
|
+
var CALL_GRAPH_RESOLUTION_VERSION = "4";
|
|
5535
|
+
var PHP_FUNCTION_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
5536
|
+
"function_declaration",
|
|
5537
|
+
"function",
|
|
5538
|
+
"function_definition"
|
|
5539
|
+
]);
|
|
5540
|
+
var PHP_CLASS_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
5541
|
+
"class_declaration",
|
|
5542
|
+
"class_definition"
|
|
5543
|
+
]);
|
|
5446
5544
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
5447
5545
|
"function_declaration",
|
|
5448
5546
|
"function",
|
|
@@ -5455,6 +5553,9 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
5455
5553
|
"enum_declaration",
|
|
5456
5554
|
"function_definition",
|
|
5457
5555
|
"class_definition",
|
|
5556
|
+
"class_specifier",
|
|
5557
|
+
"struct_specifier",
|
|
5558
|
+
"namespace_definition",
|
|
5458
5559
|
"decorated_definition",
|
|
5459
5560
|
"method_declaration",
|
|
5460
5561
|
"type_declaration",
|
|
@@ -5470,6 +5571,14 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
5470
5571
|
"test_declaration",
|
|
5471
5572
|
"struct_declaration",
|
|
5472
5573
|
"union_declaration",
|
|
5574
|
+
// Synthetic Swift declarations or declarations specific to tree-sitter-swift.
|
|
5575
|
+
"actor_declaration",
|
|
5576
|
+
"extension_declaration",
|
|
5577
|
+
"protocol_declaration",
|
|
5578
|
+
"protocol_function_declaration",
|
|
5579
|
+
"init_declaration",
|
|
5580
|
+
"deinit_declaration",
|
|
5581
|
+
"subscript_declaration",
|
|
5473
5582
|
// GDScript declarations whose names participate in the call graph.
|
|
5474
5583
|
// `function_definition` and `class_definition` are already in the set
|
|
5475
5584
|
// above (shared with Python/C/Bash and Python, respectively).
|
|
@@ -5479,6 +5588,53 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
5479
5588
|
"const_statement",
|
|
5480
5589
|
"class_name_statement"
|
|
5481
5590
|
]);
|
|
5591
|
+
var C_FAMILY_TYPE_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set(["class_specifier", "struct_specifier"]);
|
|
5592
|
+
function isCompatibleCFamilyCallTarget(language, callType, symbolKind) {
|
|
5593
|
+
if (language !== "c" && language !== "cpp") return true;
|
|
5594
|
+
if (symbolKind === "namespace_definition") return callType === "Import";
|
|
5595
|
+
const isTypeSymbol = C_FAMILY_TYPE_SYMBOL_CHUNK_TYPES.has(symbolKind);
|
|
5596
|
+
if (callType === "Constructor" || callType === "Inherits" || callType === "Implements") {
|
|
5597
|
+
return isTypeSymbol;
|
|
5598
|
+
}
|
|
5599
|
+
return !isTypeSymbol;
|
|
5600
|
+
}
|
|
5601
|
+
var EXECUTABLE_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
5602
|
+
"function_declaration",
|
|
5603
|
+
"function",
|
|
5604
|
+
"arrow_function",
|
|
5605
|
+
"method_definition",
|
|
5606
|
+
"function_definition",
|
|
5607
|
+
"method_declaration",
|
|
5608
|
+
"function_item",
|
|
5609
|
+
"protocol_function_declaration",
|
|
5610
|
+
"init_declaration",
|
|
5611
|
+
"deinit_declaration",
|
|
5612
|
+
"subscript_declaration",
|
|
5613
|
+
"constructor_definition",
|
|
5614
|
+
"trigger_declaration",
|
|
5615
|
+
"test_declaration"
|
|
5616
|
+
]);
|
|
5617
|
+
function findEnclosingSymbol(symbols, line, column) {
|
|
5618
|
+
let best;
|
|
5619
|
+
for (const symbol of symbols) {
|
|
5620
|
+
if (line < symbol.startLine || line > symbol.endLine) continue;
|
|
5621
|
+
if (column !== void 0 && (line === symbol.startLine && column < symbol.startCol || line === symbol.endLine && column >= symbol.endCol)) {
|
|
5622
|
+
continue;
|
|
5623
|
+
}
|
|
5624
|
+
if (!best) {
|
|
5625
|
+
best = symbol;
|
|
5626
|
+
continue;
|
|
5627
|
+
}
|
|
5628
|
+
const span = symbol.endLine - symbol.startLine;
|
|
5629
|
+
const bestSpan = best.endLine - best.startLine;
|
|
5630
|
+
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);
|
|
5631
|
+
const isMoreSpecificTie = span === bestSpan && symbol.startLine === best.startLine && EXECUTABLE_SYMBOL_CHUNK_TYPES.has(symbol.kind) && !EXECUTABLE_SYMBOL_CHUNK_TYPES.has(best.kind);
|
|
5632
|
+
if (span < bestSpan || span === bestSpan && symbol.startLine > best.startLine || isNarrowerPositionRange || isMoreSpecificTie) {
|
|
5633
|
+
best = symbol;
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5636
|
+
return best;
|
|
5637
|
+
}
|
|
5482
5638
|
function float32ArrayToBuffer(arr) {
|
|
5483
5639
|
const float32 = new Float32Array(arr);
|
|
5484
5640
|
return Buffer.from(float32.buffer);
|
|
@@ -5563,6 +5719,8 @@ function hasBlameMetadata(metadata) {
|
|
|
5563
5719
|
}
|
|
5564
5720
|
var INDEX_METADATA_VERSION = "1";
|
|
5565
5721
|
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
5722
|
+
var SWIFT_PARSER_VERSION = "1";
|
|
5723
|
+
var METAL_PARSER_VERSION = "1";
|
|
5566
5724
|
var RANKING_TOKEN_CACHE_LIMIT = 4096;
|
|
5567
5725
|
var RANK_HYBRID_CACHE_LIMIT = 256;
|
|
5568
5726
|
function createPendingChunkStorageText(texts) {
|
|
@@ -5885,15 +6043,25 @@ function chunkTypeBoost(chunkType) {
|
|
|
5885
6043
|
case "function_declaration":
|
|
5886
6044
|
case "method":
|
|
5887
6045
|
case "method_definition":
|
|
6046
|
+
case "method_declaration":
|
|
6047
|
+
case "protocol_function_declaration":
|
|
6048
|
+
case "init_declaration":
|
|
6049
|
+
case "deinit_declaration":
|
|
6050
|
+
case "subscript_declaration":
|
|
5888
6051
|
case "class":
|
|
5889
6052
|
case "class_declaration":
|
|
6053
|
+
case "actor_declaration":
|
|
6054
|
+
case "extension_declaration":
|
|
5890
6055
|
return 0.2;
|
|
5891
6056
|
case "interface":
|
|
5892
6057
|
case "type":
|
|
5893
6058
|
case "enum":
|
|
6059
|
+
case "enum_declaration":
|
|
5894
6060
|
case "struct":
|
|
6061
|
+
case "struct_declaration":
|
|
5895
6062
|
case "impl":
|
|
5896
6063
|
case "trait":
|
|
6064
|
+
case "protocol_declaration":
|
|
5897
6065
|
case "module":
|
|
5898
6066
|
return 0.1;
|
|
5899
6067
|
default:
|
|
@@ -6000,11 +6168,21 @@ function isImplementationChunkType(chunkType) {
|
|
|
6000
6168
|
"function_declaration",
|
|
6001
6169
|
"method",
|
|
6002
6170
|
"method_definition",
|
|
6171
|
+
"method_declaration",
|
|
6172
|
+
"protocol_function_declaration",
|
|
6173
|
+
"init_declaration",
|
|
6174
|
+
"deinit_declaration",
|
|
6175
|
+
"subscript_declaration",
|
|
6003
6176
|
"class",
|
|
6004
6177
|
"class_declaration",
|
|
6178
|
+
"actor_declaration",
|
|
6179
|
+
"extension_declaration",
|
|
6005
6180
|
"interface",
|
|
6181
|
+
"protocol_declaration",
|
|
6006
6182
|
"type",
|
|
6007
6183
|
"enum",
|
|
6184
|
+
"enum_declaration",
|
|
6185
|
+
"struct_declaration",
|
|
6008
6186
|
"module"
|
|
6009
6187
|
].includes(chunkType);
|
|
6010
6188
|
}
|
|
@@ -7007,6 +7185,29 @@ var Indexer = class {
|
|
|
7007
7185
|
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7008
7186
|
return `index.forceReembed.${projectHash}`;
|
|
7009
7187
|
}
|
|
7188
|
+
getCallGraphResolutionMetadataKey() {
|
|
7189
|
+
if (this.config.scope !== "global") {
|
|
7190
|
+
return "index.callGraphResolutionVersion";
|
|
7191
|
+
}
|
|
7192
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7193
|
+
return `index.callGraphResolutionVersion.${projectHash}`;
|
|
7194
|
+
}
|
|
7195
|
+
getSwiftParserVersionMetadataKey() {
|
|
7196
|
+
const key = "index.parser.swiftVersion";
|
|
7197
|
+
if (this.config.scope !== "global") {
|
|
7198
|
+
return key;
|
|
7199
|
+
}
|
|
7200
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7201
|
+
return `${key}.${projectHash}`;
|
|
7202
|
+
}
|
|
7203
|
+
getMetalParserVersionMetadataKey() {
|
|
7204
|
+
const key = "index.parser.metalVersion";
|
|
7205
|
+
if (this.config.scope !== "global") {
|
|
7206
|
+
return key;
|
|
7207
|
+
}
|
|
7208
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7209
|
+
return `${key}.${projectHash}`;
|
|
7210
|
+
}
|
|
7010
7211
|
hasProjectForceReembedPending() {
|
|
7011
7212
|
return this.config.scope === "global" && this.database?.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
7012
7213
|
}
|
|
@@ -8167,6 +8368,7 @@ var Indexer = class {
|
|
|
8167
8368
|
this.database.setMetadata("index.embeddingProvider", provider.provider);
|
|
8168
8369
|
this.database.setMetadata("index.embeddingModel", provider.modelInfo.model);
|
|
8169
8370
|
this.database.setMetadata("index.embeddingDimensions", provider.modelInfo.dimensions.toString());
|
|
8371
|
+
this.database.setMetadata(this.getCallGraphResolutionMetadataKey(), CALL_GRAPH_RESOLUTION_VERSION);
|
|
8170
8372
|
if (this.config.scope === "global") {
|
|
8171
8373
|
if (completeProjectEmbeddingStrategyReset) {
|
|
8172
8374
|
this.database.setMetadata(this.getProjectEmbeddingStrategyMetadataKey(), EMBEDDING_STRATEGY_VERSION);
|
|
@@ -8354,6 +8556,20 @@ var Indexer = class {
|
|
|
8354
8556
|
totalChunks: 0
|
|
8355
8557
|
});
|
|
8356
8558
|
this.loadFileHashCache();
|
|
8559
|
+
const swiftParserMetadataKey = this.getSwiftParserVersionMetadataKey();
|
|
8560
|
+
const reparseCachedSwiftFiles = database.getMetadata(swiftParserMetadataKey) !== SWIFT_PARSER_VERSION;
|
|
8561
|
+
const metalParserMetadataKey = this.getMetalParserVersionMetadataKey();
|
|
8562
|
+
const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
|
|
8563
|
+
if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some(
|
|
8564
|
+
(filePath) => path12.extname(filePath).toLowerCase() === ".swift"
|
|
8565
|
+
)) {
|
|
8566
|
+
this.logger.info("Reindexing cached Swift files for parser support");
|
|
8567
|
+
}
|
|
8568
|
+
if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some(
|
|
8569
|
+
(filePath) => path12.extname(filePath).toLowerCase() === ".metal"
|
|
8570
|
+
)) {
|
|
8571
|
+
this.logger.info("Reindexing cached Metal files for parser support");
|
|
8572
|
+
}
|
|
8357
8573
|
const includePatterns = [...this.config.include, ...this.config.additionalInclude];
|
|
8358
8574
|
const { files, skipped } = await collectFiles(
|
|
8359
8575
|
this.projectRoot,
|
|
@@ -8373,10 +8589,17 @@ var Indexer = class {
|
|
|
8373
8589
|
const changedFiles = [];
|
|
8374
8590
|
const unchangedFilePaths = /* @__PURE__ */ new Set();
|
|
8375
8591
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
8592
|
+
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
8376
8593
|
for (const f of files) {
|
|
8377
8594
|
const currentHash = hashFile(f.path);
|
|
8378
8595
|
currentFileHashes.set(f.path, currentHash);
|
|
8379
|
-
|
|
8596
|
+
const cachedHashMatches = this.fileHashCache.get(f.path) === currentHash;
|
|
8597
|
+
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(f.path).some(
|
|
8598
|
+
(chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
|
|
8599
|
+
);
|
|
8600
|
+
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path12.extname(f.path).toLowerCase() === ".swift";
|
|
8601
|
+
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path12.extname(f.path).toLowerCase() === ".metal";
|
|
8602
|
+
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade) {
|
|
8380
8603
|
unchangedFilePaths.add(f.path);
|
|
8381
8604
|
this.logger.recordCacheHit();
|
|
8382
8605
|
} else {
|
|
@@ -8578,16 +8801,28 @@ var Indexer = class {
|
|
|
8578
8801
|
const fileSymbols = [];
|
|
8579
8802
|
for (const chunk of parsed.chunks) {
|
|
8580
8803
|
if (!chunk.name || !CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(chunk.chunkType)) continue;
|
|
8581
|
-
const
|
|
8804
|
+
const existingMetalSymbol = chunk.language === "metal" ? fileSymbols.find(
|
|
8805
|
+
(symbol2) => symbol2.name === chunk.name && symbol2.kind === chunk.chunkType && symbol2.startLine <= chunk.endLine && chunk.startLine <= symbol2.endLine
|
|
8806
|
+
) : void 0;
|
|
8807
|
+
if (existingMetalSymbol) {
|
|
8808
|
+
existingMetalSymbol.startLine = Math.min(existingMetalSymbol.startLine, chunk.startLine);
|
|
8809
|
+
existingMetalSymbol.endLine = Math.max(existingMetalSymbol.endLine, chunk.endLine);
|
|
8810
|
+
existingMetalSymbol.startCol = Math.min(existingMetalSymbol.startCol, chunk.startCol ?? 0);
|
|
8811
|
+
existingMetalSymbol.endCol = Math.max(existingMetalSymbol.endCol, chunk.endCol ?? 0);
|
|
8812
|
+
continue;
|
|
8813
|
+
}
|
|
8814
|
+
const symbolId = `sym_${hashContent(
|
|
8815
|
+
parsed.path + ":" + chunk.name + ":" + chunk.chunkType + ":" + chunk.startLine + ":" + (chunk.startCol ?? 0)
|
|
8816
|
+
).slice(0, 16)}`;
|
|
8582
8817
|
const symbol = {
|
|
8583
8818
|
id: symbolId,
|
|
8584
8819
|
filePath: parsed.path,
|
|
8585
8820
|
name: chunk.name,
|
|
8586
8821
|
kind: chunk.chunkType,
|
|
8587
8822
|
startLine: chunk.startLine,
|
|
8588
|
-
startCol: 0,
|
|
8823
|
+
startCol: chunk.startCol ?? 0,
|
|
8589
8824
|
endLine: chunk.endLine,
|
|
8590
|
-
endCol: 0,
|
|
8825
|
+
endCol: chunk.endCol ?? 0,
|
|
8591
8826
|
language: chunk.language
|
|
8592
8827
|
};
|
|
8593
8828
|
fileSymbols.push(symbol);
|
|
@@ -8612,8 +8847,10 @@ var Indexer = class {
|
|
|
8612
8847
|
if (callSites.length === 0) continue;
|
|
8613
8848
|
const edges = [];
|
|
8614
8849
|
for (const site of callSites) {
|
|
8615
|
-
const enclosingSymbol =
|
|
8616
|
-
|
|
8850
|
+
const enclosingSymbol = findEnclosingSymbol(
|
|
8851
|
+
fileSymbols,
|
|
8852
|
+
site.line,
|
|
8853
|
+
site.column
|
|
8617
8854
|
);
|
|
8618
8855
|
if (!enclosingSymbol) continue;
|
|
8619
8856
|
const edgeId = `edge_${hashContent(enclosingSymbol.id + ":" + site.calleeName + ":" + site.line + ":" + site.column).slice(0, 16)}`;
|
|
@@ -8632,7 +8869,21 @@ var Indexer = class {
|
|
|
8632
8869
|
if (edges.length > 0) {
|
|
8633
8870
|
database.upsertCallEdgesBatch(edges);
|
|
8634
8871
|
for (const edge of edges) {
|
|
8635
|
-
|
|
8872
|
+
let candidates = symbolsByName.get(normalizeSymbolKey(edge.targetName));
|
|
8873
|
+
if (fileLanguage === "php" && candidates) {
|
|
8874
|
+
if (edge.callType === "Constructor") {
|
|
8875
|
+
candidates = candidates.filter(
|
|
8876
|
+
(candidate) => PHP_CLASS_SYMBOL_CHUNK_TYPES.has(candidate.kind)
|
|
8877
|
+
);
|
|
8878
|
+
} else if (edge.callType === "Call") {
|
|
8879
|
+
candidates = candidates.filter(
|
|
8880
|
+
(candidate) => PHP_FUNCTION_SYMBOL_CHUNK_TYPES.has(candidate.kind)
|
|
8881
|
+
);
|
|
8882
|
+
}
|
|
8883
|
+
}
|
|
8884
|
+
candidates = candidates?.filter(
|
|
8885
|
+
(symbol) => isCompatibleCFamilyCallTarget(fileLanguage, edge.callType, symbol.kind)
|
|
8886
|
+
);
|
|
8636
8887
|
if (candidates && candidates.length === 1) {
|
|
8637
8888
|
database.resolveCallEdge(edge.id, candidates[0].id);
|
|
8638
8889
|
}
|
|
@@ -8687,6 +8938,8 @@ var Indexer = class {
|
|
|
8687
8938
|
this.saveFileHashCache();
|
|
8688
8939
|
this.saveFailedBatches([]);
|
|
8689
8940
|
}
|
|
8941
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
8942
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
8690
8943
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
8691
8944
|
this.indexCompatibility = { compatible: true };
|
|
8692
8945
|
stats.durationMs = Date.now() - startTime;
|
|
@@ -8714,6 +8967,8 @@ var Indexer = class {
|
|
|
8714
8967
|
this.saveFileHashCache();
|
|
8715
8968
|
this.saveFailedBatches([]);
|
|
8716
8969
|
}
|
|
8970
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
8971
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
8717
8972
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
8718
8973
|
this.indexCompatibility = { compatible: true };
|
|
8719
8974
|
stats.durationMs = Date.now() - startTime;
|
|
@@ -8984,6 +9239,8 @@ var Indexer = class {
|
|
|
8984
9239
|
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
8985
9240
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
8986
9241
|
}
|
|
9242
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
9243
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
8987
9244
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
8988
9245
|
this.indexCompatibility = { compatible: true };
|
|
8989
9246
|
this.logger.recordIndexingEnd();
|
|
@@ -10094,251 +10351,45 @@ var Indexer = class {
|
|
|
10094
10351
|
import { existsSync as existsSync10, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
|
|
10095
10352
|
import * as path17 from "path";
|
|
10096
10353
|
|
|
10097
|
-
// src/
|
|
10098
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
10099
|
-
import * as path14 from "path";
|
|
10100
|
-
|
|
10101
|
-
// src/config/rebase.ts
|
|
10354
|
+
// src/tools/knowledge-base-paths.ts
|
|
10102
10355
|
import * as path13 from "path";
|
|
10103
|
-
function
|
|
10104
|
-
const
|
|
10105
|
-
|
|
10106
|
-
|
|
10107
|
-
function rebasePathEntries(values, fromDir, toDir) {
|
|
10108
|
-
if (!Array.isArray(values)) {
|
|
10109
|
-
return [];
|
|
10110
|
-
}
|
|
10111
|
-
return values.filter((value) => typeof value === "string").map((value) => {
|
|
10112
|
-
const trimmed = value.trim();
|
|
10113
|
-
if (!trimmed || path13.isAbsolute(trimmed)) {
|
|
10114
|
-
return trimmed;
|
|
10115
|
-
}
|
|
10116
|
-
return normalizePathSeparators(path13.normalize(path13.relative(toDir, path13.resolve(fromDir, trimmed))));
|
|
10117
|
-
}).filter(Boolean);
|
|
10118
|
-
}
|
|
10119
|
-
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
10120
|
-
if (!Array.isArray(values)) {
|
|
10121
|
-
return [];
|
|
10356
|
+
function resolveConfigPathValue(value, baseDir) {
|
|
10357
|
+
const trimmed = value.trim();
|
|
10358
|
+
if (!trimmed) {
|
|
10359
|
+
return trimmed;
|
|
10122
10360
|
}
|
|
10123
|
-
|
|
10124
|
-
|
|
10125
|
-
if (!trimmed) {
|
|
10126
|
-
return trimmed;
|
|
10127
|
-
}
|
|
10128
|
-
if (path13.isAbsolute(trimmed)) {
|
|
10129
|
-
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
10130
|
-
return normalizePathSeparators(path13.normalize(path13.relative(sourceRoot, trimmed) || "."));
|
|
10131
|
-
}
|
|
10132
|
-
return path13.normalize(trimmed);
|
|
10133
|
-
}
|
|
10134
|
-
const resolvedFromSource = path13.resolve(sourceRoot, trimmed);
|
|
10135
|
-
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
10136
|
-
return normalizePathSeparators(path13.normalize(trimmed));
|
|
10137
|
-
}
|
|
10138
|
-
return normalizePathSeparators(path13.normalize(path13.relative(targetRoot, resolvedFromSource)));
|
|
10139
|
-
}).filter(Boolean);
|
|
10361
|
+
const absolutePath = path13.isAbsolute(trimmed) ? trimmed : path13.resolve(baseDir, trimmed);
|
|
10362
|
+
return path13.normalize(absolutePath);
|
|
10140
10363
|
}
|
|
10141
10364
|
|
|
10142
|
-
// src/
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
"search",
|
|
10152
|
-
"debug",
|
|
10153
|
-
"scope"
|
|
10154
|
-
];
|
|
10155
|
-
var MERGE_ARRAY_KEYS = ["knowledgeBases", "additionalInclude"];
|
|
10156
|
-
function isRecord(value) {
|
|
10157
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10158
|
-
}
|
|
10159
|
-
function isStringArray2(value) {
|
|
10160
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
10161
|
-
}
|
|
10162
|
-
function applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key) {
|
|
10163
|
-
if (key in normalizedProjectConfig) {
|
|
10164
|
-
merged[key] = normalizedProjectConfig[key];
|
|
10165
|
-
return;
|
|
10166
|
-
}
|
|
10167
|
-
if (key in globalConfig) {
|
|
10168
|
-
merged[key] = globalConfig[key];
|
|
10365
|
+
// src/tools/utils.ts
|
|
10366
|
+
import { get_encoding } from "tiktoken";
|
|
10367
|
+
var MIN_CONTEXT_PACK_TOKEN_BUDGET = 128;
|
|
10368
|
+
var MAX_CONTEXT_PACK_TOKEN_BUDGET = 4e3;
|
|
10369
|
+
var DEFAULT_CONTEXT_PACK_TOKEN_BUDGET = 1200;
|
|
10370
|
+
var CONTEXT_TOKENIZER = get_encoding("cl100k_base");
|
|
10371
|
+
function clampContextPackTokenBudget(tokenBudget) {
|
|
10372
|
+
if (tokenBudget === void 0 || !Number.isFinite(tokenBudget)) {
|
|
10373
|
+
return DEFAULT_CONTEXT_PACK_TOKEN_BUDGET;
|
|
10169
10374
|
}
|
|
10375
|
+
return Math.min(
|
|
10376
|
+
MAX_CONTEXT_PACK_TOKEN_BUDGET,
|
|
10377
|
+
Math.max(MIN_CONTEXT_PACK_TOKEN_BUDGET, Math.floor(tokenBudget))
|
|
10378
|
+
);
|
|
10170
10379
|
}
|
|
10171
|
-
function
|
|
10172
|
-
return
|
|
10380
|
+
function countContextTokens(text) {
|
|
10381
|
+
return CONTEXT_TOKENIZER.encode(text).length;
|
|
10173
10382
|
}
|
|
10174
|
-
function
|
|
10175
|
-
|
|
10176
|
-
const
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
}
|
|
10185
|
-
function validateConfigLayerShape(rawConfig, filePath) {
|
|
10186
|
-
if (!isRecord(rawConfig)) {
|
|
10187
|
-
throw new Error(`Config file ${filePath} must contain a JSON object at the root.`);
|
|
10188
|
-
}
|
|
10189
|
-
if (rawConfig.knowledgeBases !== void 0 && !isStringArray2(rawConfig.knowledgeBases)) {
|
|
10190
|
-
throw new Error(`Config file ${filePath} field 'knowledgeBases' must be an array of strings.`);
|
|
10191
|
-
}
|
|
10192
|
-
if (rawConfig.additionalInclude !== void 0 && !isStringArray2(rawConfig.additionalInclude)) {
|
|
10193
|
-
throw new Error(`Config file ${filePath} field 'additionalInclude' must be an array of strings.`);
|
|
10194
|
-
}
|
|
10195
|
-
if (rawConfig.include !== void 0 && !isStringArray2(rawConfig.include)) {
|
|
10196
|
-
throw new Error(`Config file ${filePath} field 'include' must be an array of strings.`);
|
|
10197
|
-
}
|
|
10198
|
-
if (rawConfig.exclude !== void 0 && !isStringArray2(rawConfig.exclude)) {
|
|
10199
|
-
throw new Error(`Config file ${filePath} field 'exclude' must be an array of strings.`);
|
|
10200
|
-
}
|
|
10201
|
-
for (const section of ["customProvider", "indexing", "search", "debug", "reranker"]) {
|
|
10202
|
-
const value = rawConfig[section];
|
|
10203
|
-
if (value !== void 0 && !isRecord(value)) {
|
|
10204
|
-
throw new Error(`Config file ${filePath} field '${section}' must be an object.`);
|
|
10205
|
-
}
|
|
10206
|
-
}
|
|
10207
|
-
return rawConfig;
|
|
10208
|
-
}
|
|
10209
|
-
function loadJsonFile(filePath) {
|
|
10210
|
-
if (!existsSync8(filePath)) {
|
|
10211
|
-
return null;
|
|
10212
|
-
}
|
|
10213
|
-
try {
|
|
10214
|
-
const content = readFileSync8(filePath, "utf-8");
|
|
10215
|
-
return validateConfigLayerShape(JSON.parse(content), filePath);
|
|
10216
|
-
} catch (error) {
|
|
10217
|
-
if (error instanceof Error && error.message.startsWith("Config file ")) {
|
|
10218
|
-
throw error;
|
|
10219
|
-
}
|
|
10220
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
10221
|
-
throw new Error(`Failed to load config file ${filePath}: ${message}`);
|
|
10222
|
-
}
|
|
10223
|
-
}
|
|
10224
|
-
function loadConfigFile(filePath) {
|
|
10225
|
-
return loadJsonFile(filePath);
|
|
10226
|
-
}
|
|
10227
|
-
function materializeLocalProjectConfig(projectRoot, config, host = "opencode") {
|
|
10228
|
-
const localConfigPath = resolveWritableProjectConfigPath(projectRoot, host);
|
|
10229
|
-
mkdirSync4(path14.dirname(localConfigPath), { recursive: true });
|
|
10230
|
-
writeFileSync4(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
10231
|
-
return localConfigPath;
|
|
10232
|
-
}
|
|
10233
|
-
function loadProjectConfigLayer(projectRoot, host = "opencode") {
|
|
10234
|
-
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
10235
|
-
const projectConfig = loadJsonFile(projectConfigPath);
|
|
10236
|
-
if (!projectConfig) {
|
|
10237
|
-
return {};
|
|
10238
|
-
}
|
|
10239
|
-
const normalizedConfig = { ...projectConfig };
|
|
10240
|
-
const projectConfigBaseDir = path14.dirname(path14.dirname(projectConfigPath));
|
|
10241
|
-
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
10242
|
-
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
10243
|
-
normalizedConfig.knowledgeBases,
|
|
10244
|
-
projectConfigBaseDir,
|
|
10245
|
-
projectRoot
|
|
10246
|
-
);
|
|
10247
|
-
}
|
|
10248
|
-
return normalizedConfig;
|
|
10249
|
-
}
|
|
10250
|
-
function loadMergedConfig(projectRoot, host = "opencode") {
|
|
10251
|
-
const globalConfigPath = resolveGlobalConfigPath(host);
|
|
10252
|
-
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
10253
|
-
let globalConfig = null;
|
|
10254
|
-
let globalConfigError = null;
|
|
10255
|
-
try {
|
|
10256
|
-
globalConfig = loadJsonFile(globalConfigPath);
|
|
10257
|
-
} catch (error) {
|
|
10258
|
-
globalConfigError = error instanceof Error ? error : new Error(String(error));
|
|
10259
|
-
}
|
|
10260
|
-
const projectConfig = loadJsonFile(projectConfigPath);
|
|
10261
|
-
const normalizedProjectConfig = loadProjectConfigLayer(projectRoot, host);
|
|
10262
|
-
if (globalConfigError) {
|
|
10263
|
-
if (!projectConfig) {
|
|
10264
|
-
throw globalConfigError;
|
|
10265
|
-
}
|
|
10266
|
-
globalConfig = null;
|
|
10267
|
-
}
|
|
10268
|
-
if (!globalConfig && !projectConfig) {
|
|
10269
|
-
return {};
|
|
10270
|
-
}
|
|
10271
|
-
if (!projectConfig && globalConfig) {
|
|
10272
|
-
return globalConfig;
|
|
10273
|
-
}
|
|
10274
|
-
if (!globalConfig && projectConfig) {
|
|
10275
|
-
return normalizedProjectConfig;
|
|
10276
|
-
}
|
|
10277
|
-
if (!globalConfig || !projectConfig) {
|
|
10278
|
-
return globalConfig ?? normalizedProjectConfig;
|
|
10279
|
-
}
|
|
10280
|
-
const merged = { ...globalConfig };
|
|
10281
|
-
for (const key of PROJECT_OVERRIDE_KEYS) {
|
|
10282
|
-
applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key);
|
|
10283
|
-
}
|
|
10284
|
-
if (projectConfig) {
|
|
10285
|
-
for (const key of Object.keys(projectConfig)) {
|
|
10286
|
-
if (PROJECT_OVERRIDE_KEYS.includes(key) || MERGE_ARRAY_KEYS.includes(key)) {
|
|
10287
|
-
continue;
|
|
10288
|
-
}
|
|
10289
|
-
merged[key] = normalizedProjectConfig[key];
|
|
10290
|
-
}
|
|
10291
|
-
}
|
|
10292
|
-
const globalKbs = globalConfig && Array.isArray(globalConfig.knowledgeBases) ? globalConfig.knowledgeBases : [];
|
|
10293
|
-
const projectKbs = projectConfig ? Array.isArray(normalizedProjectConfig.knowledgeBases) ? normalizedProjectConfig.knowledgeBases : [] : [];
|
|
10294
|
-
const allKbs = [...globalKbs, ...projectKbs];
|
|
10295
|
-
merged.knowledgeBases = mergeKnowledgeBasePaths(allKbs);
|
|
10296
|
-
const globalAdditional = globalConfig && Array.isArray(globalConfig.additionalInclude) ? globalConfig.additionalInclude : [];
|
|
10297
|
-
const projectAdditional = projectConfig && Array.isArray(projectConfig.additionalInclude) ? projectConfig.additionalInclude : [];
|
|
10298
|
-
const allAdditional = [...globalAdditional, ...projectAdditional];
|
|
10299
|
-
merged.additionalInclude = mergeUniqueStringArray(allAdditional);
|
|
10300
|
-
return merged;
|
|
10301
|
-
}
|
|
10302
|
-
|
|
10303
|
-
// src/tools/knowledge-base-paths.ts
|
|
10304
|
-
import * as path15 from "path";
|
|
10305
|
-
function resolveConfigPathValue(value, baseDir) {
|
|
10306
|
-
const trimmed = value.trim();
|
|
10307
|
-
if (!trimmed) {
|
|
10308
|
-
return trimmed;
|
|
10309
|
-
}
|
|
10310
|
-
const absolutePath = path15.isAbsolute(trimmed) ? trimmed : path15.resolve(baseDir, trimmed);
|
|
10311
|
-
return path15.normalize(absolutePath);
|
|
10312
|
-
}
|
|
10313
|
-
|
|
10314
|
-
// src/tools/utils.ts
|
|
10315
|
-
import { get_encoding } from "tiktoken";
|
|
10316
|
-
var MIN_CONTEXT_PACK_TOKEN_BUDGET = 128;
|
|
10317
|
-
var MAX_CONTEXT_PACK_TOKEN_BUDGET = 4e3;
|
|
10318
|
-
var DEFAULT_CONTEXT_PACK_TOKEN_BUDGET = 1200;
|
|
10319
|
-
var CONTEXT_TOKENIZER = get_encoding("cl100k_base");
|
|
10320
|
-
function clampContextPackTokenBudget(tokenBudget) {
|
|
10321
|
-
if (tokenBudget === void 0 || !Number.isFinite(tokenBudget)) {
|
|
10322
|
-
return DEFAULT_CONTEXT_PACK_TOKEN_BUDGET;
|
|
10323
|
-
}
|
|
10324
|
-
return Math.min(
|
|
10325
|
-
MAX_CONTEXT_PACK_TOKEN_BUDGET,
|
|
10326
|
-
Math.max(MIN_CONTEXT_PACK_TOKEN_BUDGET, Math.floor(tokenBudget))
|
|
10327
|
-
);
|
|
10328
|
-
}
|
|
10329
|
-
function countContextTokens(text) {
|
|
10330
|
-
return CONTEXT_TOKENIZER.encode(text).length;
|
|
10331
|
-
}
|
|
10332
|
-
function fitTextToContextBudget(text, tokenBudget) {
|
|
10333
|
-
const normalizedBudget = clampContextPackTokenBudget(tokenBudget);
|
|
10334
|
-
const tokenEstimate = countContextTokens(text);
|
|
10335
|
-
if (tokenEstimate <= normalizedBudget) {
|
|
10336
|
-
return {
|
|
10337
|
-
text,
|
|
10338
|
-
tokenBudget: normalizedBudget,
|
|
10339
|
-
tokenEstimate,
|
|
10340
|
-
truncated: false
|
|
10341
|
-
};
|
|
10383
|
+
function fitTextToContextBudget(text, tokenBudget) {
|
|
10384
|
+
const normalizedBudget = clampContextPackTokenBudget(tokenBudget);
|
|
10385
|
+
const tokenEstimate = countContextTokens(text);
|
|
10386
|
+
if (tokenEstimate <= normalizedBudget) {
|
|
10387
|
+
return {
|
|
10388
|
+
text,
|
|
10389
|
+
tokenBudget: normalizedBudget,
|
|
10390
|
+
tokenEstimate,
|
|
10391
|
+
truncated: false
|
|
10392
|
+
};
|
|
10342
10393
|
}
|
|
10343
10394
|
const suffix = "\n...[truncated to context token budget]";
|
|
10344
10395
|
const codePoints = Array.from(text);
|
|
@@ -10718,8 +10769,210 @@ ${truncateContent(r.content)}
|
|
|
10718
10769
|
}
|
|
10719
10770
|
|
|
10720
10771
|
// src/tools/config-state.ts
|
|
10721
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
10772
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
10722
10773
|
import * as path16 from "path";
|
|
10774
|
+
|
|
10775
|
+
// src/config/merger.ts
|
|
10776
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
10777
|
+
import * as path15 from "path";
|
|
10778
|
+
|
|
10779
|
+
// src/config/rebase.ts
|
|
10780
|
+
import * as path14 from "path";
|
|
10781
|
+
function isWithinRoot(rootDir, targetPath) {
|
|
10782
|
+
const relativePath = path14.relative(rootDir, targetPath);
|
|
10783
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path14.isAbsolute(relativePath);
|
|
10784
|
+
}
|
|
10785
|
+
function rebasePathEntries(values, fromDir, toDir) {
|
|
10786
|
+
if (!Array.isArray(values)) {
|
|
10787
|
+
return [];
|
|
10788
|
+
}
|
|
10789
|
+
return values.filter((value) => typeof value === "string").map((value) => {
|
|
10790
|
+
const trimmed = value.trim();
|
|
10791
|
+
if (!trimmed || path14.isAbsolute(trimmed)) {
|
|
10792
|
+
return trimmed;
|
|
10793
|
+
}
|
|
10794
|
+
return normalizePathSeparators(path14.normalize(path14.relative(toDir, path14.resolve(fromDir, trimmed))));
|
|
10795
|
+
}).filter(Boolean);
|
|
10796
|
+
}
|
|
10797
|
+
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
10798
|
+
if (!Array.isArray(values)) {
|
|
10799
|
+
return [];
|
|
10800
|
+
}
|
|
10801
|
+
return values.filter((value) => typeof value === "string").map((value) => {
|
|
10802
|
+
const trimmed = value.trim();
|
|
10803
|
+
if (!trimmed) {
|
|
10804
|
+
return trimmed;
|
|
10805
|
+
}
|
|
10806
|
+
if (path14.isAbsolute(trimmed)) {
|
|
10807
|
+
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
10808
|
+
return normalizePathSeparators(path14.normalize(path14.relative(sourceRoot, trimmed) || "."));
|
|
10809
|
+
}
|
|
10810
|
+
return path14.normalize(trimmed);
|
|
10811
|
+
}
|
|
10812
|
+
const resolvedFromSource = path14.resolve(sourceRoot, trimmed);
|
|
10813
|
+
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
10814
|
+
return normalizePathSeparators(path14.normalize(trimmed));
|
|
10815
|
+
}
|
|
10816
|
+
return normalizePathSeparators(path14.normalize(path14.relative(targetRoot, resolvedFromSource)));
|
|
10817
|
+
}).filter(Boolean);
|
|
10818
|
+
}
|
|
10819
|
+
|
|
10820
|
+
// src/config/merger.ts
|
|
10821
|
+
var PROJECT_OVERRIDE_KEYS = [
|
|
10822
|
+
"embeddingProvider",
|
|
10823
|
+
"customProvider",
|
|
10824
|
+
"embeddingModel",
|
|
10825
|
+
"reranker",
|
|
10826
|
+
"include",
|
|
10827
|
+
"exclude",
|
|
10828
|
+
"indexing",
|
|
10829
|
+
"search",
|
|
10830
|
+
"debug",
|
|
10831
|
+
"scope"
|
|
10832
|
+
];
|
|
10833
|
+
var MERGE_ARRAY_KEYS = ["knowledgeBases", "additionalInclude"];
|
|
10834
|
+
function isRecord(value) {
|
|
10835
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10836
|
+
}
|
|
10837
|
+
function isStringArray2(value) {
|
|
10838
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
10839
|
+
}
|
|
10840
|
+
function applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key) {
|
|
10841
|
+
if (key in normalizedProjectConfig) {
|
|
10842
|
+
merged[key] = normalizedProjectConfig[key];
|
|
10843
|
+
return;
|
|
10844
|
+
}
|
|
10845
|
+
if (key in globalConfig) {
|
|
10846
|
+
merged[key] = globalConfig[key];
|
|
10847
|
+
}
|
|
10848
|
+
}
|
|
10849
|
+
function mergeUniqueStringArray(values) {
|
|
10850
|
+
return [...new Set(values.map((value) => String(value).trim()))];
|
|
10851
|
+
}
|
|
10852
|
+
function normalizeKnowledgeBasePath(value) {
|
|
10853
|
+
let normalized = path15.normalize(String(value).trim());
|
|
10854
|
+
const root = path15.parse(normalized).root;
|
|
10855
|
+
while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
|
|
10856
|
+
normalized = normalized.slice(0, -1);
|
|
10857
|
+
}
|
|
10858
|
+
return normalized;
|
|
10859
|
+
}
|
|
10860
|
+
function mergeKnowledgeBasePaths(values) {
|
|
10861
|
+
return [...new Set(values.map((value) => normalizeKnowledgeBasePath(value)).filter((value) => value.length > 0))];
|
|
10862
|
+
}
|
|
10863
|
+
function validateConfigLayerShape(rawConfig, filePath) {
|
|
10864
|
+
if (!isRecord(rawConfig)) {
|
|
10865
|
+
throw new Error(`Config file ${filePath} must contain a JSON object at the root.`);
|
|
10866
|
+
}
|
|
10867
|
+
if (rawConfig.knowledgeBases !== void 0 && !isStringArray2(rawConfig.knowledgeBases)) {
|
|
10868
|
+
throw new Error(`Config file ${filePath} field 'knowledgeBases' must be an array of strings.`);
|
|
10869
|
+
}
|
|
10870
|
+
if (rawConfig.additionalInclude !== void 0 && !isStringArray2(rawConfig.additionalInclude)) {
|
|
10871
|
+
throw new Error(`Config file ${filePath} field 'additionalInclude' must be an array of strings.`);
|
|
10872
|
+
}
|
|
10873
|
+
if (rawConfig.include !== void 0 && !isStringArray2(rawConfig.include)) {
|
|
10874
|
+
throw new Error(`Config file ${filePath} field 'include' must be an array of strings.`);
|
|
10875
|
+
}
|
|
10876
|
+
if (rawConfig.exclude !== void 0 && !isStringArray2(rawConfig.exclude)) {
|
|
10877
|
+
throw new Error(`Config file ${filePath} field 'exclude' must be an array of strings.`);
|
|
10878
|
+
}
|
|
10879
|
+
for (const section of ["customProvider", "indexing", "search", "debug", "reranker"]) {
|
|
10880
|
+
const value = rawConfig[section];
|
|
10881
|
+
if (value !== void 0 && !isRecord(value)) {
|
|
10882
|
+
throw new Error(`Config file ${filePath} field '${section}' must be an object.`);
|
|
10883
|
+
}
|
|
10884
|
+
}
|
|
10885
|
+
return rawConfig;
|
|
10886
|
+
}
|
|
10887
|
+
function loadJsonFile(filePath) {
|
|
10888
|
+
if (!existsSync8(filePath)) {
|
|
10889
|
+
return null;
|
|
10890
|
+
}
|
|
10891
|
+
try {
|
|
10892
|
+
const content = readFileSync8(filePath, "utf-8");
|
|
10893
|
+
return validateConfigLayerShape(JSON.parse(content), filePath);
|
|
10894
|
+
} catch (error) {
|
|
10895
|
+
if (error instanceof Error && error.message.startsWith("Config file ")) {
|
|
10896
|
+
throw error;
|
|
10897
|
+
}
|
|
10898
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10899
|
+
throw new Error(`Failed to load config file ${filePath}: ${message}`);
|
|
10900
|
+
}
|
|
10901
|
+
}
|
|
10902
|
+
function loadConfigFile(filePath) {
|
|
10903
|
+
return loadJsonFile(filePath);
|
|
10904
|
+
}
|
|
10905
|
+
function loadProjectConfigLayer(projectRoot, host = "opencode") {
|
|
10906
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
10907
|
+
const projectConfig = loadJsonFile(projectConfigPath);
|
|
10908
|
+
if (!projectConfig) {
|
|
10909
|
+
return {};
|
|
10910
|
+
}
|
|
10911
|
+
const normalizedConfig = { ...projectConfig };
|
|
10912
|
+
const projectConfigBaseDir = path15.dirname(path15.dirname(projectConfigPath));
|
|
10913
|
+
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
10914
|
+
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
10915
|
+
normalizedConfig.knowledgeBases,
|
|
10916
|
+
projectConfigBaseDir,
|
|
10917
|
+
projectRoot
|
|
10918
|
+
);
|
|
10919
|
+
}
|
|
10920
|
+
return normalizedConfig;
|
|
10921
|
+
}
|
|
10922
|
+
function loadMergedConfig(projectRoot, host = "opencode") {
|
|
10923
|
+
const globalConfigPath = resolveGlobalConfigPath(host);
|
|
10924
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
|
|
10925
|
+
let globalConfig = null;
|
|
10926
|
+
let globalConfigError = null;
|
|
10927
|
+
try {
|
|
10928
|
+
globalConfig = loadJsonFile(globalConfigPath);
|
|
10929
|
+
} catch (error) {
|
|
10930
|
+
globalConfigError = error instanceof Error ? error : new Error(String(error));
|
|
10931
|
+
}
|
|
10932
|
+
const projectConfig = loadJsonFile(projectConfigPath);
|
|
10933
|
+
const normalizedProjectConfig = loadProjectConfigLayer(projectRoot, host);
|
|
10934
|
+
if (globalConfigError) {
|
|
10935
|
+
if (!projectConfig) {
|
|
10936
|
+
throw globalConfigError;
|
|
10937
|
+
}
|
|
10938
|
+
globalConfig = null;
|
|
10939
|
+
}
|
|
10940
|
+
if (!globalConfig && !projectConfig) {
|
|
10941
|
+
return {};
|
|
10942
|
+
}
|
|
10943
|
+
if (!projectConfig && globalConfig) {
|
|
10944
|
+
return globalConfig;
|
|
10945
|
+
}
|
|
10946
|
+
if (!globalConfig && projectConfig) {
|
|
10947
|
+
return normalizedProjectConfig;
|
|
10948
|
+
}
|
|
10949
|
+
if (!globalConfig || !projectConfig) {
|
|
10950
|
+
return globalConfig ?? normalizedProjectConfig;
|
|
10951
|
+
}
|
|
10952
|
+
const merged = { ...globalConfig };
|
|
10953
|
+
for (const key of PROJECT_OVERRIDE_KEYS) {
|
|
10954
|
+
applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key);
|
|
10955
|
+
}
|
|
10956
|
+
if (projectConfig) {
|
|
10957
|
+
for (const key of Object.keys(projectConfig)) {
|
|
10958
|
+
if (PROJECT_OVERRIDE_KEYS.includes(key) || MERGE_ARRAY_KEYS.includes(key)) {
|
|
10959
|
+
continue;
|
|
10960
|
+
}
|
|
10961
|
+
merged[key] = normalizedProjectConfig[key];
|
|
10962
|
+
}
|
|
10963
|
+
}
|
|
10964
|
+
const globalKbs = globalConfig && Array.isArray(globalConfig.knowledgeBases) ? globalConfig.knowledgeBases : [];
|
|
10965
|
+
const projectKbs = projectConfig ? Array.isArray(normalizedProjectConfig.knowledgeBases) ? normalizedProjectConfig.knowledgeBases : [] : [];
|
|
10966
|
+
const allKbs = [...globalKbs, ...projectKbs];
|
|
10967
|
+
merged.knowledgeBases = mergeKnowledgeBasePaths(allKbs);
|
|
10968
|
+
const globalAdditional = globalConfig && Array.isArray(globalConfig.additionalInclude) ? globalConfig.additionalInclude : [];
|
|
10969
|
+
const projectAdditional = projectConfig && Array.isArray(projectConfig.additionalInclude) ? projectConfig.additionalInclude : [];
|
|
10970
|
+
const allAdditional = [...globalAdditional, ...projectAdditional];
|
|
10971
|
+
merged.additionalInclude = mergeUniqueStringArray(allAdditional);
|
|
10972
|
+
return merged;
|
|
10973
|
+
}
|
|
10974
|
+
|
|
10975
|
+
// src/tools/config-state.ts
|
|
10723
10976
|
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
10724
10977
|
const normalized = { ...config };
|
|
10725
10978
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
@@ -10803,15 +11056,6 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
|
|
|
10803
11056
|
indexerCache.set(key, indexer);
|
|
10804
11057
|
configCache.set(key, config);
|
|
10805
11058
|
}
|
|
10806
|
-
function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
|
|
10807
|
-
const root = getProjectRoot(projectRoot, host);
|
|
10808
|
-
const localIndexPath = path17.join(root, getHostProjectIndexRelativePath(host));
|
|
10809
|
-
if (existsSync10(localIndexPath)) {
|
|
10810
|
-
return false;
|
|
10811
|
-
}
|
|
10812
|
-
const inheritedIndexPath = resolveWorktreeFallbackProjectIndexPath(root, host);
|
|
10813
|
-
return inheritedIndexPath !== null;
|
|
10814
|
-
}
|
|
10815
11059
|
async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
10816
11060
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
10817
11061
|
return indexer.search(query, options.limit, {
|
|
@@ -10861,9 +11105,7 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth) {
|
|
|
10861
11105
|
}
|
|
10862
11106
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
10863
11107
|
const root = getProjectRoot(projectRoot, host);
|
|
10864
|
-
const
|
|
10865
|
-
let indexer = getIndexerForProject(root, host);
|
|
10866
|
-
const runtimeConfig = configCache.get(key);
|
|
11108
|
+
const indexer = getIndexerForProject(root, host);
|
|
10867
11109
|
try {
|
|
10868
11110
|
if (args.estimateOnly) {
|
|
10869
11111
|
return { kind: "estimate", estimate: await indexer.estimateCost() };
|
|
@@ -10884,19 +11126,7 @@ async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
|
10884
11126
|
return Promise.resolve();
|
|
10885
11127
|
});
|
|
10886
11128
|
};
|
|
10887
|
-
|
|
10888
|
-
if (args.force && shouldForceLocalizeProjectIndex(root, host)) {
|
|
10889
|
-
const inheritedIndexPath = resolveProjectIndexPath(root, runtimeConfig.scope, host);
|
|
10890
|
-
stats = await withIndexLock(inheritedIndexPath, "force-index", async () => {
|
|
10891
|
-
materializeLocalProjectConfig(root, loadProjectConfigLayer(root, host), host);
|
|
10892
|
-
refreshIndexerForDirectory(root, host, runtimeConfig);
|
|
10893
|
-
indexer = getIndexerForProject(root, host);
|
|
10894
|
-
return runIndex(indexer);
|
|
10895
|
-
}, { completeRecoveries: false });
|
|
10896
|
-
} else {
|
|
10897
|
-
stats = await runIndex(indexer);
|
|
10898
|
-
}
|
|
10899
|
-
return { kind: "stats", stats };
|
|
11129
|
+
return { kind: "stats", stats: await runIndex(indexer) };
|
|
10900
11130
|
} catch (error) {
|
|
10901
11131
|
const busyResult = getIndexBusyResult(error);
|
|
10902
11132
|
if (!busyResult) throw error;
|
|
@@ -11554,7 +11784,7 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
11554
11784
|
}
|
|
11555
11785
|
|
|
11556
11786
|
// src/eval/runner-config.ts
|
|
11557
|
-
import { existsSync as existsSync11, mkdirSync as
|
|
11787
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
11558
11788
|
import * as os5 from "os";
|
|
11559
11789
|
import * as path18 from "path";
|
|
11560
11790
|
function isRecord2(value) {
|
|
@@ -11676,8 +11906,8 @@ function ensureLocalEvalProjectConfig(projectRoot, configPath) {
|
|
|
11676
11906
|
projectRoot,
|
|
11677
11907
|
resolvedConfigPath
|
|
11678
11908
|
);
|
|
11679
|
-
|
|
11680
|
-
|
|
11909
|
+
mkdirSync5(path18.dirname(localConfigPath), { recursive: true });
|
|
11910
|
+
writeFileSync5(localConfigPath, JSON.stringify(sourceConfig, null, 2), "utf-8");
|
|
11681
11911
|
return localConfigPath;
|
|
11682
11912
|
}
|
|
11683
11913
|
function loadParsedConfig(projectRoot, configPath) {
|
|
@@ -15790,7 +16020,7 @@ async function handleVisualizeCommand(argv, cwd) {
|
|
|
15790
16020
|
return 1;
|
|
15791
16021
|
}
|
|
15792
16022
|
const outputPath = path26.join(os6.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
15793
|
-
|
|
16023
|
+
writeFileSync6(outputPath, generateVisualizationHtml(vizData), "utf-8");
|
|
15794
16024
|
console.log(`Temporal call graph visualization generated: ${outputPath}`);
|
|
15795
16025
|
console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
|
|
15796
16026
|
console.log(`Recent change lenses: ${vizData.changes?.length ?? 0}`);
|