@takara-ai/miru-code 0.4.11 → 0.5.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/src/utils.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { join, relative, resolve, sep } from "node:path";
2
+ import { applySnippetsToResults, searchSnippetsEnabled } from "./snippet.ts";
2
3
  import type { Chunk, ContentType, SearchResult } from "./types.ts";
3
4
  import { chunkToDict } from "./types.ts";
4
5
 
@@ -66,29 +67,25 @@ export function resolveChunk(
66
67
  return fallback;
67
68
  }
68
69
 
69
- export type SearchGuidance = {
70
- sufficient: boolean;
71
- reason: string;
72
- suggested_next: "answer" | "expand" | "find_related" | "read_to_edit";
73
- expand?: { file_path: string; line: number };
74
- };
75
-
76
- export type ExpandGuidance = {
77
- sufficient: boolean;
78
- reason: string;
79
- suggested_next: "answer" | "search";
80
- };
81
-
82
70
  export type ExpandResults = {
83
71
  file_path: string;
84
72
  line: number;
85
73
  chunk_count: number;
86
- guidance: ExpandGuidance;
87
74
  anchor: Record<string, unknown> | null;
88
75
  chunks: Record<string, unknown>[];
89
76
  };
90
77
 
91
78
  export const DEFAULT_MCP_TOP_K = 3;
79
+ export const MAX_MCP_TOP_K = 10;
80
+
81
+ /** Clamp MCP top_k to a sane range; omit for the default. */
82
+ export function clampMcpTopK(topK?: number): number {
83
+ const value = topK ?? DEFAULT_MCP_TOP_K;
84
+ if (!Number.isFinite(value) || value < 1) {
85
+ return DEFAULT_MCP_TOP_K;
86
+ }
87
+ return Math.min(Math.floor(value), MAX_MCP_TOP_K);
88
+ }
92
89
 
