@takara-ai/miru-code 1.1.2 → 1.2.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.
@@ -0,0 +1,248 @@
1
+ import { AGENT_TARGETS, type AgentTarget, type InstallAction } from "./agents.ts";
2
+ import { removeTomlBlock, stripJsonComments } from "./config.ts";
3
+
4
+ export const MCP_BENCHMARK_FLAG = "--benchmark";
5
+
6
+ const CODEX_MCP_HEADER = "[mcp_servers.miru]";
7
+
8
+ function codexMcpBlock(enabled: boolean): string {
9
+ const args = enabled
10
+ ? `["@takara-ai/miru-code", "${MCP_BENCHMARK_FLAG}"]`
11
+ : '["@takara-ai/miru-code"]';
12
+ return `${CODEX_MCP_HEADER}\ncommand = "bunx"\nargs = ${args}\n`;
13
+ }
14
+
15
+ export function withBenchmarkFlag(list: string[], enabled: boolean): string[] {
16
+ const without = list.filter((item) => item !== MCP_BENCHMARK_FLAG);
17
+ return enabled ? [...without, MCP_BENCHMARK_FLAG] : without;
18
+ }
19
+
20
+ export function listHasBenchmarkFlag(list: unknown): boolean {
21
+ return Array.isArray(list) && list.some((item) => item === MCP_BENCHMARK_FLAG);
22
+ }
23
+
24
+ /** Toggle `--benchmark` on a JSON MCP server entry (`args` and/or array `command`). */
25
+ export function applyBenchmarkFlagToMcpEntry(
26
+ entry: Record<string, unknown>,
27
+ enabled: boolean,
28
+ ): { entry: Record<string, unknown>; changed: boolean; enabled: boolean } {
29
+ const next: Record<string, unknown> = { ...entry };
30
+ let changed = false;
31
+
32
+ if (Array.isArray(next.args)) {
33
+ const args = next.args.filter((item): item is string => typeof item === "string");
34
+ const updated = withBenchmarkFlag(args, enabled);
35
+ if (JSON.stringify(updated) !== JSON.stringify(args)) {
36
+ next.args = updated;
37
+ changed = true;
38
+ }
39
+ }
40
+
41
+ if (Array.isArray(next.command)) {
42
+ const command = next.command.filter((item): item is string => typeof item === "string");
43
+ const updated = withBenchmarkFlag(command, enabled);
44
+ if (JSON.stringify(updated) !== JSON.stringify(command)) {
45
+ next.command = updated;
46
+ changed = true;
47
+ }
48
+ }
49
+
50
+ return {
51
+ entry: next,
52
+ changed,
53
+ enabled: mcpEntryHasBenchmark(next),
54
+ };
55
+ }
56
+
57
+ export function mcpEntryHasBenchmark(entry: Record<string, unknown>): boolean {
58
+ return listHasBenchmarkFlag(entry.args) || listHasBenchmarkFlag(entry.command);
59
+ }
60
+
61
+ /**
62
+ * Keep `--benchmark` on the canonical install entry when the existing MCP
63
+ * entry already had it (so `miru install` does not silently turn mode off).
64
+ */
65
+ export function withPreservedBenchmarkFlag(
66
+ canonical: Record<string, unknown>,
67
+ existing: Record<string, unknown> | null | undefined,
68
+ ): Record<string, unknown> {
69
+ if (!existing || !mcpEntryHasBenchmark(existing)) {
70
+ return canonical;
71
+ }
72
+ return applyBenchmarkFlagToMcpEntry(canonical, true).entry;
73
+ }
74
+
75
+ export interface BenchmarkModeTargetResult {
76
+ agent: string;
77
+ path: string;
78
+ action: InstallAction | "enabled" | "disabled" | "missing";
79
+ enabled: boolean;
80
+ }
81
+
82
+ async function readJsonObject(path: string): Promise<Record<string, unknown> | null> {
83
+ if (!(await Bun.file(path).exists())) {
84
+ return null;
85
+ }
86
+ try {
87
+ const text = stripJsonComments(await Bun.file(path).text());
88
+ const parsed = JSON.parse(text) as unknown;
89
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
90
+ return null;
91
+ }
92
+ return parsed as Record<string, unknown>;
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+
98
+ async function writeJsonObject(path: string, value: Record<string, unknown>): Promise<void> {
99
+ await Bun.write(path, `${JSON.stringify(value, null, 2)}\n`);
100
+ }
101
+
102
+ async function statusForToml(path: string): Promise<boolean | null> {
103
+ if (!(await Bun.file(path).exists())) {
104
+ return null;
105
+ }
106
+ const text = await Bun.file(path).text();
107
+ if (!text.includes(CODEX_MCP_HEADER)) {
108
+ return null;
109
+ }
110
+ return text.includes(`"${MCP_BENCHMARK_FLAG}"`) || text.includes(`'${MCP_BENCHMARK_FLAG}'`);
111
+ }
112
+
113
+ async function setTomlBenchmark(
114
+ path: string,
115
+ enabled: boolean,
116
+ ): Promise<InstallAction | "missing"> {
117
+ if (!(await Bun.file(path).exists())) {
118
+ return "missing";
119
+ }
120
+ const text = await Bun.file(path).text();
121
+ if (!text.includes(CODEX_MCP_HEADER)) {
122
+ return "missing";
123
+ }
124
+ const current = await statusForToml(path);
125
+ if (current === enabled) {
126
+ return "unchanged";
127
+ }
128
+
129
+ // Replace the miru block with the desired args, preserving the rest of the file.
130
+ await removeTomlBlock(path);
131
+ const existed = await Bun.file(path).exists();
132
+ const existing = existed ? await Bun.file(path).text() : "";
133
+ const base = existing.replace(/\n+$/, "");
134
+ const block = codexMcpBlock(enabled);
135
+ const next = base.length > 0 ? `${base}\n\n${block}` : block;
136
+ await Bun.write(path, next.endsWith("\n") ? next : `${next}\n`);
137
+ return "updated";
138
+ }
139
+
140
+ async function statusForJson(agent: AgentTarget): Promise<boolean | null> {
141
+ const mcp = agent.mcp;
142
+ if (mcp?.format !== "json") {
143
+ return null;
144
+ }
145
+ const parsed = await readJsonObject(mcp.path);
146
+ if (!parsed) {
147
+ return null;
148
+ }
149
+ const section = parsed[mcp.key];
150
+ if (!section || typeof section !== "object" || Array.isArray(section)) {
151
+ return null;
152
+ }
153
+ const entry = (section as Record<string, unknown>)[mcp.memberKey];
154
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
155
+ return null;
156
+ }
157
+ return mcpEntryHasBenchmark(entry as Record<string, unknown>);
158
+ }
159
+
160
+ async function setJsonBenchmark(
161
+ agent: AgentTarget,
162
+ enabled: boolean,
163
+ ): Promise<InstallAction | "missing"> {
164
+ const mcp = agent.mcp;
165
+ if (mcp?.format !== "json") {
166
+ return "missing";
167
+ }
168
+ const parsed = await readJsonObject(mcp.path);
169
+ if (!parsed) {
170
+ return "missing";
171
+ }
172
+ const sectionRaw = parsed[mcp.key];
173
+ if (!sectionRaw || typeof sectionRaw !== "object" || Array.isArray(sectionRaw)) {
174
+ return "missing";
175
+ }
176
+ const section = sectionRaw as Record<string, unknown>;
177
+ const entryRaw = section[mcp.memberKey];
178
+ if (!entryRaw || typeof entryRaw !== "object" || Array.isArray(entryRaw)) {
179
+ return "missing";
180
+ }
181
+
182
+ const applied = applyBenchmarkFlagToMcpEntry(entryRaw as Record<string, unknown>, enabled);
183
+ if (!applied.changed) {
184
+ return "unchanged";
185
+ }
186
+ section[mcp.memberKey] = applied.entry;
187
+ parsed[mcp.key] = section;
188
+ await writeJsonObject(mcp.path, parsed);
189
+ return "updated";
190
+ }
191
+
192
+ /** Inspect installed Miru MCP configs for `--benchmark`. */
193
+ export async function getBenchmarkModeStatus(): Promise<BenchmarkModeTargetResult[]> {
194
+ const results: BenchmarkModeTargetResult[] = [];
195
+ for (const agent of AGENT_TARGETS) {
196
+ const mcp = agent.mcp;
197
+ if (!mcp) {
198
+ continue;
199
+ }
200
+ if (mcp.format === "toml") {
201
+ const enabled = await statusForToml(mcp.path);
202
+ results.push({
203
+ agent: agent.displayName,
204
+ path: mcp.path,
205
+ action: enabled == null ? "missing" : enabled ? "enabled" : "disabled",
206
+ enabled: enabled === true,
207
+ });
208
+ continue;
209
+ }
210
+ const enabled = await statusForJson(agent);
211
+ results.push({
212
+ agent: agent.displayName,
213
+ path: mcp.path,
214
+ action: enabled == null ? "missing" : enabled ? "enabled" : "disabled",
215
+ enabled: enabled === true,
216
+ });
217
+ }
218
+ return results;
219
+ }
220
+
221
+ /** Enable or disable `--benchmark` on every installed Miru MCP entry. */
222
+ export async function setBenchmarkMode(enabled: boolean): Promise<BenchmarkModeTargetResult[]> {
223
+ const results: BenchmarkModeTargetResult[] = [];
224
+ for (const agent of AGENT_TARGETS) {
225
+ const mcp = agent.mcp;
226
+ if (!mcp) {
227
+ continue;
228
+ }
229
+ if (mcp.format === "toml") {
230
+ const action = await setTomlBenchmark(mcp.path, enabled);
231
+ results.push({
232
+ agent: agent.displayName,
233
+ path: mcp.path,
234
+ action,
235
+ enabled: action === "missing" ? false : enabled,
236
+ });
237
+ continue;
238
+ }
239
+ const action = await setJsonBenchmark(agent, enabled);
240
+ results.push({
241
+ agent: agent.displayName,
242
+ path: mcp.path,
243
+ action,
244
+ enabled: action === "missing" ? false : enabled,
245
+ });
246
+ }
247
+ return results;
248
+ }
@@ -6,6 +6,11 @@ const CODEX_MCP_BLOCK = `[mcp_servers.miru]
6
6
  command = "bunx"
7
7
  args = ["@takara-ai/miru-code"]
8
8
  `;
