@takara-ai/miru-code 0.4.5 → 0.4.6

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 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
- MCP loads your key from `credentials.json` automatically. Set `TAKARA_API_KEY` in MCP config only to override.
28
+ MCP loads your key from `credentials.json` automatically — no env block needed in MCP config.
29
29
 
30
30
  ## Add to your IDE
31
31
 
@@ -39,7 +39,7 @@ Interactive — pick your agent and what to enable (MCP, instructions, sub-agent
39
39
  miru uninstall # remove miru config
40
40
  ```
41
41
 
42
- **Supported:** Cursor · Claude Code · Gemini CLI · Kiro · OpenCode · GitHub Copilot · Codex · VS Code
42
+ **Supported:** Cursor · Claude Code · Gemini CLI · Kiro · OpenCode · GitHub Copilot · Codex · VS Code · Visual Studio (Windows)
43
43
 
44
44
  | IDE | Config written by `miru install` |
45
45
  |-----|----------------------------------|
@@ -50,6 +50,7 @@ miru uninstall # remove miru config
50
50
  | OpenCode | `~/.config/opencode/opencode.json(c)` + agents dir |
51
51
  | GitHub Copilot | `~/.copilot/mcp-config.json` + `~/.copilot/agents/miru-code.agent.md` |
52
52
  | Codex / VS Code | MCP config only |
53
+ | Visual Studio | `%USERPROFILE%\.mcp.json` (Windows, Copilot Agent Mode) |
53
54
 
54
55
  **Team sub-agent in a repo** (optional):
55
56
 
@@ -132,12 +133,13 @@ See `.env.example` for more.
132
133
  {
133
134
  "miru": {
134
135
  "command": "miru",
135
- "args": [],
136
- "env": { "TAKARA_API_KEY": "your-token" }
136
+ "args": []
137
137
  }
138
138
  }
139
139
  ```
140
140
 
141
+ Run `miru setup` once so the server can load your key from `credentials.json`.
142
+
141
143
  Use `bunx` + `@takara-ai/miru-code` if `miru` is not global. Wrapper key varies by IDE (`mcpServers`, `servers`, or `mcp`).
142
144
 
143
145
  ## Developing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takara-ai/miru-code",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "Miru (見る) — hybrid code search for agents. Bun, TypeScript, Takara embeddings.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/cache.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { rm } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
- import { computeSourceCacheKey } from "./utils.ts";
5
4
  import { resolveEmbeddingDimensions, resolveEmbeddingModel } from "./embeddings/openai.ts";
6
5
  import {
7
6
  loadBm25,
@@ -13,6 +12,7 @@ import {
13
12
  import { resolveSemanticStorage, semanticStorageFromMetadata } from "./index/vector-storage.ts";
14
13
  import type { Chunk, ContentType } from "./types.ts";
15
14
  import { chunkFromDict } from "./types.ts";
15
+ import { computeSourceCacheKey } from "./utils.ts";
16
16
 
17
17
  export function resolveCacheFolder(): string {
18
18
  const name = "miru";
package/src/cli.ts CHANGED
@@ -11,8 +11,8 @@ import {
11
11
  success,
12
12
  } from "./cli-ui.ts";
13
13
  import { loadStoredCredentials } from "./credentials.ts";
14
- import { loadEnvFiles } from "./env-files.ts";
15
14
  import { normalizeTakaraApiKeyEnv } from "./env.ts";
15
+ import { loadEnvFiles } from "./env-files.ts";
16
16
  import {
17
17
  AGENT_IDS,
18
18
  formatUnknownAgent,
package/src/env.ts CHANGED
@@ -33,7 +33,8 @@ 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"]);
36
+ const MCP_KEY_PLACEHOLDER = "$" + "{TAKARA_API_KEY}";
37
+ const TAKARA_API_KEY_PLACEHOLDERS = new Set([MCP_KEY_PLACEHOLDER, "$TAKARA_API_KEY"]);
37
38
 
38
39
  /** True when the value is a real key, not empty or an unexpanded MCP env placeholder. */
39
40
  export function isUsableTakaraApiKey(value: string | undefined): boolean {
@@ -1,4 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
+ import { readdir } from "node:fs/promises";
2
3
  import { homedir } from "node:os";
3
4
  import { join } from "node:path";
4
5
  import type { AgentId } from "../agents.ts";
@@ -19,30 +20,21 @@ export const MIRU_END = "<!-- miru:end -->";
19
20
 
20
21
  const HOME = homedir();
21
22
 
22
- const TAKARA_KEY_PLACEHOLDER = "$" + "{TAKARA_API_KEY}";
23
-
24
- const MCP_ENV: Record<string, string> = {
25
- TAKARA_API_KEY: TAKARA_KEY_PLACEHOLDER,
26
- };
27
-
28
23
  const STDIO_SERVER_CONFIG: Record<string, unknown> = {
29
24
  command: "bunx",
30
25
  args: ["@takara-ai/miru-code"],
31
26
  type: "stdio",
32
- env: MCP_ENV,
33
27
  };
34
28
 
35
29
  const BARE_STDIO_SERVER_CONFIG: Record<string, unknown> = {
36
30
  command: "bunx",
37
31
  args: ["@takara-ai/miru-code"],
38
- env: MCP_ENV,
39
32
  };
40
33
 
41
34
  const OPENCODE_SERVER_CONFIG: Record<string, unknown> = {
42
35
  command: ["bunx", "@takara-ai/miru-code"],
43
36
  type: "local",
44
37
  enabled: true,
45
- environment: MCP_ENV,
46
38
  };
47
39
 
48
40
  export const INSTRUCTIONS = `${MIRU_START}
@@ -54,7 +46,7 @@ A \`miru\` MCP server is available with two tools:
54
46
 
55
47
  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
48
 
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.
49
+ Run \`miru setup\` once to store your API key — the MCP server loads it from \`credentials.json\` automatically.
58
50
 
59
51
  Pass \`--content docs\` to search documentation, \`--content config\` for config files, or \`--content all\` for everything.
60
52
 
@@ -92,7 +84,7 @@ export interface McpConfig {
92
84
  }
93
85
 
94
86
  export interface AgentTarget {
95
- id: AgentId | "codex" | "vscode";
87
+ id: AgentId | "codex" | "vscode" | "visualstudio";
96
88
  displayName: string;
97
89
  binary: string | null;
98
90
  configDir: string | null;
@@ -128,6 +120,45 @@ export function vscodeMcpPath(): string {
128
120
  return join(xdg, "Code", "User", "mcp.json");
129
121
  }
130
122
 
123
+ /** Global MCP config for Visual Studio (GitHub Copilot Agent Mode). */
124
+ export function visualStudioMcpPath(): string {
125
+ const profile = process.env.USERPROFILE ?? HOME;
126
+ return join(profile, ".mcp.json");
127
+ }
128
+
129
+ export function visualStudioInstallDir(): string | null {
130
+ if (process.platform !== "win32") {
131
+ return null;
132
+ }
133
+ const programFiles = process.env.ProgramFiles ?? join("C:", "Program Files");
134
+ return join(programFiles, "Microsoft Visual Studio");
135
+ }
136
+
137
+ async function detectVisualStudio(): Promise<boolean> {
138
+ if (process.platform !== "win32") {
139
+ return false;
140
+ }
141
+
142
+ const installDir = visualStudioInstallDir();
143
+ if (installDir && existsSync(installDir)) {
144
+ try {
145
+ const entries = await readdir(installDir);
146
+ if (entries.length > 0) {
147
+ return true;
148
+ }
149
+ } catch {
150
+ // ignore unreadable install directory
151
+ }
152
+ }
153
+
154
+ try {
155
+ const proc = Bun.spawn(["where", "devenv"], { stdout: "pipe", stderr: "ignore" });
156
+ return (await proc.exited) === 0;
157
+ } catch {
158
+ return false;
159
+ }
160
+ }
161
+
131
162
  function jsonMcp(path: string, key: string, entry: Record<string, unknown>): McpConfig {
132
163
  return { path, key, memberKey: "miru", entry, format: "json" };
133
164
  }
@@ -171,7 +202,6 @@ export const AGENT_TARGETS: AgentTarget[] = [
171
202
  mcp: jsonMcp(join(HOME, ".kiro", "settings", "mcp.json"), "mcpServers", {
172
203
  ...STDIO_SERVER_CONFIG,
173
204
  env: {
174
- ...MCP_ENV,
175
205
  PATH: "/usr/local/bin:/usr/bin:/bin",
176
206
  },
177
207
  }),
@@ -225,19 +255,34 @@ export const AGENT_TARGETS: AgentTarget[] = [
225
255
  subagentPath: null,
226
256
  subagentId: null,
227
257
  },
258
+ {
259
+ id: "visualstudio",
260
+ displayName: process.platform === "win32" ? "Visual Studio" : "Visual Studio (Windows)",
261
+ binary: null,
262
+ configDir: visualStudioInstallDir(),
263
+ mcp: jsonMcp(visualStudioMcpPath(), "servers", STDIO_SERVER_CONFIG),
264
+ instructionsPath: null,
265
+ subagentPath: null,
266
+ subagentId: null,
267
+ },
228
268
  ];
229
269
 
270
+ async function commandOnPath(command: string): Promise<boolean> {
271
+ const lookup = process.platform === "win32" ? ["where", command] : ["which", command];
272
+ try {
273
+ const proc = Bun.spawn(lookup, { stdout: "pipe", stderr: "ignore" });
274
+ return (await proc.exited) === 0;
275
+ } catch {
276
+ return false;
277
+ }
278
+ }
279
+
230
280
  export async function isAgentDetected(agent: AgentTarget): Promise<boolean> {
231
- if (agent.binary) {
232
- try {
233
- const proc = Bun.spawn(["which", agent.binary], { stdout: "pipe", stderr: "ignore" });
234
- const code = await proc.exited;
235
- if (code === 0) {
236
- return true;
237
- }
238
- } catch {
239
- // which unavailable
240
- }
281
+ if (agent.id === "visualstudio") {
282
+ return detectVisualStudio();
283
+ }
284
+ if (agent.binary && (await commandOnPath(agent.binary))) {
285
+ return true;
241
286
  }
242
287
  if (agent.configDir) {
243
288
  return Bun.file(agent.configDir).exists();
@@ -6,7 +6,6 @@ const CODEX_MCP_HEADER = "[mcp_servers.miru]";
6
6
  const CODEX_MCP_BLOCK = `[mcp_servers.miru]
7
7
  command = "bunx"
8
8
  args = ["@takara-ai/miru-code"]
9
- env = { TAKARA_API_KEY = "\${TAKARA_API_KEY}" }
10
9
  `;
11
10
 
12
11
  /** Strip line and block comments so JSONC configs can be parsed. */