93
90
  function chunkToResponseDict(chunk: Chunk, repoRoot?: string | null): Record<string, unknown> {
94
91
  const dict = chunkToDict(chunk);
@@ -158,94 +155,38 @@ export function expandChunksAtLine(
158
155
  return { anchor, chunks: fileChunks.slice(start, end) };
159
156
  }
160
157
 
161
- export function assessSearchGuidance(query: string, results: SearchResult[]): SearchGuidance {
162
- if (results.length === 0) {
163
- return {
164
- sufficient: false,
165
- reason: "No results returned.",
166
- suggested_next: "find_related",
167
- };
168
- }
169
-
170
- const top = results[0];
171
- if (!top) {
172
- return {
173
- sufficient: false,
174
- reason: "No results returned.",
175
- suggested_next: "find_related",
176
- };
177
- }
178
-
179
- const topChunk = top.chunk;
180
- const expandHint = { file_path: topChunk.file_path, line: topChunk.start_line };
181
- const second = results[1];
182
- const scoreRatio = second && second.score > 0 ? top.score / second.score : Number.POSITIVE_INFINITY;
183
- const strongLead = results.length === 1 || scoreRatio >= 1.25;
184
-
185
- const sameFileHits = results.filter((result) => result.chunk.file_path === topChunk.file_path).length;
186
- const uniqueFiles = new Set(results.map((result) => result.chunk.file_path)).size;
187
- const lineSpan = topChunk.end_line - topChunk.start_line + 1;
188
- const narrowQuery = /\b(why|how|what|when|where|does|bug|error|fail)\b/i.test(query);
189
-
190
- if (sameFileHits > 1 && lineSpan < 40) {
191
- return {
192
- sufficient: false,
193
- reason: `${sameFileHits} hits in ${topChunk.file_path}; slices may be incomplete.`,
194
- suggested_next: "expand",
195
- expand: expandHint,
196
- };
197
- }
198
-
199
- if (lineSpan < 12 && sameFileHits >= 1) {
200
- return {
201
- sufficient: false,
202
- reason: "Top hit is a short slice; more context may be in adjacent chunks.",
203
- suggested_next: "expand",
204
- expand: expandHint,
205
- };
206
- }
207
-
208
- if (uniqueFiles > 2 && !strongLead) {
209
- return {
210
- sufficient: false,
211
- reason: "Results span several files; pick the best hit or use find_related to trace neighbors.",
212
- suggested_next: "find_related",
213
- expand: expandHint,
214
- };
215
- }
216
-
217
- if (strongLead && (narrowQuery || lineSpan >= 15)) {
218
- return {
219
- sufficient: true,
220
- reason:
221
- results.length === 1
222
- ? "Single focused hit — answer from this chunk unless you are editing."
223
- : `Top hit leads (${scoreRatio === Number.POSITIVE_INFINITY ? "only result" : `${scoreRatio.toFixed(1)}x`}).`,
224
- suggested_next: "answer",
225
- };
226
- }
227
-
228
- return {
229
- sufficient: true,
230
- reason: "Top hit is likely enough; call expand if the chunk looks cut off.",
231
- suggested_next: "answer",
232
- expand: expandHint,
233
- };
234
- }
235
-
236
158
  export function formatResults(
237
159
  query: string,
238
160
  results: SearchResult[],
239
- options?: { repoRoot?: string | null },
240
- ): { query: string; guidance: SearchGuidance; results: Record<string, unknown>[] } {
161
+ options?: { repoRoot?: string | null; snippet?: boolean },
162
+ ): { query: string; results: Record<string, unknown>[] } {
241
163
  const repoRoot = options?.repoRoot ?? null;
164
+ const useSnippet = options?.snippet ?? searchSnippetsEnabled();
165
+ const payload = useSnippet ? applySnippetsToResults(results, query) : null;
166
+
242
167
  return {
243
168
  query,
244
- guidance: assessSearchGuidance(query, results),
245
- results: results.map((result) => ({
246
- chunk: chunkToResponseDict(result.chunk, repoRoot),
247
- score: result.score,
248
- })),
169
+ results: results.map((result, index) => {
170
+ const entry = payload?.[index];
171
+ const chunk = entry?.result.chunk ?? result.chunk;
172
+ const dict = chunkToResponseDict(chunk, repoRoot);
173
+ if (entry?.meta.truncated) {
174
+ return {
175
+ chunk: {
176
+ ...dict,
177
+ truncated: true,
178
+ anchor_line: entry.meta.anchor_line,
179
+ full_start_line: entry.meta.full_start_line,
180
+ full_end_line: entry.meta.full_end_line,
181
+ },
182
+ score: result.score,
183
+ };
184
+ }
185
+ return {
186
+ chunk: dict,
187
+ score: result.score,
188
+ };
189
+ }),
249
190
  };
250
191
  }
251
192
 
@@ -258,22 +199,11 @@ export function formatExpandResults(
258
199
  ): ExpandResults {
259
200
  const repoRoot = options?.repoRoot ?? null;
260
201
  const indexedPath = toIndexedFilePath(filePath, repoRoot) || filePath;
261
- const suggestedNext: ExpandGuidance["suggested_next"] = expanded.length > 0 ? "answer" : "search";
262
202
 
263
203
  return {
264
204
  file_path: indexedPath,
265
205
  line,
266
206
  chunk_count: expanded.length,
267
- guidance: {
268
- sufficient: expanded.length > 1,
269
- reason:
270
- expanded.length === 0
271
- ? "No chunks at this location."
272
- : expanded.length === 1
273
- ? "Single chunk at location; call expand with higher before/after if still truncated."
274
- : `Returned ${expanded.length} adjacent chunks in ${indexedPath}.`,
275
- suggested_next: suggestedNext,
276
- },
277
207
  anchor: anchor ? chunkToResponseDict(anchor, repoRoot) : null,
278
208
  chunks: expanded.map((chunk) => chunkToResponseDict(chunk, repoRoot)),
279
209
  };