9
+ /** Same Codex block with `--benchmark` preserved across reinstall. */
10
+ const CODEX_MCP_BLOCK_BENCHMARK = `[mcp_servers.miru]
11
+ command = "bunx"
12
+ args = ["@takara-ai/miru-code", "--benchmark"]
13
+ `;
9
14
 
10
15
  /** Strip line and block comments so JSONC configs can be parsed. */
11
16
  export function stripJsonComments(text: string): string {
@@ -487,12 +492,19 @@ export async function mergeTomlBlock(path: string): Promise<InstallAction> {
487
492
  const existed = await Bun.file(path).exists();
488
493
  const existing = existed ? await Bun.file(path).text() : "";
489
494
 
490
- if (existing.includes(CODEX_MCP_BLOCK.trim())) {
495
+ const defaultBlock = CODEX_MCP_BLOCK.trim();
496
+ const benchmarkBlock = CODEX_MCP_BLOCK_BENCHMARK.trim();
497
+ if (existing.includes(defaultBlock) || existing.includes(benchmarkBlock)) {
491
498
  return "unchanged";
492
499
  }
493
500
 
501
+ const preserveBenchmark =
502
+ existing.includes(CODEX_MCP_HEADER) &&
503
+ (existing.includes('"--benchmark"') || existing.includes("'--benchmark'"));
504
+ const block = preserveBenchmark ? CODEX_MCP_BLOCK_BENCHMARK : CODEX_MCP_BLOCK;
505
+
494
506
  const base = stripTomlSection(existing, CODEX_MCP_HEADER).replace(/\n+$/, "");
495
- const next = base.length > 0 ? `${base}\n\n${CODEX_MCP_BLOCK}` : CODEX_MCP_BLOCK;
507
+ const next = base.length > 0 ? `${base}\n\n${block}` : block;
496
508
  await Bun.write(path, next.endsWith("\n") ? next : `${next}\n`);
497
509
  return existed ? "updated" : "created";
498
510
  }
@@ -3,28 +3,12 @@
3
3
  * Installed to ~/.config/opencode/plugins/miru-search-guard.ts
4
4
  */
5
5
 
6
- const REDIRECT = "Use Miru MCP search (repo = project root) instead of grep/glob/bash exploration.";
6
+ const REDIRECT =
7
+ "Use Miru MCP (repo = project root): `locate` for exact literals, `search` for meaning-based questions — not grep/glob/bash exploration.";
7
8
 
8
9
  const GREP_TOOLS = new Set(["grep", "glob", "codesearch", "codebase_search", "search"]);
9
10
  const SHELL_TOOLS = new Set(["bash", "shell", "sh"]);
10
11
 
11
- function isLiteral(pattern: string): boolean {
12
- const trimmed = pattern.trim();
13
- if (!trimmed) {
14
- return false;
15
- }
16
- if (/^[`'"][^`'"]+[`'"]$/.test(trimmed)) {
17
- return true;
18
- }
19
- if (/^[A-Z][A-Z0-9_]{2,}$/.test(trimmed)) {
20
- return true;
21
- }
22
- if (/^[a-zA-Z_][a-zA-Z0-9_.]*$/.test(trimmed) && trimmed.length <= 48) {
23
- return true;
24
- }
25
- return false;
26
- }
27
-
28
12
  function isExplorationShell(command: string): boolean {
29
13
  const cmd = command.trim();
30
14
  if (!cmd) {
@@ -43,9 +27,11 @@ function shouldBlock(tool: string, args: Record<string, unknown>): boolean {
43
27
  if (name.includes("miru")) {
44
28
  return false;
45
29
  }
30
+ if (name === "glob") {
31
+ return true;
32
+ }
46
33
  if (GREP_TOOLS.has(name)) {
47
- const pattern = String(args.pattern ?? args.query ?? args.regex ?? args.needle ?? "");
48
- return !isLiteral(pattern);
34
+ return true;
49
35
  }
50
36
  if (SHELL_TOOLS.has(name)) {
51
37
  const command = String(args.command ?? args.cmd ?? "");
@@ -2,7 +2,7 @@ import { SEARCH_GUARD_EXPAND_HINT } from "../search-policy.ts";
2
2
 
3
3
  const REDIRECT_PREFIX =
4
4
  "Use Miru MCP instead of built-in search tools (pass project root as `repo`). " +
5
- "Grep/Glob/Shell are only for exact literal confirmation or non-code tasks.";
5
+ "Prefer Miru `locate` for exact literals and `search` for meaning-based questions.";
6
6
 
7
7
  const MIRU_TOOL_RE = /miru/i;
8
8
 
@@ -121,6 +121,9 @@ export function searchGuardBlockReason(
121
121
  toolInput: Record<string, unknown>,
122
122
  ): string {
123
123
  const query = suggestedMiruQuery(toolName, toolInput);
124
+ if (GREP_TOOL_RE.test(toolName) && isLiteralGrepPattern(grepPatternFromInput(toolInput))) {
125
+ return `${REDIRECT_PREFIX} Try Miru \`locate\` with literal "${query.replace(/^['"`]|['"`]$/g, "")}". ${SEARCH_GUARD_EXPAND_HINT}`;
126
+ }
124
127
  return `${REDIRECT_PREFIX} Try Miru \`search\` with query "${query}". ${SEARCH_GUARD_EXPAND_HINT}`;
125
128
  }
126
129
 
@@ -166,10 +169,6 @@ export function evaluateSearchGuard(payload: HookPayload): GuardDecision {
166
169
  }
167
170
 
168
171
  if (GREP_TOOL_RE.test(toolName)) {
169
- const pattern = grepPatternFromInput(toolInput);
170
- if (isLiteralGrepPattern(pattern)) {
171
- return { block: false, reason: "" };
172
- }
173
172
  return { block: true, reason: searchGuardBlockReason(toolName, toolInput) };
174
173
  }
175
174
 
@@ -1,6 +1,7 @@
1
1
  import { unlink } from "node:fs/promises";
2
2
  import { loadAgentTemplate } from "../agents.ts";
3
- import { brandTitle, dim, divider, green, hint, success, writeStdout } from "../cli-ui.ts";
3
+ import { clearBenchmarkHistory } from "../benchmark/history.ts";
4
+ import { brandTitle, dim, divider, green, hint, info, success, writeStdout } from "../cli-ui.ts";
4
5
  import { ensureCredentials } from "../setup.ts";
5
6
  import {
6
7
  AGENT_TARGETS,
@@ -10,6 +11,7 @@ import {
10
11
  type InstallMode,
11
12
  isAgentDetected,
12
13
  } from "./agents.ts";
14
+ import { withPreservedBenchmarkFlag } from "./benchmark-mode.ts";
13
15
  import {
14
16
  mergeJsonMember,
15
17
  mergeTomlBlock,
@@ -17,6 +19,7 @@ import {
17
19
  removeMarked,
18
20
  removeTomlBlock,
19
21
  replaceOrAppendMarked,
22
+ stripJsonComments,
20
23
  } from "./config.ts";
21
24
  import { mergeHooks, removeHooks } from "./hooks/install.ts";
22
25
  import { promptConfirm, promptMultiSelect, requireInteractiveTerminal } from "./prompt.ts";
@@ -54,6 +57,33 @@ const ACTION_ICON: Partial<Record<InstallAction, string>> = {
54
57
  error: dim("✗"),
55
58
  };
56
59
 
60
+ async function readExistingJsonMcpEntry(
61
+ path: string,
62
+ sectionKey: string,
63
+ memberKey: string,
64
+ ): Promise<Record<string, unknown> | null> {
65
+ if (!(await Bun.file(path).exists())) {
66
+ return null;
67
+ }
68
+ try {
69
+ const parsed = JSON.parse(stripJsonComments(await Bun.file(path).text())) as unknown;
70
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
71
+ return null;
72
+ }
73
+ const section = (parsed as Record<string, unknown>)[sectionKey];
74
+ if (!section || typeof section !== "object" || Array.isArray(section)) {
75
+ return null;
76
+ }
77
+ const entry = (section as Record<string, unknown>)[memberKey];
78
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
79
+ return null;
80
+ }
81
+ return entry as Record<string, unknown>;
82
+ } catch {
83
+ return null;
84
+ }
85
+ }
86
+
57
87
  async function applyMcp(agent: AgentTarget, mode: InstallMode): Promise<WriteResult | null> {
58
88
  const mcp = agent.mcp;
59
89
  if (!mcp) {
@@ -66,10 +96,14 @@ async function applyMcp(agent: AgentTarget, mode: InstallMode): Promise<WriteRes
66
96
  return { path: mcp.path, action };
67
97
  }
68
98
 
69
- const action =
70
- mode === "install"
71
- ? await mergeJsonMember(mcp.path, mcp.key, mcp.memberKey, mcp.entry)
72
- : await removeJsonMember(mcp.path, mcp.key, mcp.memberKey);
99
+ if (mode !== "install") {
100
+ const action = await removeJsonMember(mcp.path, mcp.key, mcp.memberKey);
101
+ return { path: mcp.path, action };
102
+ }
103
+
104
+ const existing = await readExistingJsonMcpEntry(mcp.path, mcp.key, mcp.memberKey);
105
+ const entry = withPreservedBenchmarkFlag(mcp.entry, existing);
106
+ const action = await mergeJsonMember(mcp.path, mcp.key, mcp.memberKey, entry);
73
107
  return { path: mcp.path, action };
74
108
  }
75
109
 
@@ -183,7 +217,7 @@ const INTEGRATIONS: Integration[] = [
183
217
  label: "Search hooks",
184
218
  description: "blocks built-in search; routes to Miru MCP",
185
219
  experimental: true,
186
- defaultChecked: true,
220
+ defaultChecked: false,
187
221
  planPath: (agent) => agent.hooksPath,
188
222
  apply: applyHooks,
189
223
  },
@@ -235,6 +269,18 @@ async function apply(
235
269
  writeStdout("");
236
270
  }
237
271
 
272
+ /** Local Miru state cleaned up during uninstall (global, not per-agent). */
273
+ export async function removeUninstallLocalData(): Promise<{
274
+ benchmarkHistoryCleared: boolean;
275
+ benchmarkHistoryPath: string;
276
+ }> {
277
+ const result = await clearBenchmarkHistory();
278
+ return {
279
+ benchmarkHistoryCleared: result.cleared,
280
+ benchmarkHistoryPath: result.path,
281
+ };
282
+ }
283
+
238
284
  export async function runInstaller(mode: InstallMode): Promise<void> {
239
285
  const install = mode === "install";
240
286
  requireInteractiveTerminal(`miru ${mode}`);
@@ -297,6 +343,13 @@ export async function runInstaller(mode: InstallMode): Promise<void> {
297
343
 
298
344
  await apply(mode, chosenAgents, chosenIntegrations);
299
345
 
346
+ if (!install) {
347
+ const local = await removeUninstallLocalData();
348
+ if (local.benchmarkHistoryCleared) {
349
+ info(`Removed benchmark report ${local.benchmarkHistoryPath}`);
350
+ }
351
+ }
352
+
300
353
  success(install ? "Done. Restart agents to apply changes." : "Done. Configuration removed.");
301
354
  writeStdout("");
302
355
  }
@@ -18,7 +18,7 @@ export function buildSearchPolicyTable(native: NativeToolNames): string {
18
18
  | Same file, more context | Miru MCP \`expand\` on \`truncated: true\` | Re-search, ${native.read} whole file |
19
19
  | Similar code elsewhere | Miru MCP \`find_related\` | ${native.grep} chains |
20
20
  | Search docs or config | Miru \`search\` | ${native.grep} README paths |
21
- | Exact literal string in a file? | ${native.grep} | Miru |
21
+ | Exact literal string in a file? | Miru MCP \`locate\` | ${native.grep}, Miru \`search\` |
22
22
  | Edit a known file:line | ${native.read} (after Miru found it) | ${native.read}-before-search |`;
23
23
  }
24
24
 
@@ -29,6 +29,7 @@ ${SNIPPET_GUIDANCE}
29
29
 
30
30
  Use Miru MCP tools:
31
31
  - \`search\` — one call per question; pass project root as \`repo\`
32
+ - \`locate\` — exact substring (env var, symbol, error code); prefer \`mode=count\` or \`locations\`
32
33
  - \`expand\` — more context in the same file when \`truncated: true\` (\`file_path\` + \`anchor_line\`)
33
34
  - \`find_related\` — similar code in other files (hits may also be snippets; use \`expand\` if truncated)
34
35
 
@@ -38,7 +39,6 @@ Stop rules:
38
39
  - ${native.read} is for editing a path Miru already gave you, not for exploration
39
40
 
40
41
  Native tools are allowed ONLY when:
41
- - confirming an exact literal string (env var name, error code, quoted text)
42
42
  - reading a file you already located via Miru, to edit it
43
43
  - searching outside the indexed repo
44
44
 
@@ -49,15 +49,17 @@ export function buildMcpWorkflow(native: NativeToolNames): string {
49
49
  return `### MCP workflow
50
50
 
51
51
  1. Call \`search\` with \`repo\` set to the project root (local path or https:// git URL).
52
- 2. If a hit has \`truncated: true\`, call \`expand\` with \`file_path\` and \`anchor_line\`.
53
- 3. Use \`find_related\` to trace similar code in other files — not for more context in the same file.
54
- 4. ${native.read} via \`absolute_path\` only when editing or when \`expand\` still lacks context.`;
52
+ 2. For exact literals, call \`locate\` (prefer \`mode=count\` or \`locations\`).
53
+ 3. If a hit has \`truncated: true\`, call \`expand\` with \`file_path\` and \`anchor_line\`.
54
+ 4. Use \`find_related\` to trace similar code in other files — not for more context in the same file.
55
+ 5. ${native.read} via \`absolute_path\` only when editing or when \`expand\` still lacks context.`;
55
56
  }
56
57
 
57
58
  const CLI_FALLBACK = `### CLI fallback (no MCP in this session)
58
59
 
59
60
  \`\`\`bash
60
61
  miru search "authentication flow" .
62
+ miru locate DATABASE_URL . --mode locations
61
63
  miru expand src/auth.ts 42 .
62
64
  miru find-related src/auth.ts 42 .
63
65
  \`\`\`
@@ -65,7 +67,7 @@ miru find-related src/auth.ts 42 .
65
67
  If \`miru\` is not on \`$PATH\`, use \`bunx @takara-ai/miru-code\`.`;
66
68
 
67
69
  export function buildSubagentBody(native: NativeToolNames): string {
68
- return `When Miru MCP is available, use MCP \`search\`, \`expand\`, and \`find_related\` — not ${native.explorationDenied} for exploration.
70
+ return `When Miru MCP is available, use MCP \`search\`, \`locate\`, \`expand\`, and \`find_related\` — not ${native.explorationDenied} for exploration.
69
71
 
70
72
  ${buildSearchPolicyBody(native)}
71
73
 
@@ -108,7 +110,7 @@ alwaysApply: true
108
110
 
109
111
  ${SEARCH_POLICY_BODY}
110
112
 
111
- When Miru MCP is connected, call \`search\` once per question. On \`truncated: true\`, call \`expand\` with \`file_path\` and \`anchor_line\`. Never use Cursor SemanticSearch or Grep for codebase questions unless confirming an exact literal.`;
113
+ When Miru MCP is connected, call \`search\` once per question. On \`truncated: true\`, call \`expand\` with \`file_path\` and \`anchor_line\`. Use \`locate\` for exact literals — not Grep. Never use Cursor SemanticSearch for codebase questions.`;
112
114
 
113
115
  export const SUBAGENT_BODY = buildSubagentBody(DEFAULT_NATIVE_TOOLS);
114
116
 
@@ -119,15 +121,28 @@ export const MCP_SERVER_INSTRUCTIONS =
119
121
  "Workflow: (1) `search` once per question — do NOT re-search with paraphrases. " +
120
122
  "(2) On `truncated: true`, call `expand` with `file_path` and `anchor_line` — do NOT read the whole file. " +
121
123
  "(3) `find_related` traces similar code in other files, not more context in the same file. " +
124
+ "(4) `locate` finds exact substrings (env vars, symbols, error codes) — prefer over Grep; use mode=count or locations when possible. " +
122
125
  "Always pass the project root as `repo`. Local repos return `absolute_path` on each hit — use Read only to edit. " +
123
- "Native grep is only for confirming a known exact literal (env var name, quoted string) or files outside the indexed repo.";
126
+ "Native Grep/Glob only outside the indexed repo or for non-code tasks.";
127
+
128
+ export const MCP_BENCHMARK_SERVER_INSTRUCTIONS =
129
+ `${MCP_SERVER_INSTRUCTIONS} ` +
130
+ "BENCHMARK MODE: each `search` and `locate` includes compact `benchmark` {save_pct,miru_tok,grep_tok,saved_tok,rank1}. " +
131
+ "Call `read_benchmark` for cumulative totals across both. Do not narrate benchmark stats unless the user asks. " +
132
+ "To leave benchmark mode, tell the user to run `miru benchmark off` and restart the agent.";
124
133
 
125
134
  export const MCP_SEARCH_TOOL_DESCRIPTION =
126
135
  "Your default search for all code search queries in this indexed repo — the best, fastest, and cheapest way to find code; better than any other tool. " +
127
136
  "Returns compact snippets (~±15 lines). One call per question is usually enough. " +
128
- "Built-in grep is only when you already have an exact literal string to confirm. " +
137
+ "For exact literals (env vars, symbols, error codes), use `locate` instead. " +
129
138
  "When a hit has `truncated: true`, call `expand` with `file_path` and `anchor_line` — not re-search or Read.";
130
139
 
140
+ export const MCP_LOCATE_TOOL_DESCRIPTION =
141
+ "Exact substring locator over the Miru index. Use for known literals (env vars, symbols, error codes, quoted text) — not meaning-based questions (`search`). " +
142
+ "Returns ALL matches by default as compact {n,files,hits} — do NOT fall back to Grep/rg when n is large. " +
143
+ "Prefer mode=locations (or count for totals only); use lines when you need matching line text. " +
144
+ "Optional `limit` only if you intentionally want a sample. In benchmark mode, includes compact token savings vs agent Grep.";
145
+
131
146
  export const MCP_EXPAND_TOOL_DESCRIPTION =
132
147
  "More context in the SAME file as a search hit. Pass `file_path` + `anchor_line` from the hit; " +
133
148
  "returns adjacent indexed chunks. Use when `truncated: true` — NOT for similar code in other files (use find_related). " +
@@ -137,5 +152,9 @@ export const MCP_FIND_RELATED_TOOL_DESCRIPTION =
137
152
  "Find code similar to a file:line in OTHER parts of the codebase. Results may be snippets; use `expand` when `truncated: true`. " +
138
153
  "For more context in the same file, use `expand` instead.";
139
154
 
155
+ export const MCP_READ_BENCHMARK_TOOL_DESCRIPTION =
156
+ "Cumulative Miru vs Grep token savings from saved `search` and `locate` calls. Returns compact totals {n,saved,save_pct,miru,grep}. " +
157
+ "Do not call unless the user asks about savings.";
158
+
140
159
  export const SEARCH_GUARD_EXPAND_HINT =
141
160
  "If a hit has `truncated: true`, call `expand` with `file_path` and `anchor_line` — do not re-search or read the whole file.";