@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/package.json +4 -1
- package/src/agents/claude.md +0 -27
- package/src/agents/copilot.md +0 -12
- package/src/agents/cursor.md +0 -26
- package/src/agents/gemini.md +0 -14
- package/src/agents/kiro.md +0 -6
- package/src/agents/opencode.md +0 -6
- package/src/agents.ts +40 -1
- package/src/index/create.ts +13 -0
- package/src/index/entry-chunks.ts +71 -0
- package/src/installer/hooks/search-guard.ts +3 -4
- package/src/installer/prompt.ts +32 -8
- package/src/installer/search-policy.ts +91 -31
- package/src/mcp/server.ts +36 -27
- package/src/ranking/boosting.ts +11 -0
- package/src/ranking/features.ts +11 -0
- package/src/ranking/location.ts +241 -0
- package/src/search.ts +4 -1
- package/src/snippet.ts +154 -0
- package/src/utils.ts +37 -107
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;
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
chunk
|
|
247
|
-
|
|
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
|
};
|