@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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takara-ai/miru-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Miru (見る) — hybrid code search for agents. Bun, TypeScript, Takara embeddings.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,6 +50,9 @@
|
|
|
50
50
|
"lint": "biome check .",
|
|
51
51
|
"lint:fix": "biome check --write .",
|
|
52
52
|
"prepublishOnly": "bun run typecheck && bun test",
|
|
53
|
+
"benchmark:ab": "bun run scripts/search-ab-local.ts",
|
|
54
|
+
"benchmark:ab-multi": "bun run scripts/search-ab-multi.ts",
|
|
55
|
+
"benchmark:recall": "bun run scripts/benchmark-recall-tokens.ts",
|
|
53
56
|
"benchmark:quality": "bun run scripts/benchmark-quality.ts",
|
|
54
57
|
"benchmark:quantization": "bun run scripts/benchmark-quantization.ts"
|
|
55
58
|
},
|
package/src/agents/claude.md
CHANGED
|
@@ -3,30 +3,3 @@ name: miru-code
|
|
|
3
3
|
description: Code search via Miru MCP. Use search, expand, and find_related. DO NOT use Grep, Glob, or Read for exploration when Miru MCP is available.
|
|
4
4
|
tools: Bash, Read
|
|
5
5
|
---
|
|
6
|
-
|
|
7
|
-
When Miru MCP is available, use MCP `search`, `expand`, and `find_related` — not Grep, Glob, or Read-for-exploration.
|
|
8
|
-
|
|
9
|
-
| Task | Tool |
|
|
10
|
-
|------|------|
|
|
11
|
-
| How/where/what handles X? | Miru `search` (once) |
|
|
12
|
-
| Same file, more context | Miru `expand` |
|
|
13
|
-
| Similar implementations | Miru `find_related` |
|
|
14
|
-
| Exact literal confirmation | Grep |
|
|
15
|
-
| Edit a located file | Read |
|
|
16
|
-
|
|
17
|
-
### MCP workflow
|
|
18
|
-
|
|
19
|
-
1. Call `search` with `repo` set to the project root; follow `guidance` in the response.
|
|
20
|
-
2. If context is cut off, call `expand` with the hit's `file_path` and `line`.
|
|
21
|
-
3. Use `find_related` for similar code in other files — not for same-file context.
|
|
22
|
-
4. Read files only to edit or when `expand` still lacks context.
|
|
23
|
-
|
|
24
|
-
### CLI fallback
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
miru search "authentication flow" .
|
|
28
|
-
miru expand src/auth.ts 42 .
|
|
29
|
-
miru find-related src/auth.ts 42 .
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
If `miru` is not on `$PATH`, use `bunx @takara-ai/miru-code`.
|
package/src/agents/copilot.md
CHANGED
|
@@ -2,15 +2,3 @@
|
|
|
2
2
|
name: miru-code
|
|
3
3
|
description: Code search via Miru MCP. Use search, expand, and find_related instead of grep_search or codebase_search when Miru is connected.
|
|
4
4
|
---
|
|
5
|
-
|
|
6
|
-
When Miru MCP is available, use `search`, `expand`, and `find_related` instead of `grep_search`, `codebase_search`, or broad file reads.
|
|
7
|
-
|
|
8
|
-
| Task | Tool |
|
|
9
|
-
|------|------|
|
|
10
|
-
| How/where/what handles X? | Miru `search` (once; follow `guidance`) |
|
|
11
|
-
| Same file, more context | Miru `expand` |
|
|
12
|
-
| Similar implementations | Miru `find_related` |
|
|
13
|
-
| Exact literal confirmation | grep_search |
|
|
14
|
-
| Edit a located file | read_file |
|
|
15
|
-
|
|
16
|
-
Pass project root as `repo` on every MCP call.
|
package/src/agents/cursor.md
CHANGED
|
@@ -2,29 +2,3 @@
|
|
|
2
2
|
name: miru-code
|
|
3
3
|
description: Code search via Miru MCP. Use search, expand, and find_related. DO NOT use Grep, Glob, or SemanticSearch when Miru MCP is available.
|
|
4
4
|
---
|
|
5
|
-
|
|
6
|
-
Use Miru MCP tools when connected:
|
|
7
|
-
|
|
8
|
-
- `search` — one call per question; always pass project root as `repo`; read `guidance` in the response
|
|
9
|
-
- `expand` — adjacent chunks in the same file when a hit looks truncated (`file_path` + `line`)
|
|
10
|
-
- `find_related` — similar code in other files after a search hit
|
|
11
|
-
|
|
12
|
-
Do not re-search with paraphrases when `guidance.sufficient` is true. Do not read whole files for exploration.
|
|
13
|
-
|
|
14
|
-
| Task | Tool |
|
|
15
|
-
|------|------|
|
|
16
|
-
| How/where/what handles X? | Miru `search` (once) |
|
|
17
|
-
| Same file, more context | Miru `expand` |
|
|
18
|
-
| Similar implementations | Miru `find_related` |
|
|
19
|
-
| Exact literal confirmation | Grep |
|
|
20
|
-
| Edit a located file | Read |
|
|
21
|
-
|
|
22
|
-
CLI fallback when MCP is unavailable:
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
miru search "authentication flow" .
|
|
26
|
-
miru expand src/auth.ts 42 .
|
|
27
|
-
miru find-related src/auth.ts 42 .
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
If `miru` is not on `$PATH`, use `bunx @takara-ai/miru-code`.
|
package/src/agents/gemini.md
CHANGED
|
@@ -2,17 +2,3 @@
|
|
|
2
2
|
name: miru-code
|
|
3
3
|
description: Code search via Miru MCP. Use search, expand, and find_related instead of grep_search or read_file for exploration.
|
|
4
4
|
---
|
|
5
|
-
|
|
6
|
-
When Miru MCP is available, use `search`, `expand`, and `find_related` instead of `grep_search`, `glob`, or `read_file` for codebase exploration.
|
|
7
|
-
|
|
8
|
-
| Task | Tool |
|
|
9
|
-
|------|------|
|
|
10
|
-
| How/where/what handles X? | Miru `search` (once; follow `guidance`) |
|
|
11
|
-
| Same file, more context | Miru `expand` |
|
|
12
|
-
| Similar implementations | Miru `find_related` |
|
|
13
|
-
| Exact literal confirmation | grep_search |
|
|
14
|
-
| Edit a located file | read_file |
|
|
15
|
-
|
|
16
|
-
Pass project root as `repo` on every MCP call. Do not re-search when `guidance.sufficient` is true.
|
|
17
|
-
|
|
18
|
-
CLI fallback: `miru search "query" .` or `bunx @takara-ai/miru-code search "query" .`
|
package/src/agents/kiro.md
CHANGED
|
@@ -2,9 +2,3 @@
|
|
|
2
2
|
name: miru-code
|
|
3
3
|
description: Code search via Miru MCP. Use search, expand, and find_related for codebase exploration instead of built-in grep or file search.
|
|
4
4
|
---
|
|
5
|
-
|
|
6
|
-
When Miru MCP is available, use `search`, `expand`, and `find_related` before any built-in search or broad file reads.
|
|
7
|
-
|
|
8
|
-
Pass project root as `repo`. Follow `guidance` in search responses. Use `expand` for truncated hits in the same file. Use Grep only for exact literal confirmation.
|
|
9
|
-
|
|
10
|
-
CLI fallback: `miru search "query" .`
|
package/src/agents/opencode.md
CHANGED
|
@@ -3,9 +3,3 @@ name: miru-code
|
|
|
3
3
|
description: Code search via Miru MCP. Use search, expand, and find_related instead of grep or glob for exploration.
|
|
4
4
|
mode: subagent
|
|
5
5
|
---
|
|
6
|
-
|
|
7
|
-
When Miru MCP is available, use `search`, `expand`, and `find_related` instead of grep, glob, or read-for-exploration.
|
|
8
|
-
|
|
9
|
-
Pass project root as `repo` on every MCP call. Follow `guidance` in search responses.
|
|
10
|
-
|
|
11
|
-
CLI fallback: `miru search "query" .`
|
package/src/agents.ts
CHANGED
|
@@ -1,17 +1,56 @@
|
|
|
1
1
|
import { mkdir, readFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
buildSubagentBody,
|
|
5
|
+
type NativeToolNames,
|
|
6
|
+
} from "./installer/search-policy.ts";
|
|
3
7
|
|
|
4
8
|
export type AgentId = "claude" | "copilot" | "cursor" | "gemini" | "kiro" | "opencode";
|
|
5
9
|
|
|
6
10
|
const AGENTS_DIR = join(import.meta.dir, "agents");
|
|
7
11
|
|
|
12
|
+
const AGENT_NATIVE_TOOLS: Record<AgentId, NativeToolNames> = {
|
|
13
|
+
claude: {
|
|
14
|
+
explorationDenied: "Grep, Glob, SemanticSearch, or Read",
|
|
15
|
+
grep: "Grep",
|
|
16
|
+
read: "Read",
|
|
17
|
+
},
|
|
18
|
+
cursor: {
|
|
19
|
+
explorationDenied: "Grep, Glob, SemanticSearch, or Read",
|
|
20
|
+
grep: "Grep",
|
|
21
|
+
read: "Read",
|
|
22
|
+
},
|
|
23
|
+
copilot: {
|
|
24
|
+
explorationDenied: "grep_search, codebase_search, glob, or read_file",
|
|
25
|
+
grep: "grep_search",
|
|
26
|
+
read: "read_file",
|
|
27
|
+
},
|
|
28
|
+
gemini: {
|
|
29
|
+
explorationDenied: "grep_search, glob, or read_file",
|
|
30
|
+
grep: "grep_search",
|
|
31
|
+
read: "read_file",
|
|
32
|
+
},
|
|
33
|
+
kiro: {
|
|
34
|
+
explorationDenied: "built-in grep, file search, or broad file reads",
|
|
35
|
+
grep: "Grep",
|
|
36
|
+
read: "Read",
|
|
37
|
+
},
|
|
38
|
+
opencode: {
|
|
39
|
+
explorationDenied: "grep, glob, or read-for-exploration",
|
|
40
|
+
grep: "grep",
|
|
41
|
+
read: "read",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
8
45
|
export function agentDestination(agent: AgentId): string {
|
|
9
46
|
const baseDir = agent === "copilot" ? ".github" : `.${agent}`;
|
|
10
47
|
return join(baseDir, "agents", "miru-code.md");
|
|
11
48
|
}
|
|
12
49
|
|
|
13
50
|
export async function loadAgentTemplate(agent: AgentId): Promise<string> {
|
|
14
|
-
|
|
51
|
+
const frontmatter = (await readFile(join(AGENTS_DIR, `${agent}.md`), "utf-8")).trim();
|
|
52
|
+
const body = buildSubagentBody(AGENT_NATIVE_TOOLS[agent]);
|
|
53
|
+
return `${frontmatter}\n\n${body}\n`;
|
|
15
54
|
}
|
|
16
55
|
|
|
17
56
|
export async function writeAgentFile(
|
package/src/index/create.ts
CHANGED
|
@@ -9,8 +9,10 @@ import { BM25Index } from "./bm25.ts";
|
|
|
9
9
|
import { walkFiles } from "./file-walker.ts";
|
|
10
10
|
import { detectLanguage, getExtensions, getFileStatus, readFileText } from "./files.ts";
|
|
11
11
|
import type { SemanticIndex } from "./semantic-index.ts";
|
|
12
|
+
import { loadRootEntryChunks } from "./entry-chunks.ts";
|
|
12
13
|
import { enrichForBm25 } from "./sparse.ts";
|
|
13
14
|
import { buildSemanticIndex } from "./vector-storage.ts";
|
|
15
|
+
import { searchImprovementsEnabled } from "../ranking/features.ts";
|
|
14
16
|
|
|
15
17
|
const DEFAULT_PIPELINE_EMBED_BATCH = 64;
|
|
16
18
|
const DEFAULT_PIPELINE_EMBED_INFLIGHT = 8;
|
|
@@ -145,6 +147,17 @@ export async function createIndexFromPath(
|
|
|
145
147
|
await Promise.race(runningFiles);
|
|
146
148
|
}
|
|
147
149
|
|
|
150
|
+
if (searchImprovementsEnabled()) {
|
|
151
|
+
const entryChunks = await loadRootEntryChunks(
|
|
152
|
+
resolved,
|
|
153
|
+
displayRoot ? root : undefined,
|
|
154
|
+
);
|
|
155
|
+
if (entryChunks.length > 0) {
|
|
156
|
+
readyBySeq.set(fileSeq++, entryChunks);
|
|
157
|
+
flushReadySequentialChunks();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
148
161
|
flushReadySequentialChunks();
|
|
149
162
|
await maybeScheduleEmbed(true);
|
|
150
163
|
await Promise.all(embedPromises);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { join, relative } from "node:path";
|
|
2
|
+
import type { Chunk } from "../types.ts";
|
|
3
|
+
|
|
4
|
+
function indexedPath(absPath: string, repoRoot: string, displayRoot?: string): string {
|
|
5
|
+
if (displayRoot) {
|
|
6
|
+
return relative(displayRoot, absPath).replace(/\\/g, "/");
|
|
7
|
+
}
|
|
8
|
+
return absPath.replace(/\\/g, "/");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function packageJsonEntryChunks(
|
|
12
|
+
absPath: string,
|
|
13
|
+
repoRoot: string,
|
|
14
|
+
displayRoot?: string,
|
|
15
|
+
): Promise<Chunk[]> {
|
|
16
|
+
let pkg: Record<string, unknown>;
|
|
17
|
+
try {
|
|
18
|
+
pkg = JSON.parse(await Bun.file(absPath).text()) as Record<string, unknown>;
|
|
19
|
+
} catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const entryLines = ["[package entry]"];
|
|
24
|
+
|
|
25
|
+
const bin = pkg.bin;
|
|
26
|
+
if (typeof bin === "string") {
|
|
27
|
+
entryLines.push(`bin: ${bin}`);
|
|
28
|
+
} else if (bin && typeof bin === "object") {
|
|
29
|
+
for (const [name, target] of Object.entries(bin)) {
|
|
30
|
+
entryLines.push(`bin ${name}: ${String(target)}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof pkg.main === "string") {
|
|
35
|
+
entryLines.push(`main: ${pkg.main}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const types = pkg.types ?? pkg.typings;
|
|
39
|
+
if (typeof types === "string") {
|
|
40
|
+
entryLines.push(`types: ${types}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (entryLines.length <= 1) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const filePath = indexedPath(absPath, repoRoot, displayRoot);
|
|
48
|
+
const content = `${entryLines.join("\n")}\n`;
|
|
49
|
+
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
content,
|
|
53
|
+
file_path: filePath,
|
|
54
|
+
start_line: 1,
|
|
55
|
+
end_line: entryLines.length,
|
|
56
|
+
language: "json",
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Synthetic chunks for root config entry points (bin/main) when indexing code repos. */
|
|
62
|
+
export async function loadRootEntryChunks(
|
|
63
|
+
repoRoot: string,
|
|
64
|
+
displayRoot?: string,
|
|
65
|
+
): Promise<Chunk[]> {
|
|
66
|
+
const pkgPath = join(repoRoot, "package.json");
|
|
67
|
+
if (!(await Bun.file(pkgPath).exists())) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
return packageJsonEntryChunks(pkgPath, repoRoot, displayRoot);
|
|
71
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { SEARCH_GUARD_EXPAND_HINT } from "../search-policy.ts";
|
|
2
|
+
|
|
1
3
|
const REDIRECT_PREFIX =
|
|
2
4
|
"Use Miru MCP instead of built-in search tools (pass project root as `repo`). " +
|
|
3
5
|
"Grep/Glob/Shell are only for exact literal confirmation or non-code tasks.";
|
|
@@ -119,10 +121,7 @@ export function searchGuardBlockReason(
|
|
|
119
121
|
toolInput: Record<string, unknown>,
|
|
120
122
|
): string {
|
|
121
123
|
const query = suggestedMiruQuery(toolName, toolInput);
|
|
122
|
-
return
|
|
123
|
-
`${REDIRECT_PREFIX} Try Miru \`search\` with query "${query}". ` +
|
|
124
|
-
"If the hit looks truncated, call `expand` on its file_path + line — do not re-search or read the whole file."
|
|
125
|
-
);
|
|
124
|
+
return `${REDIRECT_PREFIX} Try Miru \`search\` with query "${query}". ${SEARCH_GUARD_EXPAND_HINT}`;
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
/** Map host-specific stdin JSON into a common evaluation shape. */
|
package/src/installer/prompt.ts
CHANGED
|
@@ -15,11 +15,31 @@ const ESC = "\x1b";
|
|
|
15
15
|
const HIDE_CURSOR = `${ESC}[?25l`;
|
|
16
16
|
const SHOW_CURSOR = `${ESC}[?25h`;
|
|
17
17
|
const CLEAR_LINE = `${ESC}[2K`;
|
|
18
|
+
const CURSOR_START = `${ESC}[G`;
|
|
18
19
|
|
|
19
20
|
function writeRaw(text: string): void {
|
|
20
21
|
output.write(text);
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
function createFrameWriter(): (lines: string[]) => void {
|
|
25
|
+
let previousLineCount = 0;
|
|
26
|
+
|
|
27
|
+
return (lines: string[]) => {
|
|
28
|
+
if (previousLineCount > 0) {
|
|
29
|
+
writeRaw(`${ESC}[${previousLineCount}A${CURSOR_START}`);
|
|
30
|
+
}
|
|
31
|
+
writeRaw(`${CLEAR_LINE}${lines.join(`\n${CLEAR_LINE}`)}\n`);
|
|
32
|
+
const extra = previousLineCount - lines.length;
|
|
33
|
+
if (extra > 0) {
|
|
34
|
+
for (let i = 0; i < extra; i++) {
|
|
35
|
+
writeRaw(`${CLEAR_LINE}\n`);
|
|
36
|
+
}
|
|
37
|
+
writeRaw(`${ESC}[${extra}A${CURSOR_START}`);
|
|
38
|
+
}
|
|
39
|
+
previousLineCount = lines.length;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
23
43
|
function readKey(): Promise<Buffer> {
|
|
24
44
|
return new Promise((resolve, reject) => {
|
|
25
45
|
const onData = (chunk: Buffer) => {
|
|
@@ -90,12 +110,12 @@ async function withRawMode<T>(fn: () => Promise<T>): Promise<T> {
|
|
|
90
110
|
}
|
|
91
111
|
}
|
|
92
112
|
|
|
93
|
-
function
|
|
113
|
+
function buildMultiSelectLines<T>(
|
|
94
114
|
title: string,
|
|
95
115
|
items: Array<{ label: string; value: T; checked: boolean }>,
|
|
96
116
|
cursor: number,
|
|
97
117
|
footer: string,
|
|
98
|
-
):
|
|
118
|
+
): string[] {
|
|
99
119
|
const lines: string[] = [];
|
|
100
120
|
lines.push("");
|
|
101
121
|
lines.push(bold(title));
|
|
@@ -113,7 +133,7 @@ function renderMultiSelect<T>(
|
|
|
113
133
|
}
|
|
114
134
|
lines.push("");
|
|
115
135
|
lines.push(dim(footer));
|
|
116
|
-
|
|
136
|
+
return lines;
|
|
117
137
|
}
|
|
118
138
|
|
|
119
139
|
export async function promptMultiSelect<T>(
|
|
@@ -130,10 +150,11 @@ export async function promptMultiSelect<T>(
|
|
|
130
150
|
|
|
131
151
|
const footer = "↑↓ move space toggle a all enter confirm ctrl-c cancel";
|
|
132
152
|
let cursor = 0;
|
|
153
|
+
const drawFrame = createFrameWriter();
|
|
133
154
|
|
|
134
155
|
return withRawMode(async () => {
|
|
135
156
|
while (true) {
|
|
136
|
-
|
|
157
|
+
drawFrame(buildMultiSelectLines(title, items, cursor, footer));
|
|
137
158
|
const key = parseKey(await readKey());
|
|
138
159
|
|
|
139
160
|
if (key === "ctrl-c") {
|
|
@@ -163,11 +184,13 @@ export async function promptMultiSelect<T>(
|
|
|
163
184
|
});
|
|
164
185
|
}
|
|
165
186
|
|
|
166
|
-
function
|
|
187
|
+
function buildConfirmLines(question: string, yesSelected: boolean): string[] {
|
|
167
188
|
const yes = yesSelected ? green(bold(" Yes ")) : dim(" Yes ");
|
|
168
189
|
const no = yesSelected ? dim(" No ") : green(bold(" No "));
|
|
169
|
-
|
|
170
|
-
|
|
190
|
+
return [
|
|
191
|
+
`${question} ${yes} / ${no}`,
|
|
192
|
+
dim("←→ or y/n to choose enter to confirm ctrl-c to cancel"),
|
|
193
|
+
];
|
|
171
194
|
}
|
|
172
195
|
|
|
173
196
|
export async function promptConfirm(question: string, defaultYes = true): Promise<boolean> {
|
|
@@ -186,10 +209,11 @@ export async function promptConfirm(question: string, defaultYes = true): Promis
|
|
|
186
209
|
}
|
|
187
210
|
|
|
188
211
|
let yesSelected = defaultYes;
|
|
212
|
+
const drawFrame = createFrameWriter();
|
|
189
213
|
|
|
190
214
|
return withRawMode(async () => {
|
|
191
215
|
while (true) {
|
|
192
|
-
|
|
216
|
+
drawFrame(buildConfirmLines(question, yesSelected));
|
|
193
217
|
const key = parseKey(await readKey());
|
|
194
218
|
|
|
195
219
|
if (key === "ctrl-c") {
|
|
@@ -1,32 +1,86 @@
|
|
|
1
1
|
/** Shared code-search policy text for instructions, Cursor rules, and sub-agents. */
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const SNIPPET_GUIDANCE =
|
|
4
|
+
"Search returns compact snippets (~±15 lines around the best match). " +
|
|
5
|
+
"When a hit has `truncated: true`, call `expand` with `file_path` and `anchor_line` " +
|
|
6
|
+
"(or `start_line`) — do not re-search or Read the whole file.";
|
|
7
|
+
|
|
8
|
+
export interface NativeToolNames {
|
|
9
|
+
explorationDenied: string;
|
|
10
|
+
grep: string;
|
|
11
|
+
read: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function buildSearchPolicyTable(native: NativeToolNames): string {
|
|
15
|
+
return `| Task | Use | Not |
|
|
4
16
|
|------|-----|-----|
|
|
5
|
-
| How/where/what handles X? | Miru MCP \`search\` (once) |
|
|
6
|
-
| Same file, more context | Miru MCP \`expand\` | Re-search,
|
|
7
|
-
| Similar code elsewhere | Miru MCP \`find_related\` |
|
|
8
|
-
| Search docs or config | Miru \`search\`
|
|
9
|
-
| Exact literal exists? |
|
|
10
|
-
| Edit a known file:line |
|
|
17
|
+
| How/where/what handles X? | Miru MCP \`search\` (once) | ${native.explorationDenied}, repeat searches |
|
|
18
|
+
| Same file, more context | Miru MCP \`expand\` on \`truncated: true\` | Re-search, ${native.read} whole file |
|
|
19
|
+
| Similar code elsewhere | Miru MCP \`find_related\` | ${native.grep} chains |
|
|
20
|
+
| Search docs or config | Miru \`search\` | ${native.grep} README paths |
|
|
21
|
+
| Exact literal exists? | ${native.grep} | Miru |
|
|
22
|
+
| Edit a known file:line | ${native.read} (after Miru found it) | ${native.read}-before-search |`;
|
|
23
|
+
}
|
|
11
24
|
|
|
12
|
-
export
|
|
25
|
+
export function buildSearchPolicyBody(native: NativeToolNames): string {
|
|
26
|
+
return `DO NOT use ${native.explorationDenied} to explore how code works when Miru MCP is available.
|
|
27
|
+
|
|
28
|
+
${SNIPPET_GUIDANCE}
|
|
13
29
|
|
|
14
30
|
Use Miru MCP tools:
|
|
15
|
-
- \`search\` — one call per question; pass project root as \`repo
|
|
16
|
-
- \`expand\` —
|
|
17
|
-
- \`find_related\` — similar code in other files
|
|
31
|
+
- \`search\` — one call per question; pass project root as \`repo\`
|
|
32
|
+
- \`expand\` — more context in the same file when \`truncated: true\` (\`file_path\` + \`anchor_line\` or \`start_line\`)
|
|
33
|
+
- \`find_related\` — similar code in other files (hits may also be snippets; use \`expand\` if truncated)
|
|
18
34
|
|
|
19
35
|
Stop rules:
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
36
|
+
- Answer from the first \`search\` — do not re-search with paraphrases
|
|
37
|
+
- On \`truncated: true\`, call \`expand\` — not another \`search\` or a full-file ${native.read}
|
|
38
|
+
- ${native.read} is for editing a path Miru already gave you, not for exploration
|
|
23
39
|
|
|
24
40
|
Native tools are allowed ONLY when:
|
|
25
41
|
- confirming an exact literal string (env var name, error code, quoted text)
|
|
26
42
|
- reading a file you already located via Miru, to edit it
|
|
27
43
|
- searching outside the indexed repo
|
|
28
44
|
|
|
29
|
-
${
|
|
45
|
+
${buildSearchPolicyTable(native)}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function buildMcpWorkflow(native: NativeToolNames): string {
|
|
49
|
+
return `### MCP workflow
|
|
50
|
+
|
|
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\` (or \`start_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.`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const CLI_FALLBACK = `### CLI fallback (no MCP in this session)
|
|
58
|
+
|
|
59
|
+
\`\`\`bash
|
|
60
|
+
miru search "authentication flow" .
|
|
61
|
+
miru expand src/auth.ts 42 .
|
|
62
|
+
miru find-related src/auth.ts 42 .
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
If \`miru\` is not on \`$PATH\`, use \`bunx @takara-ai/miru-code\`.`;
|
|
66
|
+
|
|
67
|
+
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.
|
|
69
|
+
|
|
70
|
+
${buildSearchPolicyBody(native)}
|
|
71
|
+
|
|
72
|
+
${buildMcpWorkflow(native)}
|
|
73
|
+
|
|
74
|
+
${CLI_FALLBACK}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const DEFAULT_NATIVE_TOOLS: NativeToolNames = {
|
|
78
|
+
explorationDenied: "Grep, Glob, SemanticSearch, or Read",
|
|
79
|
+
grep: "Grep",
|
|
80
|
+
read: "Read",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const SEARCH_POLICY_BODY = buildSearchPolicyBody(DEFAULT_NATIVE_TOOLS);
|
|
30
84
|
|
|
31
85
|
export const INSTRUCTIONS_MARKDOWN = `## Miru Code Search
|
|
32
86
|
|
|
@@ -54,25 +108,31 @@ alwaysApply: true
|
|
|
54
108
|
|
|
55
109
|
${SEARCH_POLICY_BODY}
|
|
56
110
|
|
|
57
|
-
When Miru MCP is connected, call \`search\` once per question
|
|
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.`;
|
|
58
112
|
|
|
59
|
-
export const SUBAGENT_BODY =
|
|
113
|
+
export const SUBAGENT_BODY = buildSubagentBody(DEFAULT_NATIVE_TOOLS);
|
|
60
114
|
|
|
61
|
-
|
|
115
|
+
export const MCP_SERVER_INSTRUCTIONS =
|
|
116
|
+
"DO NOT use Grep, Glob, SemanticSearch, or Read to explore code when this server is available. " +
|
|
117
|
+
`${SNIPPET_GUIDANCE} ` +
|
|
118
|
+
"Workflow: (1) `search` once per question — do NOT re-search with paraphrases. " +
|
|
119
|
+
"(2) On `truncated: true`, call `expand` with `file_path` and `anchor_line` (or `start_line`) — do NOT read the whole file. " +
|
|
120
|
+
"(3) `find_related` traces similar code in other files, not more context in the same file. " +
|
|
121
|
+
"Always pass the project root as `repo`. Local repos return `absolute_path` on each hit — use Read only to edit. " +
|
|
122
|
+
"Native search is only for exact literal confirmation or files outside the indexed repo.";
|
|
62
123
|
|
|
63
|
-
|
|
124
|
+
export const MCP_SEARCH_TOOL_DESCRIPTION =
|
|
125
|
+
"DEFAULT code exploration tool. Returns compact snippets (~±15 lines). One call per question is usually enough. " +
|
|
126
|
+
"When a hit has `truncated: true`, call `expand` with `file_path` and `anchor_line` (or `start_line`) — not re-search or Read.";
|
|
64
127
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
128
|
+
export const MCP_EXPAND_TOOL_DESCRIPTION =
|
|
129
|
+
"More context in the SAME file as a search hit. Pass `file_path` + `anchor_line` or `start_line` from a truncated hit; " +
|
|
130
|
+
"returns adjacent indexed chunks. Use when `truncated: true` — NOT for similar code in other files (use find_related). " +
|
|
131
|
+
"Prefer this over re-searching or reading the whole file.";
|
|
69
132
|
|
|
70
|
-
|
|
133
|
+
export const MCP_FIND_RELATED_TOOL_DESCRIPTION =
|
|
134
|
+
"Find code similar to a file:line in OTHER parts of the codebase. Results may be snippets; use `expand` when `truncated: true`. " +
|
|
135
|
+
"For more context in the same file, use `expand` instead.";
|
|
71
136
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
miru expand src/auth.ts 42 .
|
|
75
|
-
miru find-related src/auth.ts 42 .
|
|
76
|
-
\`\`\`
|
|
77
|
-
|
|
78
|
-
If \`miru\` is not on \`$PATH\`, use \`bunx @takara-ai/miru-code\`.`;
|
|
137
|
+
export const SEARCH_GUARD_EXPAND_HINT =
|
|
138
|
+
"If a hit has `truncated: true`, call `expand` with `file_path` and `anchor_line` — do not re-search or read the whole file.";
|