@takara-ai/miru-code 1.9.0 → 1.10.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 +43 -48
- package/docs/coverage-badge.json +6 -0
- package/grammars/manifest.json +10 -2
- package/grammars/tree-sitter-astro.wasm +0 -0
- package/grammars/tree-sitter-php.wasm +0 -0
- package/grammars/tree-sitter-php_only.wasm +0 -0
- package/grammars/tree-sitter-sql.wasm +0 -0
- package/grammars/tree-sitter-svelte.wasm +0 -0
- package/grammars/tree-sitter-vue.wasm +0 -0
- package/package.json +10 -2
- package/src/agents.ts +4 -4
- package/src/auth/device.ts +26 -21
- package/src/auth/providers.ts +26 -11
- package/src/benchmark/compare.ts +3 -10
- package/src/benchmark/grep.ts +64 -37
- package/src/benchmark/locate-compare.ts +8 -1
- package/src/benchmark/rg-literal.ts +9 -4
- package/src/cache.ts +3 -3
- package/src/chunking/ast.ts +21 -3
- package/src/chunking/grammars.ts +8 -3
- package/src/chunking/structural.ts +13 -24
- package/src/cli-mode.ts +18 -0
- package/src/cli.ts +10 -45
- package/src/credentials.ts +8 -13
- package/src/embeddings/openai.ts +6 -3
- package/src/embeddings/sagemaker.ts +18 -3
- package/src/embeddings/validate.ts +10 -7
- package/src/git.ts +6 -2
- package/src/help.ts +5 -4
- package/src/index/bm25.ts +66 -6
- package/src/index/create.ts +5 -9
- package/src/index/files.ts +5 -1
- package/src/index/incremental.ts +48 -8
- package/src/index/sparse.ts +14 -2
- package/src/installer/agents.ts +85 -65
- package/src/installer/benchmark-mode.ts +40 -11
- package/src/installer/config.ts +105 -42
- package/src/installer/installer.ts +59 -34
- package/src/installer/legacy-hooks.ts +131 -0
- package/src/installer/prompt.ts +20 -22
- package/src/installer/search-policy.ts +8 -8
- package/src/mcp/auth-tool.ts +93 -33
- package/src/mcp/index-cache.ts +16 -3
- package/src/mcp/server.ts +42 -22
- package/src/mcp/stdio.ts +17 -2
- package/src/miru-index.ts +54 -18
- package/src/prompt.ts +51 -14
- package/src/ranking/boosting.ts +5 -6
- package/src/ranking/location.ts +6 -10
- package/src/ranking/penalties.ts +14 -22
- package/src/search.ts +4 -12
- package/src/setup.ts +25 -13
- package/src/snippet.ts +8 -26
- package/src/spinner.ts +26 -16
- package/src/tokenizer/bert-wordpiece.ts +0 -6
- package/src/utils.ts +0 -4
- package/src/version.ts +4 -4
- package/src/installer/hooks/install.ts +0 -549
- package/src/installer/hooks/opencode-plugin.ts +0 -54
- package/src/installer/hooks/search-guard.ts +0 -272
package/src/setup.ts
CHANGED
|
@@ -123,25 +123,27 @@ export function parseSetupCliArgs(rest: string[]): {
|
|
|
123
123
|
return { args };
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
async function promptSageMakerArn(): Promise<string> {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
async function promptSageMakerArn(promptTextImpl: typeof promptText = promptText): Promise<string> {
|
|
127
|
+
let arn = "";
|
|
128
|
+
while (!arn) {
|
|
129
|
+
const candidate = await promptTextImpl("SageMaker endpoint ARN");
|
|
130
|
+
if (!candidate) {
|
|
130
131
|
fail("Endpoint ARN cannot be empty.");
|
|
131
132
|
continue;
|
|
132
133
|
}
|
|
133
134
|
try {
|
|
134
|
-
parseSageMakerEndpointArn(
|
|
135
|
-
|
|
135
|
+
parseSageMakerEndpointArn(candidate);
|
|
136
|
+
arn = candidate;
|
|
136
137
|
} catch (err) {
|
|
137
138
|
fail(err instanceof Error ? err.message : String(err));
|
|
138
139
|
}
|
|
139
140
|
}
|
|
141
|
+
return arn;
|
|
140
142
|
}
|
|
141
143
|
|
|
142
|
-
async function promptAwsProfile(): Promise<string> {
|
|
144
|
+
async function promptAwsProfile(promptTextImpl: typeof promptText = promptText): Promise<string> {
|
|
143
145
|
while (true) {
|
|
144
|
-
const profile = await
|
|
146
|
+
const profile = await promptTextImpl(
|
|
145
147
|
"AWS profile name (must already exist in ~/.aws)",
|
|
146
148
|
process.env.AWS_PROFILE || "miru",
|
|
147
149
|
);
|
|
@@ -152,11 +154,20 @@ async function promptAwsProfile(): Promise<string> {
|
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
|
|
157
|
+
export interface SageMakerSetupDependencies {
|
|
158
|
+
promptText?: typeof promptText;
|
|
159
|
+
canPromptForCredentials?: typeof canPromptForCredentials;
|
|
160
|
+
validateConnection?: typeof validateSageMakerConnection;
|
|
161
|
+
}
|
|
162
|
+
|
|
155
163
|
/**
|
|
156
164
|
* Miru only ever inherits AWS credentials — it never creates, writes, or rotates an
|
|
157
165
|
* AWS profile. Set one up yourself first (e.g. `aws configure --profile miru`).
|
|
158
166
|
*/
|
|
159
|
-
export async function runSageMakerSetup(
|
|
167
|
+
export async function runSageMakerSetup(
|
|
168
|
+
options: RunSetupOptions = {},
|
|
169
|
+
dependencies: SageMakerSetupDependencies = {},
|
|
170
|
+
): Promise<RunSetupResult> {
|
|
160
171
|
writeStdout("");
|
|
161
172
|
printBrandBanner(process.stderr);
|
|
162
173
|
divider("─", 48, process.stderr);
|
|
@@ -168,18 +179,19 @@ export async function runSageMakerSetup(options: RunSetupOptions = {}): Promise<
|
|
|
168
179
|
);
|
|
169
180
|
writeStdout("");
|
|
170
181
|
|
|
171
|
-
const
|
|
182
|
+
const promptTextImpl = dependencies.promptText ?? promptText;
|
|
183
|
+
const arnInput = options.sagemakerArn ?? (await promptSageMakerArn(promptTextImpl));
|
|
172
184
|
const parsed = parseSageMakerEndpointArn(arnInput);
|
|
173
185
|
|
|
174
186
|
let profile = options.profile;
|
|
175
187
|
if (!profile) {
|
|
176
|
-
if (!canPromptForCredentials()) {
|
|
188
|
+
if (!(dependencies.canPromptForCredentials ?? canPromptForCredentials)()) {
|
|
177
189
|
throw new Error(
|
|
178
190
|
"An AWS profile name is required. Pass --profile <name>, or run " +
|
|
179
191
|
"`miru setup --sagemaker` interactively.",
|
|
180
192
|
);
|
|
181
193
|
}
|
|
182
|
-
profile = await promptAwsProfile();
|
|
194
|
+
profile = await promptAwsProfile(promptTextImpl);
|
|
183
195
|
}
|
|
184
196
|
|
|
185
197
|
await beginModeSwitch("sagemaker");
|
|
@@ -197,7 +209,7 @@ export async function runSageMakerSetup(options: RunSetupOptions = {}): Promise<
|
|
|
197
209
|
truncate: true,
|
|
198
210
|
truncationDirection: "Right",
|
|
199
211
|
};
|
|
200
|
-
const result = await validateSageMakerConnection(config);
|
|
212
|
+
const result = await (dependencies.validateConnection ?? validateSageMakerConnection)(config);
|
|
201
213
|
if (!result.valid) {
|
|
202
214
|
spinner.stop();
|
|
203
215
|
throw new Error(result.message);
|
package/src/snippet.ts
CHANGED
|
@@ -79,7 +79,7 @@ const RUBY_OR_ELIXIR_BODY_RE = /^\s*def\w*\b|^\s*defmodule\b/;
|
|
|
79
79
|
const MAX_DECLARATION_LOOKBACK = 40;
|
|
80
80
|
const INDENTED_BLOCK_LANGUAGES = new Set(["python", "haskell", "ocaml"]);
|
|
81
81
|
const END_BLOCK_LANGUAGES = new Set(["ruby", "elixir"]);
|
|
82
|
-
const TAG_LANGUAGES = new Set(["html", "embeddedtemplate", "xml", "vue", "svelte"]);
|
|
82
|
+
const TAG_LANGUAGES = new Set(["html", "embeddedtemplate", "xml", "vue", "svelte", "astro"]);
|
|
83
83
|
const BRACE_BLOCK_LANGUAGES = new Set([
|
|
84
84
|
"bash",
|
|
85
85
|
"c",
|
|
@@ -192,16 +192,14 @@ function completeHtmlTag(
|
|
|
192
192
|
for (let i = Math.max(0, start - MAX_DECLARATION_LOOKBACK); i <= anchor; i++) {
|
|
193
193
|
const line = lines[i] ?? "";
|
|
194
194
|
for (const match of line.matchAll(HTML_TAG_RE)) {
|
|
195
|
-
const name = match[1]
|
|
196
|
-
if (!name) {
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
195
|
+
const name = (match[1] as string).toLowerCase();
|
|
199
196
|
if (match[0].startsWith("</")) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
197
|
+
const matchingIndex = openTags.reduce(
|
|
198
|
+
(last, tag, index) => (tag.name === name ? index : last),
|
|
199
|
+
-1,
|
|
200
|
+
);
|
|
201
|
+
if (matchingIndex >= 0) {
|
|
202
|
+
openTags.splice(matchingIndex, 1);
|
|
205
203
|
}
|
|
206
204
|
} else if (!match[0].endsWith("/>") && !HTML_VOID_TAGS.has(name)) {
|
|
207
205
|
openTags.push({ name, start: i });
|
|
@@ -374,10 +372,6 @@ function compactSupportingSnippet(snippet: SnippetResult, maxLines: number): Sni
|
|
|
374
372
|
/** Pick the 0-based line index inside `content` that best matches the query. */
|
|
375
373
|
export function anchorLineOffset(content: string, query: string): number {
|
|
376
374
|
const lines = content.split("\n");
|
|
377
|
-
if (lines.length === 0) {
|
|
378
|
-
return 0;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
375
|
const terms = queryMatchTerms(query);
|
|
382
376
|
let bestIndex = Math.floor(lines.length / 2);
|
|
383
377
|
let bestScore = -1;
|
|
@@ -401,18 +395,6 @@ export function trimChunkToSnippet(
|
|
|
401
395
|
anchorLine?: number,
|
|
402
396
|
): SnippetResult {
|
|
403
397
|
const lines = chunk.content.split("\n");
|
|
404
|
-
if (lines.length === 0) {
|
|
405
|
-
return {
|
|
406
|
-
chunk,
|
|
407
|
-
meta: {
|
|
408
|
-
truncated: false,
|
|
409
|
-
anchor_line: chunk.start_line,
|
|
410
|
-
full_start_line: chunk.start_line,
|
|
411
|
-
full_end_line: chunk.end_line,
|
|
412
|
-
},
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
|
|
416
398
|
const queryAnchorOffset = anchorLineOffset(chunk.content, query);
|
|
417
399
|
const anchorOffset =
|
|
418
400
|
anchorLine == null
|
package/src/spinner.ts
CHANGED
|
@@ -8,11 +8,14 @@ export class Spinner {
|
|
|
8
8
|
private frame = 0;
|
|
9
9
|
private belowLines = 0;
|
|
10
10
|
|
|
11
|
-
constructor(
|
|
11
|
+
constructor(
|
|
12
|
+
private readonly message: string,
|
|
13
|
+
private readonly stderr: SpinnerOutput = process.stderr,
|
|
14
|
+
) {}
|
|
12
15
|
|
|
13
16
|
start(): void {
|
|
14
|
-
if (!
|
|
15
|
-
|
|
17
|
+
if (!this.stderr.isTTY) {
|
|
18
|
+
this.stderr.write(`${this.message}...\n`);
|
|
16
19
|
return;
|
|
17
20
|
}
|
|
18
21
|
this.draw();
|
|
@@ -20,17 +23,17 @@ export class Spinner {
|
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
follow(line: string): void {
|
|
23
|
-
if (!
|
|
24
|
-
|
|
26
|
+
if (!this.stderr.isTTY) {
|
|
27
|
+
this.stderr.write(`${line}\n`);
|
|
25
28
|
return;
|
|
26
29
|
}
|
|
27
|
-
const cols =
|
|
30
|
+
const cols = this.stderr.columns || 80;
|
|
28
31
|
const rows = line.split("\n").reduce((count, part) => {
|
|
29
32
|
return count + Math.max(1, Math.ceil(displayWidth(part) / cols));
|
|
30
33
|
}, 0);
|
|
31
|
-
|
|
34
|
+
this.stderr.write(`\n${line}`);
|
|
32
35
|
this.belowLines += rows;
|
|
33
|
-
|
|
36
|
+
this.stderr.write(`\x1b[${this.belowLines}A`);
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
stop(finalMessage?: string): void {
|
|
@@ -38,19 +41,19 @@ export class Spinner {
|
|
|
38
41
|
clearInterval(this.timer);
|
|
39
42
|
this.timer = null;
|
|
40
43
|
}
|
|
41
|
-
if (
|
|
42
|
-
|
|
44
|
+
if (this.stderr.isTTY) {
|
|
45
|
+
this.stderr.write("\r\x1b[K");
|
|
43
46
|
if (finalMessage) {
|
|
44
|
-
|
|
47
|
+
this.stderr.write(finalMessage);
|
|
45
48
|
}
|
|
46
49
|
if (this.belowLines > 0) {
|
|
47
|
-
|
|
50
|
+
this.stderr.write(`\x1b[${this.belowLines}B`);
|
|
48
51
|
}
|
|
49
52
|
if (finalMessage || this.belowLines > 0) {
|
|
50
|
-
|
|
53
|
+
this.stderr.write("\n");
|
|
51
54
|
}
|
|
52
55
|
} else if (finalMessage) {
|
|
53
|
-
|
|
56
|
+
this.stderr.write(`${finalMessage}\n`);
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
|
|
@@ -68,16 +71,23 @@ export class Spinner {
|
|
|
68
71
|
|
|
69
72
|
private draw(): void {
|
|
70
73
|
const glyph = FRAMES[this.frame++ % FRAMES.length];
|
|
71
|
-
|
|
74
|
+
this.stderr.write(`\r${dim(glyph)} ${this.message}`);
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
|
|
78
|
+
interface SpinnerOutput {
|
|
79
|
+
isTTY?: boolean;
|
|
80
|
+
columns?: number;
|
|
81
|
+
write(text: string): unknown;
|
|
82
|
+
}
|
|
83
|
+
|
|
75
84
|
export async function withSpinner<T>(
|
|
76
85
|
message: string,
|
|
77
86
|
fn: () => Promise<T>,
|
|
78
87
|
options?: { successMessage?: string; failMessage?: string },
|
|
88
|
+
output: SpinnerOutput = process.stderr,
|
|
79
89
|
): Promise<T> {
|
|
80
|
-
const spinner = new Spinner(message);
|
|
90
|
+
const spinner = new Spinner(message, output);
|
|
81
91
|
spinner.start();
|
|
82
92
|
try {
|
|
83
93
|
const result = await fn();
|
|
@@ -99,9 +99,6 @@ function whitespaceTokenize(text: string): string[] {
|
|
|
99
99
|
|
|
100
100
|
function splitOnPunctuation(text: string): string[] {
|
|
101
101
|
const chars = [...text];
|
|
102
|
-
if (chars.length === 0) {
|
|
103
|
-
return [];
|
|
104
|
-
}
|
|
105
102
|
|
|
106
103
|
const output: string[][] = [];
|
|
107
104
|
let startNewWord = true;
|
|
@@ -138,9 +135,6 @@ function wordpieceTokenize(
|
|
|
138
135
|
prefix: string,
|
|
139
136
|
maxInputCharsPerWord: number,
|
|
140
137
|
): string[] {
|
|
141
|
-
if (token.length === 0) {
|
|
142
|
-
return [];
|
|
143
|
-
}
|
|
144
138
|
if (token.length > maxInputCharsPerWord) {
|
|
145
139
|
return [unkToken];
|
|
146
140
|
}
|
package/src/utils.ts
CHANGED
|
@@ -165,10 +165,6 @@ export function expandChunksAtLine(
|
|
|
165
165
|
const anchorIndex = fileChunks.findIndex(
|
|
166
166
|
(chunk) => chunk.start_line === anchor.start_line && chunk.end_line === anchor.end_line,
|
|
167
167
|
);
|
|
168
|
-
if (anchorIndex < 0) {
|
|
169
|
-
return { anchor, chunks: [anchor] };
|
|
170
|
-
}
|
|
171
|
-
|
|
172
168
|
const start = Math.max(0, anchorIndex - before);
|
|
173
169
|
const end = Math.min(fileChunks.length, anchorIndex + after + 1);
|
|
174
170
|
return { anchor, chunks: fileChunks.slice(start, end) };
|
package/src/version.ts
CHANGED
|
@@ -26,8 +26,8 @@ export function miruVersion(): string {
|
|
|
26
26
|
* - 0.x releases: `0.{minor}` (chunking may break on minor bumps pre-1.0).
|
|
27
27
|
* - 1.x+: major version only.
|
|
28
28
|
*/
|
|
29
|
-
export function indexCacheEpoch(): string {
|
|
30
|
-
const parts =
|
|
29
|
+
export function indexCacheEpoch(version: string = packageJson.version): string {
|
|
30
|
+
const parts = version.split(".");
|
|
31
31
|
const major = parts[0] ?? "0";
|
|
32
32
|
const minor = parts[1] ?? "0";
|
|
33
33
|
if (major === "0") {
|
|
@@ -118,7 +118,7 @@ export async function fetchLatestPublishedVersion(fetchImpl: Fetcher = fetch): P
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/** Print update hint to stderr when a newer package is on npm (at most once per day). */
|
|
121
|
-
export async function maybeNotifyUpdate(): Promise<void> {
|
|
121
|
+
export async function maybeNotifyUpdate(fetchImpl: Fetcher = fetch): Promise<void> {
|
|
122
122
|
if (process.env.MIRU_NO_UPDATE_CHECK === "1") {
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
@@ -133,7 +133,7 @@ export async function maybeNotifyUpdate(): Promise<void> {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
try {
|
|
136
|
-
const latest = await fetchLatestPublishedVersion();
|
|
136
|
+
const latest = await fetchLatestPublishedVersion(fetchImpl);
|
|
137
137
|
await writeUpdateCheckCache(latest);
|
|
138
138
|
if (isVersionNewer(latest, current)) {
|
|
139
139
|
writeUpdateNotice(latest, current);
|