opencode-codebase-index 0.15.0 → 0.16.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.
@@ -8902,10 +8902,19 @@ var Indexer = class {
8902
8902
  });
8903
8903
  const embeddingStartTime = import_perf_hooks.performance.now();
8904
8904
  const embeddingQuery = stripFilePathHint(query);
8905
- const embedding = await this.getQueryEmbedding(embeddingQuery, provider);
8905
+ let embedding;
8906
+ try {
8907
+ embedding = await this.getQueryEmbedding(embeddingQuery, provider);
8908
+ } catch (error) {
8909
+ this.logger.warn("Query embedding failed; falling back to keyword-only search", {
8910
+ query,
8911
+ error: getErrorMessage2(error),
8912
+ action: "Check the embedding provider configuration and retry search after restoring provider health."
8913
+ });
8914
+ }
8906
8915
  const embeddingMs = import_perf_hooks.performance.now() - embeddingStartTime;
8907
8916
  const vectorStartTime = import_perf_hooks.performance.now();
8908
- const semanticResults = store.search(embedding, maxResults * 4);
8917
+ const semanticResults = embedding ? store.search(embedding, maxResults * 4) : [];
8909
8918
  const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
8910
8919
  const keywordStartTime = import_perf_hooks.performance.now();
8911
8920
  const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
@@ -8940,12 +8949,13 @@ var Indexer = class {
8940
8949
  });
8941
8950
  }
8942
8951
  const fusionStartTime = import_perf_hooks.performance.now();
8952
+ const rankingHybridWeight = embedding === void 0 && fusionStrategy === "weighted" ? 1 : effectiveHybridWeight;
8943
8953
  const combined = rankHybridResults(query, semanticCandidates, keywordCandidates, {
8944
8954
  fusionStrategy,
8945
8955
  rrfK,
8946
8956
  rerankTopN,
8947
8957
  limit: maxResults,
8948
- hybridWeight: effectiveHybridWeight,
8958
+ hybridWeight: rankingHybridWeight,
8949
8959
  prioritizeSourcePaths: sourceIntent
8950
8960
  });
8951
8961
  const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
@@ -10065,6 +10075,17 @@ function calculatePercentage(progress) {
10065
10075
  if (progress.phase === "storing") return 95;
10066
10076
  return 0;
10067
10077
  }
