@takara-ai/miru-code 0.4.0 → 0.4.2

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
@@ -1,409 +1,154 @@
1
1
  # Miru (見る)
2
2
 
3
- Hybrid code search for AI agents — **TypeScript**, **Bun**, and **Takara embeddings**.
3
+ Hybrid code search for AI agents — **Bun**, **TypeScript**, **Takara embeddings**.
4
4
 
5
- ## How to read this doc
5
+ Find code by meaning, not grep. Miru returns the best **chunks** (path, lines, snippet) for questions like “where is auth middleware configured?”
6
6
 
7
- | If you want to… | Start here |
8
- |-----------------|------------|
9
- | Use Miru in an IDE (Cursor, Claude Code, etc.) | [User flow](#user-flow) → [Credentials](#credentials) → [IDE integration](#ide-integration) |
10
- | Run search from a terminal or script | [User flow](#user-flow) → [CLI](#cli) |
11
- | Embed Miru in your own tool | [Library](#library) |
12
- | Hack on this repo | [Developing Miru](#developing-miru) |
13
-
14
- The sections below follow that order: **what Miru does**, **how you use it end to end**, then **reference** (CLI flags, env vars, per-IDE MCP JSON).
15
-
16
- ## What is Miru?
17
-
18
- *Miru* (見る) means “to see” or “to look” in Japanese. Miru helps coding agents **find code by meaning** instead of guessing file paths or running exhaustive greps.
19
-
20
- When an agent asks “where is auth middleware configured?” or “how do we batch embeddings?”, Miru returns the most relevant **chunks** — file path, line range, and snippet — so the agent can read only what matters.
21
-
22
- ### Why hybrid search?
23
-
24
- Pure semantic search misses exact symbol matches. Pure keyword search misses paraphrased intent. Miru combines both:
25
-
26
- 1. **Dense (semantic)** — Takara code embeddings (`ds1-potion-code-16m`) over structurally chunked source files.
27
- 2. **Sparse (BM25)** — keyword scoring with an inverted index; parallelized in Bun workers when the index has 256+ chunks.
28
- 3. **RRF fusion** — reciprocal rank fusion merges the two ranked lists.
29
- 4. **Reranking** — code-tuned penalties and boosts (e.g. multi-chunk files, query-term overlap).
30
-
31
- You can index **code**, **docs**, **config**, or **all** of the above via `--content` (CLI/MCP) or when building an index in the library.
32
-
33
- ### Caching (what “fresh” means)
34
-
35
- | Layer | Behavior |
36
- |-------|----------|
37
- | **First search** | Builds an index (chunk → embed → BM25 + vectors), then writes a disk cache. |
38
- | **MCP server (local repo, same session)** | Watches the workspace and **incrementally** re-indexes changed files; updates the disk cache. Disable with `MIRU_MCP_WATCH=0`. |
39
- | **CLI or a new MCP session** | Reuses the disk cache if the embedding model, content types, and indexed files still match. Cache does **not** detect file edits by itself — run `miru clear <path>` after large changes, or rely on MCP live updates. |
40
- | **Remote `https://` repos** | Cloned per session; no filesystem watch. Use `--ref` for a branch/tag. |
41
-
42
- Cache locations: `~/Library/Caches/miru` (macOS), `~/.cache/miru` (Linux), `%LOCALAPPDATA%\miru\Cache` (Windows).
43
-
44
- ### How agents use Miru
45
-
46
- | Mode | Best for | What the agent gets |
47
- |------|----------|---------------------|
48
- | **MCP server** | Cursor, Claude Code, VS Code Copilot, Gemini CLI, Kiro, OpenCode | `search` and `find_related` tools; pass `repo` (local path or `https://` git URL) |
49
- | **CLI sub-agent** | Any agent with shell access | `miru install` (global) or `miru init --agent <id>` (per-repo) |
50
-
51
- Both modes share the same indexing logic and disk cache layout.
52
-
53
- ## User flow
54
-
55
- Typical path from zero to useful search:
56
-
57
- ```text
58
- 1. Install Bun + Miru → bun add -g @takara-ai/miru-code (or bunx for one-off)
59
- 2. Credentials → miru setup OR TAKARA_API_KEY in MCP env / .env.local
60
- 3. Integrate → miru install (MCP + instructions + sub-agent)
61
- 4. Search → Agent calls search("…", repo: project root)
62
- 5. Go deeper (optional) → find_related(file, line) on a hit
63
- 6. Refresh cache (if needed) → miru clear . after big refactors when using CLI only
64
- ```
65
-
66
- **Agent workflow:** `search` first → read returned chunks → `find_related` on a promising `file_path` + line → open full files only when the chunk is not enough → use grep for exhaustive literal matches.
67
-
68
- **`repo` rules:** Local workspaces use the **project root** path. Remote repos use an explicit **`https://` or `http://`** URL (not `git@` SSH). Never guess URLs.
7
+ **Requires:** [Bun](https://bun.sh) 1.1+ · [Takara API key](https://takara.ai)
69
8
 
70
9
  ## Install
71
10
 
72
- Requires [Bun](https://bun.sh) 1.1+ on your PATH.
73
-
74
11
  ```bash
75
- # One-off
76
- bunx @takara-ai/miru-code search "auth middleware" ./src
77
-
78
- # Global CLI
79
12
  bun add -g @takara-ai/miru-code
80
- miru search "auth middleware" ./src
81
-
82
- # Library
83
- bun add @takara-ai/miru-code
84
13
  ```
85
14
 
86
- ## Credentials
87
-
88
- Miru needs a **Takara bearer token** for embeddings (`TAKARA_API_KEY`).
89
-
90
- **CLI / terminal (recommended for local dev):**
15
+ ## Set up API key
91
16
 
92
17
  ```bash
93
- miru setup # interactive; validates key and stores locally
94
- miru setup --key TOKEN # non-interactive
95
- miru setup --clear # remove stored key
18
+ miru setup
96
19
  ```
97
20
 
98
- Stored at `~/Library/Application Support/miru/credentials.json` (macOS), `~/.config/miru/` (Linux), or `%APPDATA%\miru\` (Windows). Override directory with `MIRU_CREDENTIALS_DIR`.
99
-
100
- **MCP in an IDE:** Put `TAKARA_API_KEY` in the MCP server `env` block. MCP does not reliably inherit your shell or `.env.local` — set it explicitly.
101
-
102
- **This repo:** `bun install` then add `TAKARA_API_KEY` to `.env.local` (Bun loads it when you run from the project root).
103
-
104
- ## IDE integration
105
-
106
- ### Recommended: `miru install`
107
-
108
- Interactive setup (like Semble’s `semble install`). Detects installed agents and configures any combination of:
109
-
110
- | Integration | What it does |
111
- |-------------|----------------|
112
- | **MCP server** | Adds `miru` to your agent’s MCP config (`search`, `find_related`) |
113
- | **Instructions** | Appends a marked block to `CLAUDE.md`, `GEMINI.md`, `AGENTS.md`, etc. |
114
- | **Sub-agent** | Installs `miru-code` under your **user** config (global, all projects) |
21
+ Validates your key and saves it locally. Skip this if you prefer — `miru search` or `miru install` will prompt on first run.
115
22
 
116
23
  ```bash
117
- bun add -g @takara-ai/miru-code
118
- miru setup # store API key (or set TAKARA_API_KEY for ${TAKARA_API_KEY} in MCP)
119
- miru install
24
+ miru setup --key YOUR_TOKEN # non-interactive
25
+ miru setup --clear # remove stored key
120
26
  ```
121
27
 
122
- Undo with `miru uninstall`. Restart your IDE/agent session after install.
123
-
124
- **Supported agents:** Claude Code, Cursor, Gemini CLI, Kiro, OpenCode, GitHub Copilot, Codex, VS Code.
125
-
126
- ### Global paths (after `miru install`)
127
-
128
- | Agent | MCP config | Sub-agent |
129
- |-------|------------|-----------|
130
- | Claude Code | `~/.claude.json` | `~/.claude/agents/miru-code.md` |
131
- | Cursor | `~/.cursor/mcp.json` | `~/.cursor/agents/miru-code.md` |
132
- | Gemini CLI | `~/.gemini/settings.json` | `~/.gemini/agents/miru-code.md` |
133
- | Kiro | `~/.kiro/settings/mcp.json` | `~/.kiro/agents/miru-code.md` |
134
- | OpenCode | `~/.config/opencode/opencode.json(c)` | `~/.config/opencode/agents/miru-code.md` |
135
- | GitHub Copilot | `~/.copilot/mcp-config.json` | `~/.copilot/agents/miru-code.agent.md` |
136
- | Codex | `~/.codex/config.toml` | (MCP only) |
137
- | VS Code | user profile `mcp.json` | (MCP only) |
28
+ For MCP in an IDE, export `TAKARA_API_KEY` or paste your token into the MCP `env` block after install.
138
29
 
139
- MCP entries use `"env": { "TAKARA_API_KEY": "${TAKARA_API_KEY}" }` where the IDE supports expansion. Export the variable or edit the config with your token.
140
-
141
- ### Per-project sub-agent (`miru init`)
142
-
143
- To commit a sub-agent into a repo (team-shared) instead of global user config:
30
+ ## Add to your IDE
144
31
 
145
32
  ```bash
146
- miru init --agent claude --force # → .claude/agents/miru-code.md in cwd
33
+ miru install
147
34
  ```
148
35
 
149
- Agents: `cursor`, `claude`, `copilot`, `gemini`, `kiro`, `opencode`.
150
-
151
- ### MCP server
152
-
153
- No subcommand starts stdio MCP:
36
+ Interactive — pick your agent and what to enable (MCP, instructions, sub-agent). Restart the IDE when done.
154
37
 
155
38
  ```bash
156
- bunx @takara-ai/miru-code
157
- # or: miru
39
+ miru uninstall # remove miru config
158
40
  ```
159
41
 
160
- Optional startup flags: `--ref BRANCH` (default git ref for remote repos), `--content code docs …` (same types as CLI).
161
-
162
- **Tools**
163
-
164
- | Tool | Purpose |
165
- |------|---------|
166
- | `search` | Natural-language or code query; requires `repo`; optional `top_k` (default 5) |
167
- | `find_related` | Similar chunks to `file_path` + `line` (from a search hit); requires `repo`; optional `top_k` |
168
-
169
- Indexing runs on the first tool call for each `repo`. Later calls in the same MCP session reuse memory and refresh local files via watch (see [Caching](#caching-what-fresh-means)).
170
-
171
- Use `bunx` in MCP config (`command`: `bunx`, `args`: `["@takara-ai/miru-code"]`). After `bun add -g`, you may use `"command": "miru"` instead.
172
-
173
- #### Shared env block
42
+ **Supported:** Cursor · Claude Code · Gemini CLI · Kiro · OpenCode · GitHub Copilot · Codex · VS Code
174
43
 
175
- Use in every MCP config (some IDEs use `environment` instead of `env`):
44
+ | IDE | Config written by `miru install` |
45
+ |-----|----------------------------------|
46
+ | Cursor | `~/.cursor/mcp.json` + `~/.cursor/agents/miru-code.md` |
47
+ | Claude Code | `~/.claude.json` + `~/.claude/agents/miru-code.md` |
48
+ | Gemini CLI | `~/.gemini/settings.json` + `~/.gemini/agents/miru-code.md` |
49
+ | Kiro | `~/.kiro/settings/mcp.json` + `~/.kiro/agents/miru-code.md` |
50
+ | OpenCode | `~/.config/opencode/opencode.json(c)` + agents dir |
51
+ | GitHub Copilot | `~/.copilot/mcp-config.json` + `~/.copilot/agents/miru-code.agent.md` |
52
+ | Codex / VS Code | MCP config only |
176
53
 
177
- ```json
178
- {
179
- "TAKARA_API_KEY": "your-takara-bearer-token",
180
- "MIRU_OPENAI_BASE_URL": "https://infer.dev.takara.ai/v1",
181
- "MIRU_OPENAI_EMBEDDING_MODEL": "ds1-potion-code-16m",
182
- "MIRU_EMBEDDING_DIMENSIONS": "256"
183
- }
184
- ```
54
+ **Team sub-agent in a repo** (optional):
185
55
 
186
- ### Cursor
187
-
188
- `~/.cursor/mcp.json` or project `.cursor/mcp.json`:
189
-
190
- ```json
191
- {
192
- "mcpServers": {
193
- "miru": {
194
- "command": "bunx",
195
- "args": ["@takara-ai/miru-code"],
196
- "env": {
197
- "TAKARA_API_KEY": "your-takara-bearer-token"
198
- }
199
- }
200
- }
201
- }
56
+ ```bash
57
+ miru init --agent claude --force
202
58
  ```
203
59
 
204
- Sub-agent: `miru install` (global) or `miru init --agent cursor` (project-local).
60
+ ## Try it
205
61
 
206
- ### Claude Code
207
-
208
- Project `.mcp.json` or `~/.claude.json`:
209
-
210
- ```json
211
- {
212
- "mcpServers": {
213
- "miru": {
214
- "type": "stdio",
215
- "command": "bunx",
216
- "args": ["@takara-ai/miru-code"],
217
- "env": {
218
- "TAKARA_API_KEY": "${TAKARA_API_KEY}"
219
- }
220
- }
221
- }
222
- }
62
+ ```bash
63
+ miru search "auth middleware" ./src
64
+ miru find-related src/auth.ts 42 ./src
223
65
  ```
224
66
 
225
- `${VAR}` expansion is supported. Restart and run `/mcp`, or: `claude mcp add miru -s project -- bunx @takara-ai/miru-code`.
226
-
227
- Sub-agent: `miru install` (global) or `miru init --agent claude` (project-local).
228
-
229
- ### VS Code / GitHub Copilot
230
-
231
- `.vscode/mcp.json` or user profile (**MCP: Open User Configuration**). Note `"servers"` not `mcpServers`:
67
+ Terminal output is human-readable; use `--json` for scripts. One-off without installing:
232
68
 
233
- ```json
234
- {
235
- "servers": {
236
- "miru": {
237
- "type": "stdio",
238
- "command": "bunx",
239
- "args": ["@takara-ai/miru-code"],
240
- "env": {
241
- "TAKARA_API_KEY": "your-takara-bearer-token"
242
- }
243
- }
244
- }
245
- }
69
+ ```bash
70
+ bunx @takara-ai/miru-code search "auth middleware" ./src
246
71
  ```
247
72
 
248
- Use **MCP: List Servers** to restart after edits. Sub-agent: `miru install`.
73
+ ---
249
74
 
250
- ### Gemini CLI
75
+ ## MCP tools
251
76
 
252
- `~/.gemini/settings.json` — same `mcpServers` shape as Cursor. `gemini mcp add` or `/mcp` to verify. Prefer `miru install`.
77
+ When wired via `miru install`, your agent gets:
253
78
 
254
- ### Kiro
79
+ | Tool | What it does |
80
+ |------|----------------|
81
+ | `search` | Query by meaning; pass `repo` (project root or `https://` git URL) |
82
+ | `find_related` | Similar code at a `file_path` + line from a search hit |
255
83
 
256
- `~/.kiro/settings/mcp.json` — include `PATH` if Bun is not on the default path:
84
+ Pass the **project root** as `repo` for local projects.
257
85
 
258
- ```json
259
- "env": {
260
- "TAKARA_API_KEY": "your-takara-bearer-token",
261
- "PATH": "/usr/local/bin:/usr/bin:/bin"
262
- }
263
- ```
86
+ ## How it works
264
87
 
265
- Sub-agent: `miru install`.
88
+ Hybrid search: Takara embeddings + BM25 + fusion + reranking. Indexes **code**, **docs**, **config**, or **all** via `--content`.
266
89
 
267
- ### OpenCode
90
+ Disk cache: `~/Library/Caches/miru` (macOS), `~/.cache/miru` (Linux). MCP watches local files and updates incrementally; run `miru clear .` after big refactors when using CLI only.
268
91
 
269
- `opencode.json` uses `mcp`, `"type": "local"`, and `environment`:
92
+ ## CLI reference
270
93
 
271
- ```json
272
- {
273
- "$schema": "https://opencode.ai/config.json",
274
- "mcp": {
275
- "miru": {
276
- "type": "local",
277
- "command": ["bunx", "@takara-ai/miru-code"],
278
- "enabled": true,
279
- "environment": {
280
- "TAKARA_API_KEY": "your-takara-bearer-token"
281
- }
282
- }
283
- }
284
- }
285
- ```
286
-
287
- Verify with `opencode mcp list`. Sub-agent: `miru install`.
288
-
289
- ## CLI
94
+ | Command | Purpose |
95
+ |---------|---------|
96
+ | `miru setup` | Store API key |
97
+ | `miru install` | Configure IDE (global) |
98
+ | `miru uninstall` | Remove IDE config |
99
+ | `miru search <query> [path]` | Search (`-k N`, `--content`, `--json`) |
100
+ | `miru find-related <file> <line> [path]` | Related chunks |
101
+ | `miru init --agent <id>` | Project-local sub-agent |
102
+ | `miru clear [path]` | Drop index cache |
103
+ | `miru` | Start MCP server |
290
104
 
291
- Installed globally or via `bunx`:
105
+ ## Library
292
106
 
293
107
  ```bash
294
- miru setup
295
- miru install # interactive MCP + instructions + sub-agent (recommended)
296
- miru search "where are embeddings created" ./src -k 10 --content code
297
- miru find-related src/embeddings/openai.ts 120 ./src
298
- miru uninstall # remove miru config from agents
299
- miru clear ./src
300
- miru help search # per-command help
301
- miru -h # environment variables
108
+ bun add @takara-ai/miru-code
302
109
  ```
303
110
 
304
- From **this repo** (development), prefix with `bun run miru` instead of `miru`.
305
-
306
- | Command | Summary |
307
- |---------|---------|
308
- | `search <query> [path]` | Hybrid search; `path` defaults to cwd; accepts local path or `https://` git URL |
309
- | `find-related <file> <line> [path]` | Semantic neighbors of the chunk containing that line |
310
- | `setup` | Store and validate `TAKARA_API_KEY` locally |
311
- | `install` | Interactive global agent setup (MCP, instructions, sub-agent) |
312
- | `uninstall` | Remove miru configuration from agents |
313
- | `init --agent <id>` | Write project-local sub-agent markdown |
314
- | `clear [path]` | Delete disk cache for that source |
315
- | *(no command)* | Start MCP server |
316
-
317
- **Options:** `-k` / `--top-k N` (default 5), `--content code|docs|config|all` (default `code`). Multiple types: `--content code docs`.
318
-
319
- Output is JSON (`query` + `results` with `chunk`, `score`, `location`).
320
-
321
- ## Library
322
-
323
111
  ```ts
324
112
  import { MiruIndex } from "@takara-ai/miru-code";
325
113
 
326
- // Local directory
327
- const index = await MiruIndex.fromPath("./src", ["code"]);
328
-
329
- // Local path or https:// git URL
330
- const fromUrl = await MiruIndex.fromSource("https://github.com/org/repo", ["code"], undefined, "main");
331
-
332
- const results = await index.search({
333
- query: "BM25 tokenize",
334
- topK: 10, // default 10 in the library API
335
- filterPaths: ["src/index/bm25.ts"],
336
- rerank: true,
337
- });
338
-
339
- const related = await index.findRelated(results[0], 5);
340
- await index.saveToDefaultCache("./src");
114
+ const index = await MiruIndex.fromPath("./src");
115
+ const results = await index.search({ query: "BM25 tokenize", topK: 10 });
341
116
  ```
342
117
 
343
- Exports: `MiruIndex`, `clearCache`, `findIndexCachePath`, `resolveCacheFolder`, and chunk/result types.
344
-
345
- ## Environment variables
346
-
347
- | Variable | Default / notes |
348
- |----------|-----------------|
349
- | `TAKARA_API_KEY` | **Required** — Takara bearer token |
350
- | `MIRU_OPENAI_BASE_URL` | `https://infer.dev.takara.ai/v1` |
351
- | `MIRU_OPENAI_EMBEDDING_MODEL` | `ds1-potion-code-16m` |
352
- | `MIRU_EMBEDDING_DIMENSIONS` | `256` for potion (auto when unset) |
353
- | `MIRU_FLOAT_VECTORS` | unset = **int8** vectors; `1` = float32 |
354
- | `MIRU_EMBEDDING_BATCH_SIZE` | `32` |
355
- | `MIRU_CONCURRENCY` | logical CPUs **minus 2** (min 1); file IO, embedding batches, BM25 workers |
356
- | `MIRU_MCP_WATCH` | on; set `0` or `false` to disable MCP filesystem watch |
357
- | `MIRU_CREDENTIALS_DIR` | override stored-credentials directory |
358
- | `MIRU_PIPELINE_EMBED_BATCH` | optional pipeline tuning (see `.env.example`) |
359
- | `MIRU_PIPELINE_EMBED_INFLIGHT` | optional pipeline tuning |
118
+ ## Environment
360
119
 
361
- Parallelism covers chunking, concurrent embedding requests, overlapping query embed with BM25, and BM25 worker sharding on large indexes.
120
+ | Variable | Notes |
121
+ |----------|-------|
122
+ | `TAKARA_API_KEY` | Required |
123
+ | `MIRU_OPENAI_BASE_URL` | Default `https://infer.dev.takara.ai/v1` |
124
+ | `MIRU_MCP_WATCH` | Set `0` to disable MCP file watch |
125
+ | `NO_COLOR` | Disable CLI colors |
362
126
 
363
- ## Developing Miru
364
-
365
- ```bash
366
- bun install
367
- cp .env.example .env.local # add TAKARA_API_KEY
368
- bun test
369
- bun run typecheck
370
- ```
127
+ See `.env.example` for more.
371
128
 
372
- **Local MCP** — point at your `miru-code` clone:
129
+ ## Manual MCP (skip `miru install`)
373
130
 
374
131
  ```json
375
132
  {
376
- "command": "bun",
377
- "args": ["/absolute/path/to/miru-code/src/cli.ts"],
378
- "env": {
379
- "TAKARA_API_KEY": "your-takara-bearer-token"
133
+ "miru": {
134
+ "command": "miru",
135
+ "args": [],
136
+ "env": { "TAKARA_API_KEY": "your-token" }
380
137
  }
381
138
  }
382
139
  ```
383
140
 
384
- Merge into the IDE’s wrapper (`mcpServers`, `servers`, or `mcp`).
385
-
386
- ## Publishing
387
-
388
- Releases use **Bun** for install, test, and `bun pm pack`. Registry upload uses **`npm publish`** on that tarball because [Bun does not yet support npm OIDC trusted publishing](https://github.com/oven-sh/bun/issues/22423).
389
-
390
- **Local** (logged in to npm):
391
-
392
- ```bash
393
- bun publish
394
- ```
395
-
396
- Runs `prepublishOnly` (typecheck + tests). Do not add a `publish` script that calls `npm publish` — that would upload twice.
141
+ Use `bunx` + `@takara-ai/miru-code` if `miru` is not global. Wrapper key varies by IDE (`mcpServers`, `servers`, or `mcp`).
397
142
 
398
- **CI** (Trusted Publisher on npm):
143
+ ## Developing
399
144
 
400
145
  ```bash
401
- npm version patch
402
- git push && git push --tags
403
- gh release create v$(node -p "require('./package.json').version") --generate-notes
146
+ git clone https://github.com/takara-ai/miru-code.git && cd miru-code
147
+ bun install && cp .env.example .env.local
148
+ bun test && bun run typecheck
404
149
  ```
405
150
 
406
- `release.yml` publishes when the GitHub Release is published.
151
+ Local MCP: `"command": "bun", "args": ["/path/to/miru-code/src/cli.ts"]`
407
152
 
408
153
  ## License
409
154
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takara-ai/miru-code",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Miru (見る) — hybrid code search for agents. Bun, TypeScript, Takara embeddings.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/cli.ts CHANGED
@@ -133,7 +133,7 @@ async function runSearch(
133
133
  content: ContentType[],
134
134
  jsonFlag: boolean,
135
135
  ): Promise<void> {
136
- await ensureCredentials();
136
+ await ensureCredentials({ interactive: true });
137
137
 
138
138
  const index = await withSpinner("Indexing and searching", async () => {
139
139
  const built = await MiruIndex.fromSource(path, content);
@@ -153,7 +153,7 @@ async function runFindRelated(
153
153
  content: ContentType[],
154
154
  jsonFlag: boolean,
155
155
  ): Promise<void> {
156
- await ensureCredentials();
156
+ await ensureCredentials({ interactive: true });
157
157
 
158
158
  const { results, label } = await withSpinner("Finding related chunks", async () => {
159
159
  const built = await MiruIndex.fromSource(path, content);
@@ -2,6 +2,7 @@ import { mkdir, unlink, writeFile } from "node:fs/promises";
2
2
  import { dirname } from "node:path";
3
3
  import { loadAgentTemplate } from "../agents.ts";
4
4
  import { brandTitle, dim, divider, green, hint, success, writeStdout } from "../cli-ui.ts";
5
+ import { ensureCredentials } from "../setup.ts";
5
6
  import {
6
7
  AGENT_TARGETS,
7
8
  type AgentTarget,
@@ -183,6 +184,10 @@ export async function runInstaller(mode: InstallMode): Promise<void> {
183
184
  const install = mode === "install";
184
185
  requireInteractiveTerminal(`miru ${mode}`);
185
186
 
187
+ if (install) {
188
+ await ensureCredentials({ interactive: true });
189
+ }
190
+
186
191
  writeStdout("");
187
192
  writeStdout(`${brandTitle()}${install ? " installer" : " uninstaller"}`);
188
193
  divider();
package/src/setup.ts CHANGED
@@ -87,30 +87,45 @@ export async function runClearCredentials(): Promise<void> {
87
87
  info(`No stored API key at ${path}`);
88
88
  }
89
89
 
90
- export async function ensureCredentials(options?: { interactive?: boolean }): Promise<void> {
90
+ export function canPromptForCredentials(): boolean {
91
+ return Boolean(process.stdin.isTTY);
92
+ }
93
+
94
+ export function hasCredentials(): boolean {
91
95
  try {
92
96
  resolveEmbeddingApiKey();
93
- return;
97
+ return true;
94
98
  } catch {
95
- await loadStoredCredentials();
99
+ return false;
96
100
  }
101
+ }
97
102
 
98
- try {
99
- resolveEmbeddingApiKey();
103
+ async function refreshCredentialsFromStore(): Promise<void> {
104
+ await loadStoredCredentials();
105
+ }
106
+
107
+ export async function ensureCredentials(options?: { interactive?: boolean }): Promise<void> {
108
+ if (hasCredentials()) {
109
+ return;
110
+ }
111
+
112
+ await refreshCredentialsFromStore();
113
+ if (hasCredentials()) {
100
114
  return;
101
- } catch {
102
- // still missing
103
115
  }
104
116
 
105
- const interactive = options?.interactive ?? process.stdin.isTTY;
106
- if (interactive) {
117
+ const wantsPrompt = options?.interactive ?? true;
118
+ if (wantsPrompt && canPromptForCredentials()) {
119
+ writeStderr("");
120
+ info("No Takara API key found.");
121
+ hint("Miru needs one for embeddings — enter it below (same as `miru setup`).");
107
122
  await runSetup();
108
123
  resolveEmbeddingApiKey();
109
124
  return;
110
125
  }
111
126
 
112
127
  throw new Error(
113
- "Embedding API key required. Run `miru setup` in a terminal, or set TAKARA_API_KEY " +
128
+ "Takara API key required. Run `miru setup` in a terminal, or set TAKARA_API_KEY " +
114
129
  "in your MCP server env (Cursor mcp.json) or .env.local.",
115
130
  );
116
131
  }