opencode-codebase-index 0.17.1 → 0.18.1

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/dist/index.js CHANGED
@@ -670,7 +670,8 @@ var DEFAULT_INCLUDE = [
670
670
  "**/*.{sh,bash,zsh}",
671
671
  "**/*.{txt,html,htm}",
672
672
  "**/*.zig",
673
- "**/*.gd"
673
+ "**/*.gd",
674
+ "**/*.metal"
674
675
  ];
675
676
  var DEFAULT_EXCLUDE = [
676
677
  "**/node_modules/**",
@@ -861,6 +862,9 @@ function isValidProvider(value) {
861
862
  return typeof value === "string" && Object.keys(EMBEDDING_MODELS).includes(value);
862
863
  }
863
864
  function isValidModel(value, provider) {
865
+ if (typeof value === "string" && provider === "ollama" && value.trim().length > 0) {
866
+ return true;
867
+ }
864
868
  return typeof value === "string" && Object.keys(EMBEDDING_MODELS[provider]).includes(value);
865
869
  }
866
870
  function isValidScope(value) {
@@ -1043,7 +1047,7 @@ var autoDetectProviders = AUTO_DETECT_PROVIDER_ORDER.filter(
1043
1047
  );
1044
1048
 
1045
1049
  // src/config/merger.ts
1046
- import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
1050
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
1047
1051
  import * as path6 from "path";
1048
1052
 
1049
1053
  // src/config/paths.ts
@@ -1225,16 +1229,6 @@ function resolveWorktreeFallbackPath(projectRoot, relativePath) {
1225
1229
  const fallbackPath = path3.join(mainRepoRoot, relativePath);
1226
1230
  return existsSync3(fallbackPath) ? fallbackPath : null;
1227
1231
  }
1228
- function resolveWorktreeFallbackProjectIndexPath(projectRoot, host) {
1229
- const inheritedHostPath = resolveWorktreeFallbackPath(projectRoot, getProjectIndexRelativePath(host));
1230
- if (inheritedHostPath) {
1231
- return inheritedHostPath;
1232
- }
1233
- if (host !== "opencode") {
1234
- return resolveWorktreeFallbackPath(projectRoot, OPENCODE_PROJECT_INDEX_RELATIVE_PATH);
1235
- }
1236
- return null;
1237
- }
1238
1232
  function getHostProjectConfigRelativePath(host) {
1239
1233
  return getProjectConfigRelativePath(host);
1240
1234
  }
@@ -1339,6 +1333,9 @@ function resolveProjectIndexPath(projectRoot, scope, host = "opencode") {
1339
1333
  if (hasHostProjectConfig(projectRoot, host)) {
1340
1334
  return localIndexPath;
1341
1335
  }
1336
+ if (resolveWorktreeMainRepoRoot(projectRoot)) {
1337
+ return localIndexPath;
1338
+ }
1342
1339
  const hostFallback = resolveWorktreeFallbackPath(projectRoot, getProjectIndexRelativePath(host));
1343
1340
  if (hostFallback) {
1344
1341
  return hostFallback;
@@ -1511,12 +1508,6 @@ function loadJsonFile(filePath) {
1511
1508
  function loadConfigFile(filePath) {
1512
1509
  return loadJsonFile(filePath);
1513
1510
  }
1514
- function materializeLocalProjectConfig(projectRoot, config, host = "opencode") {
1515
- const localConfigPath = resolveWritableProjectConfigPath(projectRoot, host);
1516
- mkdirSync(path6.dirname(localConfigPath), { recursive: true });
1517
- writeFileSync(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
1518
- return localConfigPath;
1519
- }
1520
1511
  function loadProjectConfigLayer(projectRoot, host = "opencode") {
1521
1512
  const projectConfigPath = resolveProjectConfigPath(projectRoot, host);
1522
1513
  const projectConfig = loadJsonFile(projectConfigPath);
@@ -1592,13 +1583,13 @@ import { randomUUID } from "crypto";
1592
1583
  import {
1593
1584
  existsSync as existsSync5,
1594
1585
  lstatSync,
1595
- mkdirSync as mkdirSync2,
1586
+ mkdirSync,
1596
1587
  readFileSync as readFileSync4,
1597
1588
  readdirSync as readdirSync2,
1598
1589
  realpathSync,
1599
1590
  renameSync,
1600
1591
  rmSync,
1601
- writeFileSync as writeFileSync2
1592
+ writeFileSync
1602
1593
  } from "fs";
1603
1594
  import * as os2 from "os";
1604
1595
  import * as path7 from "path";
@@ -1709,13 +1700,13 @@ function sameReclaimOwner(left, right) {
1709
1700
  function publishJsonDirectory(finalPath, value) {
1710
1701
  const candidatePath = `${finalPath}.candidate.${process.pid}.${randomUUID()}`;
1711
1702
  try {
1712
- mkdirSync2(candidatePath, { mode: 448 });
1703
+ mkdirSync(candidatePath, { mode: 448 });
1713
1704
  } catch (error) {
1714
1705
  if (getErrorCode(error) === "ENOENT") return false;
1715
1706
  throw error;
1716
1707
  }
1717
1708
  try {
1718
- writeFileSync2(path7.join(candidatePath, OWNER_FILE_NAME), JSON.stringify(value), {
1709
+ writeFileSync(path7.join(candidatePath, OWNER_FILE_NAME), JSON.stringify(value), {
1719
1710
  encoding: "utf-8",
1720
1711
  flag: "wx",
1721
1712
  mode: 384
@@ -1885,7 +1876,7 @@ function isTransientIndexLockContention(error) {
1885
1876
  return error.reason === "active" || error.reason === "reclaiming";
1886
1877
  }
1887
1878
  function acquireIndexLock(indexPath, operation) {
1888
- mkdirSync2(indexPath, { recursive: true });
1879
+ mkdirSync(indexPath, { recursive: true });
1889
1880
  const canonicalIndexPath = realpathSync.native(indexPath);
1890
1881
  const lockPath = path7.join(canonicalIndexPath, "indexing.lock");
1891
1882
  cleanupDeadPublicationCandidates(canonicalIndexPath);
@@ -1955,40 +1946,6 @@ function releaseIndexLock(lease) {
1955
1946
  }
1956
1947
  return true;
1957
1948
  }
1958
- async function withIndexLock(indexPath, operation, callback, options = {}) {
1959
- const lease = acquireIndexLock(indexPath, operation);
1960
- let result;
1961
- let callbackError;
1962
- let callbackFailed = false;
1963
- try {
1964
- result = await callback(lease);
1965
- } catch (error) {
1966
- callbackFailed = true;
1967
- callbackError = error;
1968
- }
1969
- if (!callbackFailed && options.completeRecoveries !== false) {
1970
- try {
1971
- completeLeaseRecovery(lease);
1972
- } catch (error) {
1973
- callbackFailed = true;
1974
- callbackError = error;
1975
- }
1976
- }
1977
- let releaseError;
1978
- try {
1979
- if (!releaseIndexLock(lease)) {
1980
- releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
1981
- }
1982
- } catch (error) {
1983
- releaseError = error;
1984
- }
1985
- if (releaseError !== void 0) {
1986
- if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
1987
- throw releaseError;
1988
- }
1989
- if (callbackFailed) throw callbackError;
1990
- return result;
1991
- }
1992
1949
  function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
1993
1950
  if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
1994
1951
  temporaryCounter += 1;
@@ -2027,7 +1984,7 @@ import { existsSync as existsSync10, realpathSync as realpathSync2, statSync as
2027
1984
  import * as path16 from "path";
2028
1985
 
2029
1986
  // src/indexer/index.ts
2030
- import { existsSync as existsSync8, readFileSync as readFileSync7, statSync as statSync3, writeFileSync as writeFileSync3, renameSync as renameSync2, unlinkSync, mkdirSync as mkdirSync3, promises as fsPromises2 } from "fs";
1987
+ import { existsSync as existsSync8, readFileSync as readFileSync7, statSync as statSync3, writeFileSync as writeFileSync2, renameSync as renameSync2, unlinkSync, mkdirSync as mkdirSync2, promises as fsPromises2 } from "fs";
2031
1988
  import * as path13 from "path";
2032
1989
  import { performance as performance2 } from "perf_hooks";
2033
1990
  import { execFile as execFile3 } from "child_process";
@@ -3095,6 +3052,9 @@ function loadOpenCodeAuth() {
3095
3052
  return {};
3096
3053
  }
3097
3054
  async function detectEmbeddingProvider(preferredProvider, model) {
3055
+ if (preferredProvider === "ollama") {
3056
+ return detectOllamaProvider(model);
3057
+ }
3098
3058
  const credentials = await getProviderCredentials(preferredProvider);
3099
3059
  if (credentials) {
3100
3060
  if (!model) {
@@ -3110,10 +3070,16 @@ async function detectEmbeddingProvider(preferredProvider, model) {
3110
3070
  );
3111
3071
  }
3112
3072
  const providerModels = EMBEDDING_MODELS[preferredProvider];
3073
+ const modelInfo = Object.values(providerModels).find((candidate) => candidate.model === model);
3074
+ if (!modelInfo) {
3075
+ throw new Error(
3076
+ `Model '${model}' is not supported by provider '${preferredProvider}'`
3077
+ );
3078
+ }
3113
3079
  return {
3114
3080
  provider: preferredProvider,
3115
3081
  credentials,
3116
- modelInfo: providerModels[model]
3082
+ modelInfo
3117
3083
  };
3118
3084
  }
3119
3085
  throw new Error(
@@ -3122,6 +3088,13 @@ async function detectEmbeddingProvider(preferredProvider, model) {
3122
3088
  }
3123
3089
  async function tryDetectProvider() {
3124
3090
  for (const provider of autoDetectProviders) {
3091
+ if (provider === "ollama") {
3092
+ const ollamaProvider = await tryDetectOllamaProvider();
3093
+ if (ollamaProvider) {
3094
+ return ollamaProvider;
3095
+ }
3096
+ continue;
3097
+ }
3125
3098
  const credentials = await getProviderCredentials(provider);
3126
3099
  if (credentials) {
3127
3100
  return {
@@ -3189,32 +3162,105 @@ function getGoogleCredentials() {
3189
3162
  }
3190
3163
  return null;
3191
3164
  }
3192
- async function getOllamaCredentials() {
3193
- const baseUrl = process.env.OLLAMA_HOST || "http://localhost:11434";
3165
+ async function fetchOllama(url, init) {
3166
+ const controller = new AbortController();
3167
+ const timeoutId = setTimeout(() => controller.abort(), 2e3);
3194
3168
  try {
3195
- const controller = new AbortController();
3196
- const timeoutId = setTimeout(() => controller.abort(), 2e3);
3197
- const response = await fetch(`${baseUrl}/api/tags`, {
3198
- signal: controller.signal
3199
- });
3169
+ return await fetch(url, { ...init, signal: controller.signal });
3170
+ } finally {
3200
3171
  clearTimeout(timeoutId);
3172
+ }
3173
+ }
3174
+ async function getOllamaCredentials() {
3175
+ const baseUrl = (process.env.OLLAMA_HOST || "http://localhost:11434").replace(/\/+$/, "");
3176
+ try {
3177
+ const response = await fetchOllama(`${baseUrl}/api/tags`);
3201
3178
  if (response.ok) {
3202
- const data = await response.json();
3203
- const hasEmbeddingModel = data.models?.some(
3204
- (m) => m.name.includes("nomic-embed") || m.name.includes("mxbai-embed") || m.name.includes("all-minilm")
3205
- );
3206
- if (hasEmbeddingModel) {
3207
- return {
3208
- provider: "ollama",
3209
- baseUrl
3210
- };
3211
- }
3179
+ return {
3180
+ provider: "ollama",
3181
+ baseUrl
3182
+ };
3212
3183
  }
3213
3184
  } catch {
3214
3185
  return null;
3215
3186
  }
3216
3187
  return null;
3217
3188
  }
3189
+ function findCatalogOllamaModel(model) {
3190
+ const stableName = model.endsWith(":latest") ? model.slice(0, -":latest".length) : model;
3191
+ return Object.values(EMBEDDING_MODELS.ollama).find((candidate) => candidate.model === stableName) ?? null;
3192
+ }
3193
+ function getPositiveIntegerMetadata(modelInfo, suffix) {
3194
+ const values = Object.entries(modelInfo).filter(([key]) => key.endsWith(suffix)).map(([, value]) => value).filter((value) => typeof value === "number" && Number.isInteger(value) && value > 0);
3195
+ return values.length === 1 ? values[0] : null;
3196
+ }
3197
+ async function fetchOllamaModelInfo(credentials, model) {
3198
+ const response = await fetchOllama(`${credentials.baseUrl}/api/show`, {
3199
+ method: "POST",
3200
+ headers: { "Content-Type": "application/json" },
3201
+ body: JSON.stringify({ model })
3202
+ });
3203
+ if (!response.ok) {
3204
+ return null;
3205
+ }
3206
+ const data = await response.json();
3207
+ if (!data.capabilities?.includes("embedding") || !data.model_info) {
3208
+ return null;
3209
+ }
3210
+ const dimensions = getPositiveIntegerMetadata(data.model_info, ".embedding_length");
3211
+ const maxTokens = getPositiveIntegerMetadata(data.model_info, ".context_length");
3212
+ if (!dimensions || !maxTokens) {
3213
+ return null;
3214
+ }
3215
+ return {
3216
+ provider: "ollama",
3217
+ model,
3218
+ dimensions,
3219
+ maxTokens,
3220
+ costPer1MTokens: 0
3221
+ };
3222
+ }
3223
+ async function listOllamaModels(credentials) {
3224
+ const response = await fetchOllama(`${credentials.baseUrl}/api/tags`);
3225
+ if (!response.ok) {
3226
+ return [];
3227
+ }
3228
+ const data = await response.json();
3229
+ return [...new Set((data.models ?? []).map((entry) => entry.name ?? entry.model).filter((name) => typeof name === "string" && name.trim().length > 0))];
3230
+ }
3231
+ async function detectOllamaProvider(model) {
3232
+ const credentials = await getOllamaCredentials();
3233
+ if (!credentials) {
3234
+ throw new Error("Preferred provider 'ollama' is not configured or authenticated");
3235
+ }
3236
+ const requestedModel = model?.trim();
3237
+ if (requestedModel) {
3238
+ const catalogModel = findCatalogOllamaModel(requestedModel);
3239
+ if (catalogModel) {
3240
+ return { provider: "ollama", credentials, modelInfo: catalogModel };
3241
+ }
3242
+ }
3243
+ const candidates = requestedModel ? [requestedModel] : await listOllamaModels(credentials);
3244
+ for (const candidate of candidates) {
3245
+ const modelInfo = await fetchOllamaModelInfo(credentials, candidate);
3246
+ if (modelInfo) {
3247
+ return {
3248
+ provider: "ollama",
3249
+ credentials,
3250
+ modelInfo: findCatalogOllamaModel(candidate) ?? modelInfo
3251
+ };
3252
+ }
3253
+ }
3254
+ const detail = model ? `Model '${model}' is not installed or is not embedding-capable` : "No installed embedding-capable Ollama model was found";
3255
+ throw new Error(detail);
3256
+ }
3257
+ async function tryDetectOllamaProvider() {
3258
+ try {
3259
+ return await detectOllamaProvider();
3260
+ } catch {
3261
+ return null;
3262
+ }
3263
+ }
3218
3264
  function getProviderDisplayName(provider) {
3219
3265
  switch (provider) {
3220
3266
  case "github-copilot":
@@ -3554,6 +3600,7 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
3554
3600
  // src/embeddings/providers/ollama.ts
3555
3601
  var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
3556
3602
  static MIN_TRUNCATION_CHARS = 512;
3603
+ static REQUEST_TIMEOUT_MS = 12e4;
3557
3604
  constructor(credentials, modelInfo) {
3558
3605
  super(credentials, modelInfo);
3559
3606
  }
@@ -3628,22 +3675,45 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
3628
3675
  }
3629
3676
  }
3630
3677
  async embedSingle(text) {
3631
- const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
3632
- method: "POST",
3633
- headers: {
3634
- "Content-Type": "application/json"
3635
- },
3636
- body: JSON.stringify({
3637
- model: this.modelInfo.model,
3638
- prompt: text,
3639
- truncate: false
3640
- })
3641
- });
3678
+ const controller = new AbortController();
3679
+ const timeout = setTimeout(
3680
+ () => controller.abort(),
3681
+ _OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS
3682
+ );
3683
+ let response;
3684
+ try {
3685
+ response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
3686
+ method: "POST",
3687
+ headers: {
3688
+ "Content-Type": "application/json"
3689
+ },
3690
+ body: JSON.stringify({
3691
+ model: this.modelInfo.model,
3692
+ prompt: text,
3693
+ truncate: false
3694
+ }),
3695
+ signal: controller.signal
3696
+ });
3697
+ } catch (error) {
3698
+ if (error instanceof Error && error.name === "AbortError") {
3699
+ throw new Error(
3700
+ `Ollama embedding request timed out after ${_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS}ms`
3701
+ );
3702
+ }
3703
+ throw error;
3704
+ } finally {
3705
+ clearTimeout(timeout);
3706
+ }
3642
3707
  if (!response.ok) {
3643
3708
  const error = (await response.text()).slice(0, 500);
3644
3709
  throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
3645
3710
  }
3646
3711
  const data = await response.json();
3712
+ if (!Array.isArray(data.embedding) || data.embedding.length !== this.modelInfo.dimensions || data.embedding.some((value) => typeof value !== "number" || !Number.isFinite(value))) {
3713
+ throw new Error(
3714
+ `Ollama returned an invalid embedding; expected ${this.modelInfo.dimensions} finite dimensions`
3715
+ );
3716
+ }
3647
3717
  return {
3648
3718
  embedding: data.embedding,
3649
3719
  tokensUsed: this.estimateTokens(text)
@@ -4506,7 +4576,9 @@ function mapChunk(c) {
4506
4576
  return {
4507
4577
  content: c.content,
4508
4578
  startLine: c.startLine ?? c.start_line,
4579
+ startCol: c.startCol ?? c.start_col,
4509
4580
  endLine: c.endLine ?? c.end_line,
4581
+ endCol: c.endCol ?? c.end_col,
4510
4582
  chunkType: c.chunkType ?? c.chunk_type,
4511
4583
  name: c.name ?? void 0,
4512
4584
  language: c.language
@@ -4627,6 +4699,7 @@ function getEmbeddingHeaderParts(chunk, filePath) {
4627
4699
  javascript: "JavaScript",
4628
4700
  python: "Python",
4629
4701
  rust: "Rust",
4702
+ swift: "Swift",
4630
4703
  go: "Go",
4631
4704
  java: "Java"
4632
4705
  };
@@ -4635,7 +4708,16 @@ function getEmbeddingHeaderParts(chunk, filePath) {
4635
4708
  function: "function",
4636
4709
  arrow_function: "arrow function",
4637
4710
  method_definition: "method",
4711
+ method_declaration: "method",
4712
+ protocol_function_declaration: "protocol requirement",
4713
+ init_declaration: "initializer",
4714
+ deinit_declaration: "deinitializer",
4715
+ subscript_declaration: "subscript",
4638
4716
  class_declaration: "class",
4717
+ actor_declaration: "actor",
4718
+ extension_declaration: "extension",
4719
+ protocol_declaration: "protocol",
4720
+ struct_declaration: "struct",
4639
4721
  interface_declaration: "interface",
4640
4722
  type_alias_declaration: "type alias",
4641
4723
  enum_declaration: "enum",
@@ -5306,8 +5388,18 @@ async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
5306
5388
  }
5307
5389
 
5308
5390
  // src/indexer/index.ts
5309
- var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab", "bash"]);
5310
- var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
5391
+ var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "swift", "php", "apex", "zig", "gdscript", "matlab", "bash", "c", "cpp", "metal"]);
5392
+ var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex", "php"]);
5393
+ var CALL_GRAPH_RESOLUTION_VERSION = "4";
5394
+ var PHP_FUNCTION_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
5395
+ "function_declaration",
5396
+ "function",
5397
+ "function_definition"
5398
+ ]);
5399
+ var PHP_CLASS_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
5400
+ "class_declaration",
5401
+ "class_definition"
5402
+ ]);
5311
5403
  var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
5312
5404
  "function_declaration",
5313
5405
  "function",
@@ -5320,6 +5412,9 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
5320
5412
  "enum_declaration",
5321
5413
  "function_definition",
5322
5414
  "class_definition",
5415
+ "class_specifier",
5416
+ "struct_specifier",
5417
+ "namespace_definition",
5323
5418
  "decorated_definition",
5324
5419
  "method_declaration",
5325
5420
  "type_declaration",
@@ -5335,6 +5430,14 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
5335
5430
  "test_declaration",
5336
5431
  "struct_declaration",
5337
5432
  "union_declaration",
5433
+ // Synthetic Swift declarations or declarations specific to tree-sitter-swift.
5434
+ "actor_declaration",
5435
+ "extension_declaration",
5436
+ "protocol_declaration",
5437
+ "protocol_function_declaration",
5438
+ "init_declaration",
5439
+ "deinit_declaration",
5440
+ "subscript_declaration",
5338
5441
  // GDScript declarations whose names participate in the call graph.
5339
5442
  // `function_definition` and `class_definition` are already in the set
5340
5443
  // above (shared with Python/C/Bash and Python, respectively).
@@ -5344,6 +5447,53 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
5344
5447
  "const_statement",
5345
5448
  "class_name_statement"
5346
5449
  ]);
5450
+ var C_FAMILY_TYPE_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set(["class_specifier", "struct_specifier"]);
5451
+ function isCompatibleCFamilyCallTarget(language, callType, symbolKind) {
5452
+ if (language !== "c" && language !== "cpp") return true;
5453
+ if (symbolKind === "namespace_definition") return callType === "Import";
5454
+ const isTypeSymbol = C_FAMILY_TYPE_SYMBOL_CHUNK_TYPES.has(symbolKind);
5455
+ if (callType === "Constructor" || callType === "Inherits" || callType === "Implements") {
5456
+ return isTypeSymbol;
5457
+ }
5458
+ return !isTypeSymbol;
5459
+ }
5460
+ var EXECUTABLE_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
5461
+ "function_declaration",
5462
+ "function",
5463
+ "arrow_function",
5464
+ "method_definition",
5465
+ "function_definition",
5466
+ "method_declaration",
5467
+ "function_item",
5468
+ "protocol_function_declaration",
5469
+ "init_declaration",
5470
+ "deinit_declaration",
5471
+ "subscript_declaration",
5472
+ "constructor_definition",
5473
+ "trigger_declaration",
5474
+ "test_declaration"
5475
+ ]);
5476
+ function findEnclosingSymbol(symbols, line, column) {
5477
+ let best;
5478
+ for (const symbol of symbols) {
5479
+ if (line < symbol.startLine || line > symbol.endLine) continue;
5480
+ if (column !== void 0 && (line === symbol.startLine && column < symbol.startCol || line === symbol.endLine && column >= symbol.endCol)) {
5481
+ continue;
5482
+ }
5483
+ if (!best) {
5484
+ best = symbol;
5485
+ continue;
5486
+ }
5487
+ const span = symbol.endLine - symbol.startLine;
5488
+ const bestSpan = best.endLine - best.startLine;
5489
+ 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);
5490
+ const isMoreSpecificTie = span === bestSpan && symbol.startLine === best.startLine && EXECUTABLE_SYMBOL_CHUNK_TYPES.has(symbol.kind) && !EXECUTABLE_SYMBOL_CHUNK_TYPES.has(best.kind);
5491
+ if (span < bestSpan || span === bestSpan && symbol.startLine > best.startLine || isNarrowerPositionRange || isMoreSpecificTie) {
5492
+ best = symbol;
5493
+ }
5494
+ }
5495
+ return best;
5496
+ }
5347
5497
  function float32ArrayToBuffer(arr) {
5348
5498
  const float32 = new Float32Array(arr);
5349
5499
  return Buffer.from(float32.buffer);
@@ -5428,6 +5578,8 @@ function hasBlameMetadata(metadata) {
5428
5578
  }
5429
5579
  var INDEX_METADATA_VERSION = "1";
5430
5580
  var EMBEDDING_STRATEGY_VERSION = "2";
5581
+ var SWIFT_PARSER_VERSION = "1";
5582
+ var METAL_PARSER_VERSION = "1";
5431
5583
  var RANKING_TOKEN_CACHE_LIMIT = 4096;
5432
5584
  var RANK_HYBRID_CACHE_LIMIT = 256;
5433
5585
  function createPendingChunkStorageText(texts) {
@@ -5750,15 +5902,25 @@ function chunkTypeBoost(chunkType) {
5750
5902
  case "function_declaration":
5751
5903
  case "method":
5752
5904
  case "method_definition":
5905
+ case "method_declaration":
5906
+ case "protocol_function_declaration":
5907
+ case "init_declaration":
5908
+ case "deinit_declaration":
5909
+ case "subscript_declaration":
5753
5910
  case "class":
5754
5911
  case "class_declaration":
5912
+ case "actor_declaration":
5913
+ case "extension_declaration":
5755
5914
  return 0.2;
5756
5915
  case "interface":
5757
5916
  case "type":
5758
5917
  case "enum":
5918
+ case "enum_declaration":
5759
5919
  case "struct":
5920
+ case "struct_declaration":
5760
5921
  case "impl":
5761
5922
  case "trait":
5923
+ case "protocol_declaration":
5762
5924
  case "module":
5763
5925
  return 0.1;
5764
5926
  default:
@@ -5865,11 +6027,21 @@ function isImplementationChunkType(chunkType) {
5865
6027
  "function_declaration",
5866
6028
  "method",
5867
6029
  "method_definition",
6030
+ "method_declaration",
6031
+ "protocol_function_declaration",
6032
+ "init_declaration",
6033
+ "deinit_declaration",
6034
+ "subscript_declaration",
5868
6035
  "class",
5869
6036
  "class_declaration",
6037
+ "actor_declaration",
6038
+ "extension_declaration",
5870
6039
  "interface",
6040
+ "protocol_declaration",
5871
6041
  "type",
5872
6042
  "enum",
6043
+ "enum_declaration",
6044
+ "struct_declaration",
5873
6045
  "module"
5874
6046
  ].includes(chunkType);
5875
6047
  }
@@ -6821,9 +6993,9 @@ var Indexer = class {
6821
6993
  atomicWriteSync(targetPath, data) {
6822
6994
  const lease = this.requireActiveLease();
6823
6995
  const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
6824
- mkdirSync3(path13.dirname(targetPath), { recursive: true });
6996
+ mkdirSync2(path13.dirname(targetPath), { recursive: true });
6825
6997
  try {
6826
- writeFileSync3(tempPath, data);
6998
+ writeFileSync2(tempPath, data);
6827
6999
  renameSync2(tempPath, targetPath);
6828
7000
  } finally {
6829
7001
  removeLeaseTemporaryPath(tempPath);
@@ -6872,6 +7044,29 @@ var Indexer = class {
6872
7044
  const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
6873
7045
  return `index.forceReembed.${projectHash}`;
6874
7046
  }
7047
+ getCallGraphResolutionMetadataKey() {
7048
+ if (this.config.scope !== "global") {
7049
+ return "index.callGraphResolutionVersion";
7050
+ }
7051
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7052
+ return `index.callGraphResolutionVersion.${projectHash}`;
7053
+ }
7054
+ getSwiftParserVersionMetadataKey() {
7055
+ const key = "index.parser.swiftVersion";
7056
+ if (this.config.scope !== "global") {
7057
+ return key;
7058
+ }
7059
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7060
+ return `${key}.${projectHash}`;
7061
+ }
7062
+ getMetalParserVersionMetadataKey() {
7063
+ const key = "index.parser.metalVersion";
7064
+ if (this.config.scope !== "global") {
7065
+ return key;
7066
+ }
7067
+ const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
7068
+ return `${key}.${projectHash}`;
7069
+ }
6875
7070
  hasProjectForceReembedPending() {
6876
7071
  return this.config.scope === "global" && this.database?.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
6877
7072
  }
@@ -8032,6 +8227,7 @@ var Indexer = class {
8032
8227
  this.database.setMetadata("index.embeddingProvider", provider.provider);
8033
8228
  this.database.setMetadata("index.embeddingModel", provider.modelInfo.model);
8034
8229
  this.database.setMetadata("index.embeddingDimensions", provider.modelInfo.dimensions.toString());
8230
+ this.database.setMetadata(this.getCallGraphResolutionMetadataKey(), CALL_GRAPH_RESOLUTION_VERSION);
8035
8231
  if (this.config.scope === "global") {
8036
8232
  if (completeProjectEmbeddingStrategyReset) {
8037
8233
  this.database.setMetadata(this.getProjectEmbeddingStrategyMetadataKey(), EMBEDDING_STRATEGY_VERSION);
@@ -8219,6 +8415,20 @@ var Indexer = class {
8219
8415
  totalChunks: 0
8220
8416
  });
8221
8417
  this.loadFileHashCache();
8418
+ const swiftParserMetadataKey = this.getSwiftParserVersionMetadataKey();
8419
+ const reparseCachedSwiftFiles = database.getMetadata(swiftParserMetadataKey) !== SWIFT_PARSER_VERSION;
8420
+ const metalParserMetadataKey = this.getMetalParserVersionMetadataKey();
8421
+ const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
8422
+ if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some(
8423
+ (filePath) => path13.extname(filePath).toLowerCase() === ".swift"
8424
+ )) {
8425
+ this.logger.info("Reindexing cached Swift files for parser support");
8426
+ }
8427
+ if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some(
8428
+ (filePath) => path13.extname(filePath).toLowerCase() === ".metal"
8429
+ )) {
8430
+ this.logger.info("Reindexing cached Metal files for parser support");
8431
+ }
8222
8432
  const includePatterns = [...this.config.include, ...this.config.additionalInclude];
8223
8433
  const { files, skipped } = await collectFiles(
8224
8434
  this.projectRoot,
@@ -8238,10 +8448,17 @@ var Indexer = class {
8238
8448
  const changedFiles = [];
8239
8449
  const unchangedFilePaths = /* @__PURE__ */ new Set();
8240
8450
  const currentFileHashes = /* @__PURE__ */ new Map();
8451
+ const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
8241
8452
  for (const f of files) {
8242
8453
  const currentHash = hashFile(f.path);
8243
8454
  currentFileHashes.set(f.path, currentHash);
8244
- if (this.fileHashCache.get(f.path) === currentHash) {
8455
+ const cachedHashMatches = this.fileHashCache.get(f.path) === currentHash;
8456
+ const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(f.path).some(
8457
+ (chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
8458
+ );
8459
+ const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path13.extname(f.path).toLowerCase() === ".swift";
8460
+ const requiresMetalParserUpgrade = reparseCachedMetalFiles && path13.extname(f.path).toLowerCase() === ".metal";
8461
+ if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade) {
8245
8462
  unchangedFilePaths.add(f.path);
8246
8463
  this.logger.recordCacheHit();
8247
8464
  } else {
@@ -8443,16 +8660,28 @@ var Indexer = class {
8443
8660
  const fileSymbols = [];
8444
8661
  for (const chunk of parsed.chunks) {
8445
8662
  if (!chunk.name || !CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(chunk.chunkType)) continue;
8446
- const symbolId = `sym_${hashContent(parsed.path + ":" + chunk.name + ":" + chunk.chunkType + ":" + chunk.startLine).slice(0, 16)}`;
8663
+ const existingMetalSymbol = chunk.language === "metal" ? fileSymbols.find(
8664
+ (symbol2) => symbol2.name === chunk.name && symbol2.kind === chunk.chunkType && symbol2.startLine <= chunk.endLine && chunk.startLine <= symbol2.endLine
8665
+ ) : void 0;
8666
+ if (existingMetalSymbol) {
8667
+ existingMetalSymbol.startLine = Math.min(existingMetalSymbol.startLine, chunk.startLine);
8668
+ existingMetalSymbol.endLine = Math.max(existingMetalSymbol.endLine, chunk.endLine);
8669
+ existingMetalSymbol.startCol = Math.min(existingMetalSymbol.startCol, chunk.startCol ?? 0);
8670
+ existingMetalSymbol.endCol = Math.max(existingMetalSymbol.endCol, chunk.endCol ?? 0);
8671
+ continue;
8672
+ }
8673
+ const symbolId = `sym_${hashContent(
8674
+ parsed.path + ":" + chunk.name + ":" + chunk.chunkType + ":" + chunk.startLine + ":" + (chunk.startCol ?? 0)
8675
+ ).slice(0, 16)}`;
8447
8676
  const symbol = {
8448
8677
  id: symbolId,
8449
8678
  filePath: parsed.path,
8450
8679
  name: chunk.name,
8451
8680
  kind: chunk.chunkType,
8452
8681
  startLine: chunk.startLine,
8453
- startCol: 0,
8682
+ startCol: chunk.startCol ?? 0,
8454
8683
  endLine: chunk.endLine,
8455
- endCol: 0,
8684
+ endCol: chunk.endCol ?? 0,
8456
8685
  language: chunk.language
8457
8686
  };
8458
8687
  fileSymbols.push(symbol);
@@ -8477,8 +8706,10 @@ var Indexer = class {
8477
8706
  if (callSites.length === 0) continue;
8478
8707
  const edges = [];
8479
8708
  for (const site of callSites) {
8480
- const enclosingSymbol = fileSymbols.find(
8481
- (sym) => site.line >= sym.startLine && site.line <= sym.endLine
8709
+ const enclosingSymbol = findEnclosingSymbol(
8710
+ fileSymbols,
8711
+ site.line,
8712
+ site.column
8482
8713
  );
8483
8714
  if (!enclosingSymbol) continue;
8484
8715
  const edgeId = `edge_${hashContent(enclosingSymbol.id + ":" + site.calleeName + ":" + site.line + ":" + site.column).slice(0, 16)}`;
@@ -8497,7 +8728,21 @@ var Indexer = class {
8497
8728
  if (edges.length > 0) {
8498
8729
  database.upsertCallEdgesBatch(edges);
8499
8730
  for (const edge of edges) {
8500
- const candidates = symbolsByName.get(normalizeSymbolKey(edge.targetName));
8731
+ let candidates = symbolsByName.get(normalizeSymbolKey(edge.targetName));
8732
+ if (fileLanguage === "php" && candidates) {
8733
+ if (edge.callType === "Constructor") {
8734
+ candidates = candidates.filter(
8735
+ (candidate) => PHP_CLASS_SYMBOL_CHUNK_TYPES.has(candidate.kind)
8736
+ );
8737
+ } else if (edge.callType === "Call") {
8738
+ candidates = candidates.filter(
8739
+ (candidate) => PHP_FUNCTION_SYMBOL_CHUNK_TYPES.has(candidate.kind)
8740
+ );
8741
+ }
8742
+ }
8743
+ candidates = candidates?.filter(
8744
+ (symbol) => isCompatibleCFamilyCallTarget(fileLanguage, edge.callType, symbol.kind)
8745
+ );
8501
8746
  if (candidates && candidates.length === 1) {
8502
8747
  database.resolveCallEdge(edge.id, candidates[0].id);
8503
8748
  }
@@ -8552,6 +8797,8 @@ var Indexer = class {
8552
8797
  this.saveFileHashCache();
8553
8798
  this.saveFailedBatches([]);
8554
8799
  }
8800
+ database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
8801
+ database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
8555
8802
  this.saveIndexMetadata(configuredProviderInfo);
8556
8803
  this.indexCompatibility = { compatible: true };
8557
8804
  stats.durationMs = Date.now() - startTime;
@@ -8579,6 +8826,8 @@ var Indexer = class {
8579
8826
  this.saveFileHashCache();
8580
8827
  this.saveFailedBatches([]);
8581
8828
  }
8829
+ database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
8830
+ database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
8582
8831
  this.saveIndexMetadata(configuredProviderInfo);
8583
8832
  this.indexCompatibility = { compatible: true };
8584
8833
  stats.durationMs = Date.now() - startTime;
@@ -8849,6 +9098,8 @@ var Indexer = class {
8849
9098
  if (forceScopedReembed && failedForcedChunkIds.size === 0) {
8850
9099
  database.deleteMetadata(this.getProjectForceReembedMetadataKey());
8851
9100
  }
9101
+ database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
9102
+ database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
8852
9103
  this.saveIndexMetadata(configuredProviderInfo);
8853
9104
  this.indexCompatibility = { compatible: true };
8854
9105
  this.logger.recordIndexingEnd();
@@ -10403,7 +10654,7 @@ ${truncateContent(r.content)}
10403
10654
  }
10404
10655
 
10405
10656
  // src/tools/config-state.ts
10406
- import { existsSync as existsSync9, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
10657
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
10407
10658
  import * as path15 from "path";
10408
10659
  function normalizeKnowledgeBasePaths(config, projectRoot) {
10409
10660
  const normalized = { ...config };
@@ -10434,7 +10685,7 @@ function saveConfig(projectRoot, config, host = "opencode") {
10434
10685
  const configDir = path15.dirname(configPath);
10435
10686
  const configBaseDir = path15.dirname(configDir);
10436
10687
  if (!existsSync9(configDir)) {
10437
- mkdirSync4(configDir, { recursive: true });
10688
+ mkdirSync3(configDir, { recursive: true });
10438
10689
  }
10439
10690
  const serializableConfig = { ...config };
10440
10691
  if (Array.isArray(serializableConfig.knowledgeBases)) {
@@ -10442,7 +10693,7 @@ function saveConfig(projectRoot, config, host = "opencode") {
10442
10693
  (kb) => serializeConfigPathValue(kb, configBaseDir)
10443
10694
  );
10444
10695
  }
10445
- writeFileSync4(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
10696
+ writeFileSync3(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
10446
10697
  }
10447
10698
 
10448
10699
  // src/tools/operations.ts
@@ -10509,15 +10760,6 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
10509
10760
  indexerCache.set(key, indexer);
10510
10761
  configCache.set(key, config);
10511
10762
  }
10512
- function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
10513
- const root = getProjectRoot(projectRoot, host);
10514
- const localIndexPath = path16.join(root, getHostProjectIndexRelativePath(host));
10515
- if (existsSync10(localIndexPath)) {
10516
- return false;
10517
- }
10518
- const inheritedIndexPath = resolveWorktreeFallbackProjectIndexPath(root, host);
10519
- return inheritedIndexPath !== null;
10520
- }
10521
10763
  async function searchCodebase(projectRoot, host, query, options = {}) {
10522
10764
  const indexer = getIndexerForProject(projectRoot, host);
10523
10765
  return indexer.search(query, options.limit, {
@@ -10567,9 +10809,7 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth) {
10567
10809
  }
10568
10810
  async function runIndexCodebase(projectRoot, host, args, onProgress) {
10569
10811
  const root = getProjectRoot(projectRoot, host);
10570
- const key = getIndexerCacheKey(root, host);
10571
- let indexer = getIndexerForProject(root, host);
10572
- const runtimeConfig = configCache.get(key);
10812
+ const indexer = getIndexerForProject(root, host);
10573
10813
  try {
10574
10814
  if (args.estimateOnly) {
10575
10815
  return { kind: "estimate", estimate: await indexer.estimateCost() };
@@ -10590,19 +10830,7 @@ async function runIndexCodebase(projectRoot, host, args, onProgress) {
10590
10830
  return Promise.resolve();
10591
10831
  });
10592
10832
  };
10593
- let stats;
10594
- if (args.force && shouldForceLocalizeProjectIndex(root, host)) {
10595
- const inheritedIndexPath = resolveProjectIndexPath(root, runtimeConfig.scope, host);
10596
- stats = await withIndexLock(inheritedIndexPath, "force-index", async () => {
10597
- materializeLocalProjectConfig(root, loadProjectConfigLayer(root, host), host);
10598
- refreshIndexerForDirectory(root, host, runtimeConfig);
10599
- indexer = getIndexerForProject(root, host);
10600
- return runIndex(indexer);
10601
- }, { completeRecoveries: false });
10602
- } else {
10603
- stats = await runIndex(indexer);
10604
- }
10605
- return { kind: "stats", stats };
10833
+ return { kind: "stats", stats: await runIndex(indexer) };
10606
10834
  } catch (error) {
10607
10835
  const busyResult = getIndexBusyResult(error);
10608
10836
  if (!busyResult) throw error;
@@ -12891,11 +13119,12 @@ function getConfigPaths(projectRoot, host, options) {
12891
13119
  ].map((configPath) => pathNormalize(configPath));
12892
13120
  }
12893
13121
 
12894
- // src/tools/index.ts
12895
- import { tool as tool2 } from "@opencode-ai/plugin";
12896
-
12897
- // src/tools/pr-impact.ts
12898
- import { tool } from "@opencode-ai/plugin";
13122
+ // node_modules/@opencode-ai/plugin/dist/tool.js
13123
+ import { z } from "zod";
13124
+ function tool(input) {
13125
+ return input;
13126
+ }
13127
+ tool.schema = z;
12899
13128
 
12900
13129
  // src/tools/format-pr-impact.ts
12901
13130
  function formatPrImpact(result) {
@@ -12949,16 +13178,16 @@ function formatPrImpact(result) {
12949
13178
  }
12950
13179
 
12951
13180
  // src/tools/pr-impact.ts
12952
- var z = tool.schema;
13181
+ var z2 = tool.schema;
12953
13182
  var pr_impact = tool({
12954
13183
  description: "Analyze the impact of a pull request or branch by examining changed files, affected symbols, transitive callers, communities touched, hub nodes, and risk level. Use to understand blast radius before merging.",
12955
13184
  args: {
12956
- pr: z.number().optional().describe("Pull request number to analyze"),
12957
- branch: z.string().optional().describe("Branch name to analyze (defaults to current branch)"),
12958
- maxDepth: z.number().optional().default(5).describe("Maximum traversal depth for transitive callers (default: 5)"),
12959
- hubThreshold: z.number().optional().default(10).describe("Minimum caller count to flag a symbol as a hub node (default: 10)"),
12960
- checkConflicts: z.boolean().optional().default(false).describe("Check for conflicting open PRs touching the same communities (default: false)"),
12961
- direction: z.enum(["callers", "callees", "both"]).optional().default("both").describe("Call-graph traversal direction: 'callers' for upstream, 'callees' for downstream, 'both' for union (default: both)")
13185
+ pr: z2.number().optional().describe("Pull request number to analyze"),
13186
+ branch: z2.string().optional().describe("Branch name to analyze (defaults to current branch)"),
13187
+ maxDepth: z2.number().optional().default(5).describe("Maximum traversal depth for transitive callers (default: 5)"),
13188
+ hubThreshold: z2.number().optional().default(10).describe("Minimum caller count to flag a symbol as a hub node (default: 10)"),
13189
+ checkConflicts: z2.boolean().optional().default(false).describe("Check for conflicting open PRs touching the same communities (default: false)"),
13190
+ direction: z2.enum(["callers", "callees", "both"]).optional().default("both").describe("Call-graph traversal direction: 'callers' for upstream, 'callees' for downstream, 'both' for union (default: both)")
12962
13191
  },
12963
13192
  async execute(args, context) {
12964
13193
  const indexer = getIndexerForProject2(context?.worktree);
@@ -13227,7 +13456,7 @@ async function resolveCodebaseContext(projectRoot, host, input) {
13227
13456
  }
13228
13457
 
13229
13458
  // src/tools/index.ts
13230
- import { writeFileSync as writeFileSync5 } from "fs";
13459
+ import { writeFileSync as writeFileSync4 } from "fs";
13231
13460
  import * as os5 from "os";
13232
13461
  import * as path21 from "path";
13233
13462
 
@@ -13935,7 +14164,7 @@ function transformForVisualization(symbols, edges, options = {}) {
13935
14164
  }
13936
14165
 
13937
14166
  // src/tools/index.ts
13938
- var z2 = tool2.schema;
14167
+ var z3 = tool.schema;
13939
14168
  var DEFAULT_HOST = "opencode";
13940
14169
  var CHUNK_TYPE_VALUES = [
13941
14170
  "function",
@@ -13957,34 +14186,34 @@ function initializeTools2(projectRoot, config) {
13957
14186
  function getIndexerForProject2(directory) {
13958
14187
  return getIndexerForProject(directory, DEFAULT_HOST);
13959
14188
  }
13960
- var codebase_context = tool2({
14189
+ var codebase_context = tool({
13961
14190
  description: "PREFERRED FIRST TOOL for repository questions. Returns a deduplicated, file-diverse evidence pack within tokenBudget. Provide from and to for dependency paths, symbol for definitions, or only query for conceptual discovery.",
13962
14191
  args: {
13963
- query: z2.string().describe("The repository question or behavior to locate"),
13964
- from: z2.string().nullable().optional().describe("Source symbol for a dependency path"),
13965
- to: z2.string().nullable().optional().describe("Target symbol for a dependency path"),
13966
- symbol: z2.string().nullable().optional().describe("Exact symbol for authoritative definition lookup"),
13967
- limit: z2.number().int().min(MIN_CONTEXT_RESULT_LIMIT).max(MAX_CONTEXT_RESULT_LIMIT).nullable().optional().default(10).describe(`Maximum number of results (${MIN_CONTEXT_RESULT_LIMIT}-${MAX_CONTEXT_RESULT_LIMIT})`),
13968
- maxDepth: z2.number().int().min(MIN_CONTEXT_PATH_DEPTH).max(MAX_CONTEXT_PATH_DEPTH).nullable().optional().default(10).describe(`Maximum call-path traversal depth (${MIN_CONTEXT_PATH_DEPTH}-${MAX_CONTEXT_PATH_DEPTH})`),
13969
- fileType: z2.string().nullable().optional().describe("Filter by file extension"),
13970
- directory: z2.string().nullable().optional().describe("Filter by directory path"),
13971
- tokenBudget: z2.number().int().min(MIN_CONTEXT_PACK_TOKEN_BUDGET).max(MAX_CONTEXT_PACK_TOKEN_BUDGET).nullable().optional().default(DEFAULT_CONTEXT_PACK_TOKEN_BUDGET).describe(`Maximum response tokens (${MIN_CONTEXT_PACK_TOKEN_BUDGET}-${MAX_CONTEXT_PACK_TOKEN_BUDGET})`)
14192
+ query: z3.string().describe("The repository question or behavior to locate"),
14193
+ from: z3.string().nullable().optional().describe("Source symbol for a dependency path"),
14194
+ to: z3.string().nullable().optional().describe("Target symbol for a dependency path"),
14195
+ symbol: z3.string().nullable().optional().describe("Exact symbol for authoritative definition lookup"),
14196
+ limit: z3.number().int().min(MIN_CONTEXT_RESULT_LIMIT).max(MAX_CONTEXT_RESULT_LIMIT).nullable().optional().default(10).describe(`Maximum number of results (${MIN_CONTEXT_RESULT_LIMIT}-${MAX_CONTEXT_RESULT_LIMIT})`),
14197
+ maxDepth: z3.number().int().min(MIN_CONTEXT_PATH_DEPTH).max(MAX_CONTEXT_PATH_DEPTH).nullable().optional().default(10).describe(`Maximum call-path traversal depth (${MIN_CONTEXT_PATH_DEPTH}-${MAX_CONTEXT_PATH_DEPTH})`),
14198
+ fileType: z3.string().nullable().optional().describe("Filter by file extension"),
14199
+ directory: z3.string().nullable().optional().describe("Filter by directory path"),
14200
+ tokenBudget: z3.number().int().min(MIN_CONTEXT_PACK_TOKEN_BUDGET).max(MAX_CONTEXT_PACK_TOKEN_BUDGET).nullable().optional().default(DEFAULT_CONTEXT_PACK_TOKEN_BUDGET).describe(`Maximum response tokens (${MIN_CONTEXT_PACK_TOKEN_BUDGET}-${MAX_CONTEXT_PACK_TOKEN_BUDGET})`)
13972
14201
  },
13973
14202
  async execute(args, context) {
13974
14203
  return (await resolveCodebaseContext(context?.worktree, DEFAULT_HOST, args)).text;
13975
14204
  }
13976
14205
  });
13977
- var codebase_peek = tool2({
14206
+ var codebase_peek = tool({
13978
14207
  description: "Quick lookup of code locations by meaning. Returns only metadata (file, line, name, type) WITHOUT code content. Use this first to find WHERE code is, then use Read tool to examine specific files. Saves tokens by not returning full code blocks. Best for: discovery, navigation, finding multiple related locations.",
13979
14208
  args: {
13980
- query: z2.string().describe("Natural language description of what code you're looking for."),
13981
- limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
13982
- fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
13983
- directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
13984
- chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
13985
- blameAuthor: z2.string().optional().describe("Filter by git blame author name or email"),
13986
- blameSha: z2.string().optional().describe("Filter by git blame commit SHA or prefix"),
13987
- blameSince: z2.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
14209
+ query: z3.string().describe("Natural language description of what code you're looking for."),
14210
+ limit: z3.number().optional().default(10).describe("Maximum number of results to return"),
14211
+ fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14212
+ directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14213
+ chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14214
+ blameAuthor: z3.string().optional().describe("Filter by git blame author name or email"),
14215
+ blameSha: z3.string().optional().describe("Filter by git blame commit SHA or prefix"),
14216
+ blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
13988
14217
  },
13989
14218
  async execute(args, context) {
13990
14219
  const results = await searchCodebase(context?.worktree, DEFAULT_HOST, args.query, {
@@ -14000,12 +14229,12 @@ var codebase_peek = tool2({
14000
14229
  return formatCodebasePeek(results);
14001
14230
  }
14002
14231
  });
14003
- var index_codebase = tool2({
14232
+ var index_codebase = tool({
14004
14233
  description: "Index the codebase for semantic search. Creates vector embeddings of code chunks. Incremental - only re-indexes changed files (~50ms when nothing changed). Run before first codebase_search.",
14005
14234
  args: {
14006
- force: z2.boolean().optional().default(false).describe("Force reindex even if already indexed"),
14007
- estimateOnly: z2.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
14008
- verbose: z2.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
14235
+ force: z3.boolean().optional().default(false).describe("Force reindex even if already indexed"),
14236
+ estimateOnly: z3.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
14237
+ verbose: z3.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
14009
14238
  },
14010
14239
  async execute(args, context) {
14011
14240
  const result = await runIndexCodebase(context?.worktree, DEFAULT_HOST, args, (title, metadata) => {
@@ -14016,14 +14245,14 @@ var index_codebase = tool2({
14016
14245
  return formatIndexStats(result.stats, args.verbose ?? false);
14017
14246
  }
14018
14247
  });
14019
- var index_status = tool2({
14248
+ var index_status = tool({
14020
14249
  description: "Check the status of the codebase index. Shows whether the codebase is indexed, how many chunks are stored, and the embedding provider being used.",
14021
14250
  args: {},
14022
14251
  async execute(_args, context) {
14023
14252
  return formatStatus(await getIndexStatus(context?.worktree, DEFAULT_HOST));
14024
14253
  }
14025
14254
  });
14026
- var index_health_check = tool2({
14255
+ var index_health_check = tool({
14027
14256
  description: "Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
14028
14257
  args: {},
14029
14258
  async execute(_args, context) {
@@ -14032,33 +14261,33 @@ var index_health_check = tool2({
14032
14261
  return formatHealthCheck(result.health);
14033
14262
  }
14034
14263
  });
14035
- var index_metrics = tool2({
14264
+ var index_metrics = tool({
14036
14265
  description: "Get metrics and performance statistics for the codebase index. Shows indexing stats, search timings, cache hit rates, and API usage. Requires debug.enabled=true and debug.metrics=true in config.",
14037
14266
  args: {},
14038
14267
  async execute(_args, context) {
14039
14268
  return (await getIndexMetrics(context?.worktree, DEFAULT_HOST)).text;
14040
14269
  }
14041
14270
  });
14042
- var index_logs = tool2({
14271
+ var index_logs = tool({
14043
14272
  description: "Get recent debug logs from the codebase indexer. Shows timestamped log entries with level and category. Requires debug.enabled=true in config.",
14044
14273
  args: {
14045
- limit: z2.number().optional().default(20).describe("Maximum number of log entries to return"),
14046
- category: z2.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
14047
- level: z2.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
14274
+ limit: z3.number().optional().default(20).describe("Maximum number of log entries to return"),
14275
+ category: z3.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
14276
+ level: z3.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
14048
14277
  },
14049
14278
  async execute(args, context) {
14050
14279
  return (await getIndexLogs(context?.worktree, DEFAULT_HOST, args)).text;
14051
14280
  }
14052
14281
  });
14053
- var find_similar = tool2({
14282
+ var find_similar = tool({
14054
14283
  description: "Find code similar to a given snippet. Use for duplicate detection, pattern discovery, or refactoring prep. Paste code and find semantically similar implementations elsewhere in the codebase.",
14055
14284
  args: {
14056
- code: z2.string().describe("The code snippet to find similar code for"),
14057
- limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
14058
- fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14059
- directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14060
- chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14061
- excludeFile: z2.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
14285
+ code: z3.string().describe("The code snippet to find similar code for"),
14286
+ limit: z3.number().optional().default(10).describe("Maximum number of results to return"),
14287
+ fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14288
+ directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14289
+ chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14290
+ excludeFile: z3.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
14062
14291
  },
14063
14292
  async execute(args, context) {
14064
14293
  const results = await findSimilarCode(context?.worktree, DEFAULT_HOST, args.code, {
@@ -14074,18 +14303,18 @@ var find_similar = tool2({
14074
14303
  return formatSearchResults(results);
14075
14304
  }
14076
14305
  });
14077
- var codebase_search = tool2({
14306
+ var codebase_search = tool({
14078
14307
  description: "Search codebase by MEANING, not keywords. Returns full code content. Use when you need to see actual implementation. For just finding WHERE code is (saves ~90% tokens), use codebase_peek instead. For known identifiers like 'validateToken', use grep - it's faster.",
14079
14308
  args: {
14080
- query: z2.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
14081
- limit: z2.number().optional().default(5).describe("Maximum number of results to return"),
14082
- fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14083
- directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14084
- chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14085
- contextLines: z2.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
14086
- blameAuthor: z2.string().optional().describe("Filter by git blame author name or email"),
14087
- blameSha: z2.string().optional().describe("Filter by git blame commit SHA or prefix"),
14088
- blameSince: z2.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
14309
+ query: z3.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
14310
+ limit: z3.number().optional().default(5).describe("Maximum number of results to return"),
14311
+ fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14312
+ directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14313
+ chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14314
+ contextLines: z3.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
14315
+ blameAuthor: z3.string().optional().describe("Filter by git blame author name or email"),
14316
+ blameSha: z3.string().optional().describe("Filter by git blame commit SHA or prefix"),
14317
+ blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
14089
14318
  },
14090
14319
  async execute(args, context) {
14091
14320
  const results = await searchCodebase(context?.worktree, DEFAULT_HOST, args.query, {
@@ -14104,13 +14333,13 @@ var codebase_search = tool2({
14104
14333
  return formatSearchResults(results, "score");
14105
14334
  }
14106
14335
  });
14107
- var implementation_lookup = tool2({
14336
+ var implementation_lookup = tool({
14108
14337
  description: "Jump to symbol definition. Find WHERE something is defined. Returns the authoritative source location(s) for a function, class, method, type, or variable. Prefers real implementation files over tests, docs, examples, and fixtures. Use when you need the definition site, not all usages.",
14109
14338
  args: {
14110
- query: z2.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
14111
- limit: z2.number().optional().default(5).describe("Maximum number of results"),
14112
- fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
14113
- directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
14339
+ query: z3.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
14340
+ limit: z3.number().optional().default(5).describe("Maximum number of results"),
14341
+ fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
14342
+ directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
14114
14343
  },
14115
14344
  async execute(args, context) {
14116
14345
  const results = await implementationLookup(context?.worktree, DEFAULT_HOST, args.query, {
@@ -14121,13 +14350,13 @@ var implementation_lookup = tool2({
14121
14350
  return formatDefinitionLookup(results, args.query);
14122
14351
  }
14123
14352
  });
14124
- var call_graph = tool2({
14353
+ var call_graph = tool({
14125
14354
  description: "Query the call graph to find callers or callees of a function/method. Use to understand code flow and dependencies between functions. Supports relationship types: Call, MethodCall, Constructor, Import, Inherits, Implements.",
14126
14355
  args: {
14127
- name: z2.string().describe("Function or method name to query"),
14128
- direction: z2.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
14129
- symbolId: z2.string().optional().describe("Symbol ID (required for 'callees' direction, returned by previous call_graph queries)"),
14130
- relationshipType: z2.enum(RELATIONSHIP_TYPE_VALUES).optional().describe("Filter by relationship type. Omit to show all.")
14356
+ name: z3.string().describe("Function or method name to query"),
14357
+ direction: z3.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
14358
+ symbolId: z3.string().optional().describe("Symbol ID (required for 'callees' direction, returned by previous call_graph queries)"),
14359
+ relationshipType: z3.enum(RELATIONSHIP_TYPE_VALUES).optional().describe("Filter by relationship type. Omit to show all.")
14131
14360
  },
14132
14361
  async execute(args, context) {
14133
14362
  if (args.direction === "callees") {
@@ -14141,49 +14370,49 @@ var call_graph = tool2({
14141
14370
  return formatCallGraphCallers(args.name, callers, args.relationshipType);
14142
14371
  }
14143
14372
  });
14144
- var call_graph_path = tool2({
14373
+ var call_graph_path = tool({
14145
14374
  description: "Find the shortest connection path between two symbols in the call graph. Given a source and target function/method name, returns the chain of calls connecting them.",
14146
14375
  args: {
14147
- from: z2.string().describe("Source function/method name (starting point)"),
14148
- to: z2.string().describe("Target function/method name (destination)"),
14149
- maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
14376
+ from: z3.string().describe("Source function/method name (starting point)"),
14377
+ to: z3.string().describe("Target function/method name (destination)"),
14378
+ maxDepth: z3.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
14150
14379
  },
14151
14380
  async execute(args, context) {
14152
14381
  const path24 = await getCallGraphPath(context?.worktree, DEFAULT_HOST, args.from, args.to, args.maxDepth);
14153
14382
  return formatCallGraphPath(args.from, args.to, path24);
14154
14383
  }
14155
14384
  });
14156
- var add_knowledge_base = tool2({
14385
+ var add_knowledge_base = tool({
14157
14386
  description: "Add a folder as a knowledge base to the semantic search index. The folder will be indexed alongside the main project code. Supports absolute paths or relative paths (relative to the project root).",
14158
14387
  args: {
14159
- path: z2.string().describe("Path to the folder to add as a knowledge base (absolute or relative to the project root)")
14388
+ path: z3.string().describe("Path to the folder to add as a knowledge base (absolute or relative to the project root)")
14160
14389
  },
14161
14390
  async execute(args, context) {
14162
14391
  return addKnowledgeBase(context?.worktree, DEFAULT_HOST, args.path);
14163
14392
  }
14164
14393
  });
14165
- var list_knowledge_bases = tool2({
14394
+ var list_knowledge_bases = tool({
14166
14395
  description: "List all configured knowledge base folders that are indexed alongside the main project.",
14167
14396
  args: {},
14168
14397
  async execute(_args, context) {
14169
14398
  return listKnowledgeBases(context?.worktree, DEFAULT_HOST);
14170
14399
  }
14171
14400
  });
14172
- var remove_knowledge_base = tool2({
14401
+ var remove_knowledge_base = tool({
14173
14402
  description: "Remove a knowledge base folder from the semantic search index.",
14174
14403
  args: {
14175
- path: z2.string().describe("Path of the knowledge base to remove (must match the configured path exactly)")
14404
+ path: z3.string().describe("Path of the knowledge base to remove (must match the configured path exactly)")
14176
14405
  },
14177
14406
  async execute(args, context) {
14178
14407
  return removeKnowledgeBase(context?.worktree, DEFAULT_HOST, args.path.trim());
14179
14408
  }
14180
14409
  });
14181
- var index_visualize = tool2({
14410
+ var index_visualize = tool({
14182
14411
  description: "Generate an interactive HTML visualization of recent code movement and the call graph. Starts with temporal onboarding context from Git history, then supports module, symbol, hotspot, and cycle drill-down.",
14183
14412
  args: {
14184
- directory: z2.string().optional().describe("Filter to symbols in this directory (e.g., 'src/services')"),
14185
- maxNodes: z2.number().optional().default(5e3).describe("Maximum nodes to include (default 5000)"),
14186
- includeOrphans: z2.boolean().optional().default(false).describe("Include symbols with no call relationships")
14413
+ directory: z3.string().optional().describe("Filter to symbols in this directory (e.g., 'src/services')"),
14414
+ maxNodes: z3.number().optional().default(5e3).describe("Maximum nodes to include (default 5000)"),
14415
+ includeOrphans: z3.boolean().optional().default(false).describe("Include symbols with no call relationships")
14187
14416
  },
14188
14417
  async execute(args, context) {
14189
14418
  const projectRoot = context?.worktree ?? process.cwd();
@@ -14204,7 +14433,7 @@ var index_visualize = tool2({
14204
14433
  }
14205
14434
  const html = generateVisualizationHtml(vizData);
14206
14435
  const outputPath = path21.join(os5.tmpdir(), `call-graph-${Date.now()}.html`);
14207
- writeFileSync5(outputPath, html, "utf-8");
14436
+ writeFileSync4(outputPath, html, "utf-8");
14208
14437
  let result = `Temporal call graph visualization generated: ${outputPath}
14209
14438
 
14210
14439
  `;