10078
+ function formatCodebasePeek(results) {
10079
+ if (results.length === 0) {
10080
+ return "No matching code found. Try a different query or run index_codebase first.";
10081
+ }
10082
+ const formatted = results.map((r, idx) => {
10083
+ const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
10084
+ const name = r.name ? `"${r.name}"` : "(anonymous)";
10085
+ return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})${formatBlame(r)}`;
10086
+ });
10087
+ return formatted.join("\n");
10088
+ }
10068
10089
  function formatHealthCheck(result) {
10069
10090
  if (result.resetCorruptedIndex) {
10070
10091
  return result.warning ?? "Detected a corrupted local index and reset it. Run index_codebase to rebuild search data.";
@@ -10651,10 +10672,67 @@ function projectRoot2(ctx) {
10651
10672
  return ctx?.cwd ?? process.cwd();
10652
10673
  }
10653
10674
  function codebaseIndexPiExtension(pi) {
10675
+ pi.registerTool({
10676
+ name: "codebase_context",
10677
+ label: "Codebase Context",
10678
+ description: "PREFERRED FIRST TOOL for any repository question. Check index_status when freshness is unknown, then use this tool for low-token location discovery and dependency flow.",
10679
+ parameters: import_typebox2.Type.Object({
10680
+ query: import_typebox2.Type.String({ description: "Natural language description of what code you're trying to locate" }),
10681
+ from: import_typebox2.Type.Optional(import_typebox2.Type.String({ description: "Source symbol when asking for a dependency path." })),
10682
+ to: import_typebox2.Type.Optional(import_typebox2.Type.String({ description: "Target symbol when asking for a dependency path." })),
10683
+ symbol: import_typebox2.Type.Optional(import_typebox2.Type.String({ description: "Exact symbol name for an authoritative definition lookup." })),
10684
+ limit: import_typebox2.Type.Optional(import_typebox2.Type.Number({ description: "Maximum results (default: 10)" })),
10685
+ maxDepth: import_typebox2.Type.Optional(import_typebox2.Type.Number({ description: "Maximum call-graph traversal depth for from/to paths" })),
10686
+ fileType: import_typebox2.Type.Optional(import_typebox2.Type.String({ description: "Filter by file extension, e.g., ts, py, rs" })),
10687
+ directory: import_typebox2.Type.Optional(import_typebox2.Type.String({ description: "Filter by directory path" }))
10688
+ }),
10689
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
10690
+ const root = projectRoot2(ctx);
10691
+ if (params.from && params.to) {
10692
+ const path17 = await getCallGraphPath(root, HOST2, params.from, params.to, params.maxDepth);
10693
+ if (path17.length > 0) {
10694
+ return text2(formatCallGraphPath(params.from, params.to, path17), path17);
10695
+ }
10696
+ const { callers } = await getCallGraphData(root, HOST2, {
10697
+ name: params.to,
10698
+ direction: "callers"
10699
+ });
10700
+ const directEdge = callers.find((edge) => edge.fromSymbolName === params.from);
10701
+ if (directEdge) {
10702
+ const location = directEdge.fromSymbolFilePath ? ` at ${directEdge.fromSymbolFilePath}:${directEdge.line}` : "";
10703
+ return text2(
10704
+ `Direct path: ${params.from} --${directEdge.callType}--> ${params.to}${location} (edge is ${directEdge.isResolved ? "resolved" : "unresolved"}).`,
10705
+ path17
10706
+ );
10707
+ }
10708
+ return text2(formatCallGraphPath(params.from, params.to, path17), path17);
10709
+ }
10710
+ if (params.symbol) {
10711
+ const results2 = await implementationLookup(root, HOST2, params.symbol, {
10712
+ limit: params.limit ?? 10,
10713
+ fileType: params.fileType,
10714
+ directory: params.directory
10715
+ });
10716
+ return text2(formatDefinitionLookup(results2, params.symbol), results2);
10717
+ }
10718
+ const results = await searchCodebase(root, HOST2, params.query, {
10719
+ limit: params.limit ?? 10,
10720
+ fileType: params.fileType,
10721
+ directory: params.directory,
10722
+ metadataOnly: true
10723
+ });
10724
+ if (results.length === 0) {
10725
+ return text2("No matching code found. Try a different query or run index_status/index_codebase first.");
10726
+ }
10727
+ return text2(`Found ${results.length} locations for "${params.query}":
10728
+
10729
+ ${formatCodebasePeek(results)}`, results);
10730
+ }
10731
+ });
10654
10732
  pi.registerTool({
10655
10733
  name: "codebase_search",
10656
10734
  label: "Codebase Search",
10657
- description: "Semantic search over the indexed codebase. Describe behavior, not syntax.",
10735
+ description: "Use this after codebase_context when you need semantic content, not just locations. Describe behavior, not syntax.",
10658
10736
  parameters: import_typebox2.Type.Object({
10659
10737
  query: import_typebox2.Type.String({ description: "Natural language description of what code you're looking for" }),
10660
10738
  limit: import_typebox2.Type.Optional(import_typebox2.Type.Number({ description: "Maximum results (default: 10)" })),
@@ -10674,7 +10752,7 @@ function codebaseIndexPiExtension(pi) {
10674
10752
  pi.registerTool({
10675
10753
  name: "codebase_peek",
10676
10754
  label: "Codebase Peek",
10677
- description: "Semantic search returning only metadata (file, lines, symbol) to save tokens.",
10755
+ description: "LOW-TOKEN location-first retrieval. Prefer codebase_context first, then use this for cheap conceptual lookup.",
10678
10756
  parameters: import_typebox2.Type.Object({
10679
10757
  query: import_typebox2.Type.String(),
10680
10758
  limit: import_typebox2.Type.Optional(import_typebox2.Type.Number()),
@@ -10693,7 +10771,7 @@ function codebaseIndexPiExtension(pi) {
10693
10771
  pi.registerTool({
10694
10772
  name: "find_similar",
10695
10773
  label: "Find Similar Code",
10696
- description: "Find code similar to a snippet for duplicate detection and pattern discovery.",
10774
+ description: "Find code similar to a snippet for duplicate detection, pattern discovery, and refactor planning.",
10697
10775
  parameters: import_typebox2.Type.Object({
10698
10776
  code: import_typebox2.Type.String({ description: "Code snippet to compare" }),
10699
10777
  limit: import_typebox2.Type.Optional(import_typebox2.Type.Number()),
@@ -10710,7 +10788,7 @@ function codebaseIndexPiExtension(pi) {
10710
10788
  pi.registerTool({
10711
10789
  name: "implementation_lookup",
10712
10790
  label: "Implementation Lookup",
10713
- description: "Find likely symbol definitions or implementations by name or natural language.",
10791
+ description: "Find likely symbol definitions or implementations after codebase_context identifies a symbol.",
10714
10792
  parameters: import_typebox2.Type.Object({
10715
10793
  query: import_typebox2.Type.String(),
10716
10794
  limit: import_typebox2.Type.Optional(import_typebox2.Type.Number()),
@@ -10725,7 +10803,7 @@ function codebaseIndexPiExtension(pi) {
10725
10803
  pi.registerTool({
10726
10804
  name: "index_codebase",
10727
10805
  label: "Index Codebase",
10728
- description: "Build or refresh the semantic codebase index.",
10806
+ description: "Build or refresh the semantic codebase index. Run index_status when freshness is unknown.",
10729
10807
  parameters: import_typebox2.Type.Object({
10730
10808
  force: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: false })),
10731
10809
  estimateOnly: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: false })),
@@ -10751,7 +10829,7 @@ function codebaseIndexPiExtension(pi) {
10751
10829
  pi.registerTool({
10752
10830
  name: "index_health_check",
10753
10831
  label: "Index Health Check",
10754
- description: "Garbage collect orphaned embeddings/chunks and report health.",
10832
+ description: "Garbage collect orphaned embeddings/chunks and report index health status.",
10755
10833
  parameters: import_typebox2.Type.Object({}),
10756
10834
  async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
10757
10835
  const result = await runIndexHealthCheck(projectRoot2(ctx), HOST2);
@@ -10791,6 +10869,11 @@ function codebaseIndexPiExtension(pi) {
10791
10869
  }
10792
10870
  });
10793
10871
  registerPiCallGraphTools(pi);
10872
+ pi.on("before_agent_start", (event) => ({
10873
+ systemPrompt: `${event.systemPrompt}
10874
+
10875
+ Check index_status first when index readiness is unknown. For repository questions, call codebase_context before search/grep/bash/read-style broad reads. Use implementation_lookup for known symbols and call_graph/call_graph_path after endpoints are identified for dependency flow. Avoid broad tool calls until semantic locations are known.`
10876
+ }));
10794
10877
  pi.registerTool({
10795
10878
  name: "pr_impact",
10796
10879
  label: "PR Impact",