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