@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.
- package/README.md +178 -107
- package/package.json +9 -5
- package/src/benchmark/compare.ts +278 -0
- package/src/benchmark/grep.ts +352 -0
- package/src/benchmark/history.ts +206 -0
- package/src/benchmark/locate-compare.ts +65 -0
- package/src/benchmark/rg-literal.ts +143 -0
- package/src/benchmark/summary.ts +29 -0
- package/src/benchmark/types.ts +65 -0
- package/src/cli.ts +185 -4
- package/src/credentials.ts +8 -0
- package/src/help.ts +45 -2
- package/src/index/create.ts +2 -2
- package/src/index/incremental.ts +4 -3
- package/src/index.ts +3 -1
- package/src/installer/benchmark-mode.ts +248 -0
- package/src/installer/config.ts +14 -2
- package/src/installer/hooks/opencode-plugin.ts +6 -20
- package/src/installer/hooks/search-guard.ts +4 -5
- package/src/installer/installer.ts +59 -6
- package/src/installer/search-policy.ts +28 -9
- package/src/literal.ts +139 -0
- package/src/mcp/index-cache.ts +2 -2
- package/src/mcp/serve.ts +4 -3
- package/src/mcp/server.ts +166 -9
- package/src/miru-index.ts +15 -6
- package/src/snippet.ts +6 -3
- package/src/token-count.ts +62 -0
- package/src/tokenizer/bert-wordpiece.ts +219 -0
- package/src/tokenizer/load.ts +14 -0
- package/src/types.ts +8 -0
- package/src/utils.ts +8 -1
- package/tokenizer/tokenizer.json +1 -0
package/src/cli.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { type AgentId, writeAgentFile } from "./agents.ts";
|
|
3
|
+
import { clearBenchmarkHistory, resolveBenchmarkHistoryPath } from "./benchmark/history.ts";
|
|
3
4
|
import { clearCache } from "./cache.ts";
|
|
4
5
|
import {
|
|
5
6
|
fail,
|
|
@@ -7,8 +8,10 @@ import {
|
|
|
7
8
|
formatSearchErrorPretty,
|
|
8
9
|
formatSearchResultsPretty,
|
|
9
10
|
hint,
|
|
11
|
+
info,
|
|
10
12
|
prefersJsonOutput,
|
|
11
13
|
success,
|
|
14
|
+
writeStdout,
|
|
12
15
|
} from "./cli-ui.ts";
|
|
13
16
|
import { loadStoredCredentials } from "./credentials.ts";
|
|
14
17
|
import { normalizeTakaraApiKeyEnv } from "./env.ts";
|
|
@@ -20,9 +23,11 @@ import {
|
|
|
20
23
|
printFullHelp,
|
|
21
24
|
printMainHelp,
|
|
22
25
|
} from "./help.ts";
|
|
26
|
+
import { getBenchmarkModeStatus, setBenchmarkMode } from "./installer/benchmark-mode.ts";
|
|
23
27
|
import { runSearchGuardFromStdin } from "./installer/hooks/search-guard.ts";
|
|
24
28
|
import { runInstaller } from "./installer/installer.ts";
|
|
25
29
|
import { promptConfirm } from "./installer/prompt.ts";
|
|
30
|
+
import { formatLiteralLocate, type LiteralMode } from "./literal.ts";
|
|
26
31
|
import { serveMcp } from "./mcp/serve.ts";
|
|
27
32
|
import { MiruIndex } from "./miru-index.ts";
|
|
28
33
|
import {
|
|
@@ -34,6 +39,8 @@ import {
|
|
|
34
39
|
import { withSpinner } from "./spinner.ts";
|
|
35
40
|
import type { ContentType, SearchResult } from "./types.ts";
|
|
36
41
|
import {
|
|
42
|
+
DEFAULT_EXPAND_AFTER,
|
|
43
|
+
DEFAULT_EXPAND_BEFORE,
|
|
37
44
|
expandChunksAtLine,
|
|
38
45
|
formatExpandResults,
|
|
39
46
|
formatResults,
|
|
@@ -44,12 +51,15 @@ import {
|
|
|
44
51
|
} from "./utils.ts";
|
|
45
52
|
import { maybeNotifyUpdate, miruVersion } from "./version.ts";
|
|
46
53
|
|
|
54
|
+
process.title = "miru";
|
|
55
|
+
|
|
47
56
|
await loadEnvFiles();
|
|
48
57
|
normalizeTakaraApiKeyEnv();
|
|
49
58
|
await loadStoredCredentials();
|
|
50
59
|
|
|
51
60
|
const CLI_COMMANDS = new Set([
|
|
52
61
|
"search",
|
|
62
|
+
"locate",
|
|
53
63
|
"expand",
|
|
54
64
|
"find-related",
|
|
55
65
|
"init",
|
|
@@ -57,6 +67,7 @@ const CLI_COMMANDS = new Set([
|
|
|
57
67
|
"uninstall",
|
|
58
68
|
"setup",
|
|
59
69
|
"clear",
|
|
70
|
+
"benchmark",
|
|
60
71
|
"hook-guard",
|
|
61
72
|
"help",
|
|
62
73
|
"-h",
|
|
@@ -102,7 +113,7 @@ function parseContentArgv(argv: string[]): { content: ContentType[]; rest: strin
|
|
|
102
113
|
rest.push(arg);
|
|
103
114
|
}
|
|
104
115
|
}
|
|
105
|
-
return { content: resolveContent(content
|
|
116
|
+
return { content: resolveContent(content), rest };
|
|
106
117
|
}
|
|
107
118
|
|
|
108
119
|
function parseTopK(argv: string[]): { topK: number; rest: string[] } {
|
|
@@ -248,6 +259,53 @@ class RelatedChunkNotFoundError extends Error {
|
|
|
248
259
|
}
|
|
249
260
|
}
|
|
250
261
|
|
|
262
|
+
async function runLocate(
|
|
263
|
+
path: string,
|
|
264
|
+
literal: string,
|
|
265
|
+
content: ContentType[],
|
|
266
|
+
jsonFlag: boolean,
|
|
267
|
+
mode: LiteralMode,
|
|
268
|
+
limit: number | undefined,
|
|
269
|
+
ignoreCase: boolean,
|
|
270
|
+
): Promise<void> {
|
|
271
|
+
await ensureCredentials({ interactive: true });
|
|
272
|
+
|
|
273
|
+
const payload = await withSpinner("Locating literal", async () => {
|
|
274
|
+
const built = await MiruIndex.fromSource(path, content);
|
|
275
|
+
await built.saveToCache(path);
|
|
276
|
+
return formatLiteralLocate(
|
|
277
|
+
built.locateLiteral(literal, {
|
|
278
|
+
mode,
|
|
279
|
+
ignore_case: ignoreCase,
|
|
280
|
+
...(limit != null ? { limit } : {}),
|
|
281
|
+
}),
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (prefersJsonOutput(jsonFlag)) {
|
|
286
|
+
console.log(JSON.stringify(payload));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const n = Number(payload.n ?? 0);
|
|
291
|
+
const files = Number(payload.files ?? 0);
|
|
292
|
+
writeStdout(`literal=${literal} n=${n} files=${files} mode=${mode}`);
|
|
293
|
+
const hits = payload.hits as Array<{ f: string; l: number; t?: string }> | undefined;
|
|
294
|
+
if (hits) {
|
|
295
|
+
for (const hit of hits) {
|
|
296
|
+
if (hit.t !== undefined) {
|
|
297
|
+
writeStdout(` ${hit.f}:${hit.l}: ${hit.t}`);
|
|
298
|
+
} else {
|
|
299
|
+
writeStdout(` ${hit.f}:${hit.l}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (payload.truncated) {
|
|
303
|
+
writeStdout(` … truncated (showing ${hits.length} of ${n})`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
writeStdout("");
|
|
307
|
+
}
|
|
308
|
+
|
|
251
309
|
async function runInit(agent: AgentId, force: boolean): Promise<void> {
|
|
252
310
|
try {
|
|
253
311
|
const dest = await writeAgentFile(agent, { force });
|
|
@@ -265,6 +323,75 @@ async function runClear(path: string): Promise<void> {
|
|
|
265
323
|
success(`Cleared cached index for ${path}`);
|
|
266
324
|
}
|
|
267
325
|
|
|
326
|
+
async function runBenchmarkCommand(rest: string[]): Promise<void> {
|
|
327
|
+
const action = rest[0];
|
|
328
|
+
if (!action || action === "-h" || action === "--help") {
|
|
329
|
+
printCommandHelp("benchmark");
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (action === "status") {
|
|
334
|
+
const rows = await getBenchmarkModeStatus();
|
|
335
|
+
const installed = rows.filter((row) => row.action !== "missing");
|
|
336
|
+
if (installed.length === 0) {
|
|
337
|
+
info("No Miru MCP installs found. Run `miru install` first.");
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
for (const row of installed) {
|
|
341
|
+
writeStdout(` ${row.enabled ? "on " : "off"} ${row.agent.padEnd(16)} ${row.path}`);
|
|
342
|
+
}
|
|
343
|
+
const onCount = installed.filter((row) => row.enabled).length;
|
|
344
|
+
writeStdout("");
|
|
345
|
+
if (onCount > 0) {
|
|
346
|
+
hint(
|
|
347
|
+
`Benchmark mode on for ${onCount}/${installed.length}. Run \`miru benchmark off\` to leave.`,
|
|
348
|
+
);
|
|
349
|
+
} else {
|
|
350
|
+
hint("Benchmark mode is off for all installed agents.");
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (action === "on" || action === "off") {
|
|
356
|
+
const enabled = action === "on";
|
|
357
|
+
const rows = await setBenchmarkMode(enabled);
|
|
358
|
+
const touched = rows.filter((row) => row.action === "updated" || row.action === "unchanged");
|
|
359
|
+
if (touched.length === 0) {
|
|
360
|
+
info("No Miru MCP installs found. Run `miru install` first.");
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
for (const row of touched) {
|
|
364
|
+
const label = row.action === "updated" ? (enabled ? "enabled" : "disabled") : "unchanged";
|
|
365
|
+
writeStdout(` ${label.padEnd(9)} ${row.agent.padEnd(16)} ${row.path}`);
|
|
366
|
+
}
|
|
367
|
+
writeStdout("");
|
|
368
|
+
success(
|
|
369
|
+
enabled
|
|
370
|
+
? "Benchmark mode on. Restart agents to apply."
|
|
371
|
+
: "Benchmark mode off. Restart agents to apply.",
|
|
372
|
+
);
|
|
373
|
+
if (enabled) {
|
|
374
|
+
hint("Leave anytime with `miru benchmark off`.");
|
|
375
|
+
}
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (action === "clear") {
|
|
380
|
+
const path = resolveBenchmarkHistoryPath();
|
|
381
|
+
const result = await clearBenchmarkHistory(path);
|
|
382
|
+
if (result.cleared) {
|
|
383
|
+
success(`Cleared benchmark report ${result.path}`);
|
|
384
|
+
} else {
|
|
385
|
+
info(`No benchmark report at ${result.path}`);
|
|
386
|
+
}
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
fail(`Unknown benchmark action "${action}". Use on, off, status, or clear.`);
|
|
391
|
+
printCommandHelp("benchmark");
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
|
|
268
395
|
async function runCli(argv: string[]): Promise<void> {
|
|
269
396
|
const [command, ...rest] = argv;
|
|
270
397
|
|
|
@@ -302,6 +429,11 @@ async function runCli(argv: string[]): Promise<void> {
|
|
|
302
429
|
return;
|
|
303
430
|
}
|
|
304
431
|
|
|
432
|
+
if (command === "benchmark") {
|
|
433
|
+
await runBenchmarkCommand(rest);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
305
437
|
if (command === "init") {
|
|
306
438
|
let agent: AgentId | undefined;
|
|
307
439
|
let force = false;
|
|
@@ -390,6 +522,49 @@ async function runCli(argv: string[]): Promise<void> {
|
|
|
390
522
|
return;
|
|
391
523
|
}
|
|
392
524
|
|
|
525
|
+
if (command === "locate") {
|
|
526
|
+
const literal = sizedRest[0];
|
|
527
|
+
if (!literal) {
|
|
528
|
+
printCommandHelp("locate");
|
|
529
|
+
process.exit(1);
|
|
530
|
+
}
|
|
531
|
+
let mode: LiteralMode = "lines";
|
|
532
|
+
let limit: number | undefined;
|
|
533
|
+
let ignoreCase = false;
|
|
534
|
+
const pathArgs: string[] = [];
|
|
535
|
+
for (let i = 1; i < sizedRest.length; i++) {
|
|
536
|
+
const arg = sizedRest[i];
|
|
537
|
+
if (arg === "--mode" && sizedRest[i + 1]) {
|
|
538
|
+
const value = sizedRest[++i];
|
|
539
|
+
if (value !== "count" && value !== "locations" && value !== "lines") {
|
|
540
|
+
fail(`Unknown locate mode "${value}". Use count, locations, or lines.`);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
mode = value;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (arg === "--limit" && sizedRest[i + 1]) {
|
|
547
|
+
const raw = Number(sizedRest[++i]);
|
|
548
|
+
if (!Number.isFinite(raw) || raw < 1) {
|
|
549
|
+
fail("locate --limit must be a positive integer.");
|
|
550
|
+
process.exit(1);
|
|
551
|
+
}
|
|
552
|
+
limit = Math.floor(raw);
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
if (arg === "--ignore-case") {
|
|
556
|
+
ignoreCase = true;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if (arg !== undefined) {
|
|
560
|
+
pathArgs.push(arg);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
const path = resolveSearchPath(pathArgs[0] ?? process.cwd());
|
|
564
|
+
await runLocate(path, literal, content, jsonFlag, mode, limit, ignoreCase);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
|
|
393
568
|
if (command === "expand") {
|
|
394
569
|
const filePath = sizedRest[0];
|
|
395
570
|
const lineRaw = sizedRest[1];
|
|
@@ -399,8 +574,8 @@ async function runCli(argv: string[]): Promise<void> {
|
|
|
399
574
|
}
|
|
400
575
|
const line = Number(lineRaw);
|
|
401
576
|
const path = resolveSearchPath(sizedRest[2] ?? process.cwd());
|
|
402
|
-
let before =
|
|
403
|
-
let after =
|
|
577
|
+
let before = DEFAULT_EXPAND_BEFORE;
|
|
578
|
+
let after = DEFAULT_EXPAND_AFTER;
|
|
404
579
|
for (let i = 3; i < sizedRest.length; i++) {
|
|
405
580
|
const arg = sizedRest[i];
|
|
406
581
|
if (arg === "--before" && sizedRest[i + 1]) {
|
|
@@ -433,10 +608,15 @@ async function runCli(argv: string[]): Promise<void> {
|
|
|
433
608
|
|
|
434
609
|
async function runMcp(argv: string[]): Promise<void> {
|
|
435
610
|
let ref: string | null = null;
|
|
611
|
+
let benchmark = false;
|
|
436
612
|
const contentTokens: string[] = [];
|
|
437
613
|
|
|
438
614
|
for (let i = 0; i < argv.length; i++) {
|
|
439
615
|
const arg = argv[i];
|
|
616
|
+
if (arg === "--benchmark") {
|
|
617
|
+
benchmark = true;
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
440
620
|
if (arg === "--ref" && argv[i + 1]) {
|
|
441
621
|
ref = argv[++i] ?? null;
|
|
442
622
|
continue;
|
|
@@ -457,7 +637,8 @@ async function runMcp(argv: string[]): Promise<void> {
|
|
|
457
637
|
|
|
458
638
|
await serveMcp({
|
|
459
639
|
ref,
|
|
460
|
-
content: resolveContent(contentTokens
|
|
640
|
+
content: resolveContent(contentTokens),
|
|
641
|
+
benchmark,
|
|
461
642
|
});
|
|
462
643
|
}
|
|
463
644
|
|
package/src/credentials.ts
CHANGED
|
@@ -27,6 +27,14 @@ export function resolveCredentialsDir(): string {
|
|
|
27
27
|
return join(xdg, "miru");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Global Miru state directory (credentials, benchmark history, etc.).
|
|
32
|
+
* Same location as `resolveCredentialsDir()`; prefer this name for non-secret files.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveMiruStateDir(): string {
|
|
35
|
+
return resolveCredentialsDir();
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
export function resolveCredentialsPath(): string {
|
|
31
39
|
return join(resolveCredentialsDir(), CREDENTIALS_FILENAME);
|
|
32
40
|
}
|
package/src/help.ts
CHANGED
|
@@ -34,11 +34,13 @@ export function printMainHelp(): void {
|
|
|
34
34
|
|
|
35
35
|
section("Commands");
|
|
36
36
|
commandRow("search", "Hybrid search over a codebase");
|
|
37
|
+
commandRow("locate", "Exact substring location in the index");
|
|
37
38
|
commandRow("expand", "Adjacent chunks in the same file as a hit");
|
|
38
39
|
commandRow("find-related", "Find chunks related to a file:line");
|
|
39
40
|
commandRow("setup", "Save your Takara API key locally");
|
|
40
41
|
commandRow("install", "Configure miru across coding agents");
|
|
41
42
|
commandRow("uninstall", "Remove miru agent configuration");
|
|
43
|
+
commandRow("benchmark", "Turn MCP benchmark mode on or off");
|
|
42
44
|
commandRow("init", "Write a project-local sub-agent file");
|
|
43
45
|
commandRow("clear", "Remove cached index for a path");
|
|
44
46
|
commandRow("help", "Show help for a command");
|
|
@@ -62,6 +64,8 @@ export function printEnvHelp(): void {
|
|
|
62
64
|
writeStdout(` Default: ${DEFAULT_EMBEDDING_MODEL}`);
|
|
63
65
|
writeStdout(" MIRU_CONCURRENCY");
|
|
64
66
|
writeStdout(` Parallel workers (default: CPUs − ${DEFAULT_RESERVE_CORES})`);
|
|
67
|
+
writeStdout(" MIRU_TOKENIZER_JSON");
|
|
68
|
+
writeStdout(" Path to tokenizer.json (default: <package>/tokenizer/tokenizer.json)");
|
|
65
69
|
writeStdout("");
|
|
66
70
|
}
|
|
67
71
|
|
|
@@ -78,12 +82,26 @@ export function printCommandHelp(command: string): void {
|
|
|
78
82
|
writeStdout(" miru search <query> [path] [options]");
|
|
79
83
|
section("Options");
|
|
80
84
|
writeStdout(" -k, --top-k N Number of results (default: 5)");
|
|
81
|
-
writeStdout(" --content TYPE code | docs | config | all");
|
|
85
|
+
writeStdout(" --content TYPE code | docs | config | all (default: code config)");
|
|
82
86
|
writeStdout(" --json JSON output (default when piped)");
|
|
83
87
|
section("Example");
|
|
84
88
|
writeStdout(' miru search "where is auth" ./src -k 10 --content code docs');
|
|
85
89
|
writeStdout("");
|
|
86
90
|
return;
|
|
91
|
+
case "locate":
|
|
92
|
+
commandHeader("locate", "Exact substring location over the Miru index.");
|
|
93
|
+
section("Usage");
|
|
94
|
+
writeStdout(" miru locate <literal> [path] [options]");
|
|
95
|
+
section("Options");
|
|
96
|
+
writeStdout(" --mode MODE count | locations | lines (default: lines)");
|
|
97
|
+
writeStdout(" --limit N Optional hit cap (default: all matches)");
|
|
98
|
+
writeStdout(" --ignore-case Case-insensitive match");
|
|
99
|
+
writeStdout(" --content TYPE code | docs | config | all (default: code config)");
|
|
100
|
+
writeStdout(" --json JSON output (default when piped)");
|
|
101
|
+
section("Example");
|
|
102
|
+
writeStdout(" miru locate MIRU_BENCHMARK_HISTORY_PATH . --mode locations");
|
|
103
|
+
writeStdout("");
|
|
104
|
+
return;
|
|
87
105
|
case "expand":
|
|
88
106
|
commandHeader("expand", "More context in the same file as a search hit.");
|
|
89
107
|
section("Usage");
|
|
@@ -121,6 +139,28 @@ export function printCommandHelp(command: string): void {
|
|
|
121
139
|
case "uninstall":
|
|
122
140
|
commandHeader("uninstall", "Remove miru configuration from agents.");
|
|
123
141
|
writeStdout("Removes MCP entries, marked instruction blocks, and global sub-agents.");
|
|
142
|
+
writeStdout("Also deletes the global benchmark report from Miru's state directory.");
|
|
143
|
+
writeStdout("");
|
|
144
|
+
return;
|
|
145
|
+
case "benchmark":
|
|
146
|
+
commandHeader("benchmark", "Toggle MCP benchmark mode on installed agents.");
|
|
147
|
+
section("Usage");
|
|
148
|
+
writeStdout(" miru benchmark on");
|
|
149
|
+
writeStdout(" miru benchmark off");
|
|
150
|
+
writeStdout(" miru benchmark status");
|
|
151
|
+
writeStdout(" miru benchmark clear");
|
|
152
|
+
writeStdout("");
|
|
153
|
+
writeStdout("Adds or removes `--benchmark` from Miru MCP args in agent configs.");
|
|
154
|
+
writeStdout("If `--benchmark` is present, benchmark mode is on — no env overrides.");
|
|
155
|
+
writeStdout("Restart agents after changing mode. Prefer `off` when finished measuring.");
|
|
156
|
+
writeStdout("");
|
|
157
|
+
writeStdout("History is a global report (not per-repo) under Miru's state directory.");
|
|
158
|
+
writeStdout("Override path with MIRU_BENCHMARK_HISTORY_PATH. Append-only JSONL of savings.");
|
|
159
|
+
writeStdout("Repo paths in the report are plaintext — use `clear` on shared machines.");
|
|
160
|
+
writeStdout("");
|
|
161
|
+
writeStdout("Savings compare Miru workflow tokens to a Grep baseline");
|
|
162
|
+
writeStdout("(rg search + read of the top matched file), not the agent's full tool chain.");
|
|
163
|
+
writeStdout("Local paths only — git URL repos skip comparison with benchmark_skipped.");
|
|
124
164
|
writeStdout("");
|
|
125
165
|
return;
|
|
126
166
|
case "init":
|
|
@@ -145,9 +185,12 @@ export function printCommandHelp(command: string): void {
|
|
|
145
185
|
case "mcp":
|
|
146
186
|
commandHeader("mcp", "Stdio MCP server (default with no subcommand).");
|
|
147
187
|
section("Usage");
|
|
148
|
-
writeStdout(" miru [--ref BRANCH] [--content TYPE ...]");
|
|
188
|
+
writeStdout(" miru [--ref BRANCH] [--content TYPE ...] [--benchmark]");
|
|
189
|
+
writeStdout(" Default content: code config");
|
|
149
190
|
writeStdout("");
|
|
150
191
|
writeStdout("Indexes on the first search/expand/find_related tool call (repo argument).");
|
|
192
|
+
writeStdout("Use --benchmark (or `miru benchmark on`) for token-savings comparisons.");
|
|
193
|
+
writeStdout("Leave with `miru benchmark off` and restart the agent.");
|
|
151
194
|
writeStdout("");
|
|
152
195
|
return;
|
|
153
196
|
default:
|
package/src/index/create.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { EmbeddingBackend } from "../embeddings/openai.ts";
|
|
|
6
6
|
import { envOptionalInt } from "../env.ts";
|
|
7
7
|
import { searchImprovementsEnabled } from "../ranking/features.ts";
|
|
8
8
|
import { tokenize } from "../tokens.ts";
|
|
9
|
-
import type
|
|
9
|
+
import { type Chunk, type ContentType, defaultContentTypes } from "../types.ts";
|
|
10
10
|
import { BM25Index } from "./bm25.ts";
|
|
11
11
|
import { loadRootEntryChunks } from "./entry-chunks.ts";
|
|
12
12
|
import { walkFiles } from "./file-walker.ts";
|
|
@@ -33,7 +33,7 @@ function resolveMaxIndexFiles(): number | undefined {
|
|
|
33
33
|
export async function createIndexFromPath(
|
|
34
34
|
path: string,
|
|
35
35
|
embeddings: EmbeddingBackend,
|
|
36
|
-
content: ContentType[] =
|
|
36
|
+
content: ContentType[] = defaultContentTypes(),
|
|
37
37
|
displayRoot?: string,
|
|
38
38
|
): Promise<{ bm25: BM25Index; semantic: SemanticIndex; chunks: Chunk[] }> {
|
|
39
39
|
const profile = process.env.MIRU_PROFILE === "1";
|
package/src/index/incremental.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join, relative, resolve } from "node:path";
|
|
1
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
2
2
|
import { chunkSource } from "../chunking/chunking.ts";
|
|
3
3
|
import type { EmbeddingBackend } from "../embeddings/openai.ts";
|
|
4
4
|
import { tokenize } from "../tokens.ts";
|
|
@@ -123,8 +123,9 @@ function rebuildBm25(chunks: Chunk[]): BM25Index {
|
|
|
123
123
|
export function relativePathFromRoot(root: string, absoluteOrRelative: string): string {
|
|
124
124
|
const resolvedRoot = resolve(root);
|
|
125
125
|
const candidate = resolve(resolvedRoot, absoluteOrRelative);
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
const rel = normalizeRelativePath(relative(resolvedRoot, candidate));
|
|
127
|
+
if (rel === "" || (!rel.startsWith("../") && rel !== ".." && !isAbsolute(rel))) {
|
|
128
|
+
return rel;
|
|
128
129
|
}
|
|
129
130
|
return normalizeRelativePath(absoluteOrRelative);
|
|
130
131
|
}
|
package/src/index.ts
CHANGED
|
@@ -18,8 +18,10 @@ export { MiruIndex } from "./miru-index.ts";
|
|
|
18
18
|
export type {
|
|
19
19
|
/** A searchable text span from a source file (path, line range, content). */
|
|
20
20
|
Chunk,
|
|
21
|
-
/** What to index: source code,
|
|
21
|
+
/** What to index: source code, docs, and/or config. */
|
|
22
22
|
ContentType,
|
|
23
23
|
/** Ranked hit returned by `search` or `findRelated`. */
|
|
24
24
|
SearchResult,
|
|
25
25
|
} from "./types.ts";
|
|
26
|
+
|
|
27
|
+
export { DEFAULT_CONTENT_TYPES, defaultContentTypes } from "./types.ts";
|