@takara-ai/miru-code 0.4.3 → 0.4.5
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 +1 -1
- package/package.json +1 -1
- package/src/cache.ts +4 -3
- package/src/chunking/structural.ts +35 -1
- package/src/cli.ts +6 -5
- package/src/credentials.ts +2 -1
- package/src/env.ts +20 -2
- package/src/index/files.ts +4 -1
- package/src/installer/agents.ts +1 -1
- package/src/installer/installer.ts +1 -1
- package/src/setup.ts +1 -1
- package/src/utils.ts +5 -0
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ miru setup --key YOUR_TOKEN # non-interactive
|
|
|
25
25
|
miru setup --clear # remove stored key
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
MCP loads your key from `credentials.json` automatically. Set `TAKARA_API_KEY` in MCP config only to override.
|
|
29
29
|
|
|
30
30
|
## Add to your IDE
|
|
31
31
|
|
package/package.json
CHANGED
package/src/cache.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { rm } from "node:fs/promises";
|
|
3
|
-
import { join
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { computeSourceCacheKey } from "./utils.ts";
|
|
4
5
|
import { resolveEmbeddingDimensions, resolveEmbeddingModel } from "./embeddings/openai.ts";
|
|
5
6
|
import {
|
|
6
7
|
loadBm25,
|
|
@@ -30,8 +31,8 @@ export function resolveCacheFolder(): string {
|
|
|
30
31
|
return base;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
export function findIndexCachePath(path: string): string {
|
|
34
|
-
const normalized =
|
|
34
|
+
export function findIndexCachePath(path: string, ref?: string | null): string {
|
|
35
|
+
const normalized = computeSourceCacheKey(path, ref);
|
|
35
36
|
const subdir = createHash("sha256").update(normalized, "utf-8").digest("hex");
|
|
36
37
|
return join(resolveCacheFolder(), subdir, "index");
|
|
37
38
|
}
|
|
@@ -5,7 +5,16 @@ import {
|
|
|
5
5
|
splitLinesKeepEnds,
|
|
6
6
|
} from "./lines.ts";
|
|
7
7
|
|
|
8
|
-
const SUPPORTED_STRUCTURAL_LANGUAGES = new Set([
|
|
8
|
+
const SUPPORTED_STRUCTURAL_LANGUAGES = new Set([
|
|
9
|
+
"python",
|
|
10
|
+
"go",
|
|
11
|
+
"typescript",
|
|
12
|
+
"javascript",
|
|
13
|
+
"cpp",
|
|
14
|
+
"c",
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
const BRACE_FAMILY_LANGUAGES = new Set(["cpp", "c"]);
|
|
9
18
|
|
|
10
19
|
export function chunkStructural(
|
|
11
20
|
source: string,
|
|
@@ -164,9 +173,27 @@ function declPattern(language: string): RegExp {
|
|
|
164
173
|
if (language === "go") {
|
|
165
174
|
return /^\s*(func\b|type\b.*\b(struct|interface)\b)/;
|
|
166
175
|
}
|
|
176
|
+
if (language === "cpp") {
|
|
177
|
+
return /^\s*(?:(?:template\s*<[^>]*>\s*)?(?:class|struct|namespace|enum(?:\s+class)?)\b|(?!(?:if|for|while|switch|catch|return|sizeof|static_assert|else|do)\b)(?:[\w:~<>,*&]+\s+)+\w+\s*\([^;{}]*\)\s*(?:const)?\s*(?:override)?(?:\s*=\s*0)?)/;
|
|
178
|
+
}
|
|
179
|
+
if (language === "c") {
|
|
180
|
+
return /^\s*(?:struct|enum|union|typedef)\b|(?!(?:if|for|while|switch|return|sizeof|else|do)\b)(?:[\w:*\s]+\s+)+\w+\s*\([^;{}]*\)/;
|
|
181
|
+
}
|
|
167
182
|
return /^\s*(export\s+)?(async\s+function\b|function\b|class\b|interface\b|type\b|(?:const|let|var)\s+[A-Za-z_$][\w$]*\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)(?:\s*:\s*[^=]+)?\s*=>)/;
|
|
168
183
|
}
|
|
169
184
|
|
|
185
|
+
function isBraceFamilyTypeDecl(text: string, language: string): boolean {
|
|
186
|
+
if (language === "cpp") {
|
|
187
|
+
return /^\s*(?:(?:template\s*<[^>]*>\s*)?(?:class|struct|namespace|enum(?:\s+class)?)\b)/.test(
|
|
188
|
+
text,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
if (language === "c") {
|
|
192
|
+
return /^\s*(?:struct|enum|union)\b/.test(text);
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
170
197
|
function braceUnits(lines: LineGroup[], language: string): ChunkBoundary[] {
|
|
171
198
|
const units: ChunkBoundary[] = [];
|
|
172
199
|
const full = lines.map((l) => l.text).join("");
|
|
@@ -178,6 +205,13 @@ function braceUnits(lines: LineGroup[], language: string): ChunkBoundary[] {
|
|
|
178
205
|
if (!re.test(text)) {
|
|
179
206
|
continue;
|
|
180
207
|
}
|
|
208
|
+
if (
|
|
209
|
+
BRACE_FAMILY_LANGUAGES.has(language) &&
|
|
210
|
+
lineIndent(text) > 0 &&
|
|
211
|
+
!isBraceFamilyTypeDecl(text, language)
|
|
212
|
+
) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
181
215
|
if (firstDeclStart === null) {
|
|
182
216
|
firstDeclStart = lines[i]?.start;
|
|
183
217
|
}
|
package/src/cli.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
2
|
import { type AgentId, writeAgentFile } from "./agents.ts";
|
|
4
3
|
import { clearCache } from "./cache.ts";
|
|
5
4
|
import {
|
|
@@ -13,6 +12,7 @@ import {
|
|
|
13
12
|
} from "./cli-ui.ts";
|
|
14
13
|
import { loadStoredCredentials } from "./credentials.ts";
|
|
15
14
|
import { loadEnvFiles } from "./env-files.ts";
|
|
15
|
+
import { normalizeTakaraApiKeyEnv } from "./env.ts";
|
|
16
16
|
import {
|
|
17
17
|
AGENT_IDS,
|
|
18
18
|
formatUnknownAgent,
|
|
@@ -26,9 +26,10 @@ import { MiruIndex } from "./miru-index.ts";
|
|
|
26
26
|
import { ensureCredentials, runClearCredentials, runSetup } from "./setup.ts";
|
|
27
27
|
import { withSpinner } from "./spinner.ts";
|
|
28
28
|
import type { ContentType, SearchResult } from "./types.ts";
|
|
29
|
-
import { formatResults, resolveChunk, resolveContent } from "./utils.ts";
|
|
29
|
+
import { formatResults, resolveChunk, resolveContent, resolveSearchPath } from "./utils.ts";
|
|
30
30
|
|
|
31
31
|
loadEnvFiles();
|
|
32
|
+
normalizeTakaraApiKeyEnv();
|
|
32
33
|
await loadStoredCredentials();
|
|
33
34
|
|
|
34
35
|
const CLI_COMMANDS = new Set([
|
|
@@ -281,7 +282,7 @@ async function runCli(argv: string[]): Promise<void> {
|
|
|
281
282
|
}
|
|
282
283
|
|
|
283
284
|
if (command === "clear") {
|
|
284
|
-
const path =
|
|
285
|
+
const path = resolveSearchPath(rest[0] ?? process.cwd());
|
|
285
286
|
await runClear(path);
|
|
286
287
|
return;
|
|
287
288
|
}
|
|
@@ -296,7 +297,7 @@ async function runCli(argv: string[]): Promise<void> {
|
|
|
296
297
|
printCommandHelp("search");
|
|
297
298
|
process.exit(1);
|
|
298
299
|
}
|
|
299
|
-
const path =
|
|
300
|
+
const path = resolveSearchPath(sizedRest[1] ?? process.cwd());
|
|
300
301
|
await runSearch(path, query, topK, content, jsonFlag);
|
|
301
302
|
return;
|
|
302
303
|
}
|
|
@@ -309,7 +310,7 @@ async function runCli(argv: string[]): Promise<void> {
|
|
|
309
310
|
process.exit(1);
|
|
310
311
|
}
|
|
311
312
|
const line = Number(lineRaw);
|
|
312
|
-
const path =
|
|
313
|
+
const path = resolveSearchPath(sizedRest[2] ?? process.cwd());
|
|
313
314
|
try {
|
|
314
315
|
await runFindRelated(path, filePath, line, topK, content, jsonFlag);
|
|
315
316
|
} catch (err) {
|
package/src/credentials.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { hasTakaraApiKeyInEnv } from "./env.ts";
|
|
3
|
+
import { hasTakaraApiKeyInEnv, normalizeTakaraApiKeyEnv } from "./env.ts";
|
|
4
4
|
|
|
5
5
|
const CREDENTIALS_FILENAME = "credentials.json";
|
|
6
6
|
const CREDENTIALS_VERSION = 1;
|
|
@@ -49,6 +49,7 @@ export async function readStoredCredentials(): Promise<StoredCredentials | null>
|
|
|
49
49
|
|
|
50
50
|
/** Set TAKARA_API_KEY from the user credentials file when env is unset. */
|
|
51
51
|
export async function loadStoredCredentials(): Promise<boolean> {
|
|
52
|
+
normalizeTakaraApiKeyEnv();
|
|
52
53
|
if (hasTakaraApiKeyInEnv()) {
|
|
53
54
|
return false;
|
|
54
55
|
}
|
package/src/env.ts
CHANGED
|
@@ -33,13 +33,31 @@ export function envOptionalInt(names: string[], min = 1): number | undefined {
|
|
|
33
33
|
|
|
34
34
|
export const TAKARA_API_KEY_ENV = "TAKARA_API_KEY";
|
|
35
35
|
|
|
36
|
+
const TAKARA_API_KEY_PLACEHOLDERS = new Set(["${TAKARA_API_KEY}", "$TAKARA_API_KEY"]);
|
|
37
|
+
|
|
38
|
+
/** True when the value is a real key, not empty or an unexpanded MCP env placeholder. */
|
|
39
|
+
export function isUsableTakaraApiKey(value: string | undefined): boolean {
|
|
40
|
+
const key = value?.trim() ?? "";
|
|
41
|
+
if (!key) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return !TAKARA_API_KEY_PLACEHOLDERS.has(key);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Drop placeholder values so credentials.json can supply the key for MCP. */
|
|
48
|
+
export function normalizeTakaraApiKeyEnv(): void {
|
|
49
|
+
if (!isUsableTakaraApiKey(process.env[TAKARA_API_KEY_ENV])) {
|
|
50
|
+
delete process.env[TAKARA_API_KEY_ENV];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
36
54
|
export function hasTakaraApiKeyInEnv(): boolean {
|
|
37
|
-
return
|
|
55
|
+
return isUsableTakaraApiKey(process.env[TAKARA_API_KEY_ENV]);
|
|
38
56
|
}
|
|
39
57
|
|
|
40
58
|
export function resolveEmbeddingApiKey(): string {
|
|
41
59
|
const key = process.env[TAKARA_API_KEY_ENV]?.trim() ?? "";
|
|
42
|
-
if (!key) {
|
|
60
|
+
if (!isUsableTakaraApiKey(key)) {
|
|
43
61
|
throw new Error(
|
|
44
62
|
"Takara API key required. Run `miru setup`, or set TAKARA_API_KEY in your MCP server env or .env.local.",
|
|
45
63
|
);
|
package/src/index/files.ts
CHANGED
|
@@ -21,11 +21,14 @@ const EXTENSION_TO_LANGUAGE: Record<string, string> = {
|
|
|
21
21
|
".kt": "kotlin",
|
|
22
22
|
".kts": "kotlin",
|
|
23
23
|
".c": "c",
|
|
24
|
-
".h": "
|
|
24
|
+
".h": "cpp",
|
|
25
25
|
".cpp": "cpp",
|
|
26
26
|
".cc": "cpp",
|
|
27
27
|
".cxx": "cpp",
|
|
28
28
|
".hpp": "cpp",
|
|
29
|
+
".hh": "cpp",
|
|
30
|
+
".hxx": "cpp",
|
|
31
|
+
".h++": "cpp",
|
|
29
32
|
".cs": "csharp",
|
|
30
33
|
".rb": "ruby",
|
|
31
34
|
".php": "php",
|
package/src/installer/agents.ts
CHANGED
|
@@ -54,7 +54,7 @@ A \`miru\` MCP server is available with two tools:
|
|
|
54
54
|
|
|
55
55
|
Always call \`search\` before using Grep, Glob, or Read to explore the codebase. Use Grep/Glob/Read only for exact path lookup, exhaustive literal matches, or when the returned chunk lacks enough context.
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
Run \`miru setup\` once to store your API key — the MCP server loads it from \`credentials.json\`. Optionally set \`TAKARA_API_KEY\` in MCP config to override.
|
|
58
58
|
|
|
59
59
|
Pass \`--content docs\` to search documentation, \`--content config\` for config files, or \`--content all\` for everything.
|
|
60
60
|
|
|
@@ -246,7 +246,7 @@ export async function runInstaller(mode: InstallMode): Promise<void> {
|
|
|
246
246
|
install ? "Done! Restart your agents to pick up changes." : "Done! Configuration removed.",
|
|
247
247
|
);
|
|
248
248
|
if (install) {
|
|
249
|
-
hint("Run miru setup
|
|
249
|
+
hint("Run miru setup — MCP loads the key from credentials.json automatically.");
|
|
250
250
|
}
|
|
251
251
|
writeStdout("");
|
|
252
252
|
}
|
package/src/setup.ts
CHANGED
|
@@ -73,7 +73,7 @@ export async function runSetup(options: RunSetupOptions = {}): Promise<string> {
|
|
|
73
73
|
process.env.TAKARA_API_KEY = apiKey;
|
|
74
74
|
writeStderr("");
|
|
75
75
|
success(`Saved credentials to ${path}`);
|
|
76
|
-
hint("
|
|
76
|
+
hint("MCP loads this key from credentials.json automatically.");
|
|
77
77
|
writeStderr("");
|
|
78
78
|
return path;
|
|
79
79
|
}
|
package/src/utils.ts
CHANGED
|
@@ -62,3 +62,8 @@ export function computeSourceCacheKey(source: string, ref?: string | null): stri
|
|
|
62
62
|
}
|
|
63
63
|
return resolve(source);
|
|
64
64
|
}
|
|
65
|
+
|
|
66
|
+
/** Resolve a local search path; leave git URLs unchanged for remote indexing. */
|
|
67
|
+
export function resolveSearchPath(path: string): string {
|
|
68
|
+
return isGitUrl(path) ? path : resolve(path);
|
|
69
|
+
}
|