@takara-ai/miru-code 1.6.0 → 1.7.3

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
@@ -57,6 +57,7 @@ Interactive TUI — **↑↓** move, **space** toggle, **a** all, **enter** conf
57
57
  | Cursor rules | Always-on `.cursor/rules/miru-code.mdc` (Cursor only) |
58
58
  | Search hooks *(experimental)* | Block built-in Grep/Glob and redirect to Miru MCP |
59
59
  | Caveman *(experimental)* | On-demand chat compression skill (`/caveman`) |
60
+ | STE writing *(experimental)* | On-demand clear technical English for docs (`/ste`) |
60
61
 
61
62
 
62
63
  Restart the IDE when done.
@@ -68,18 +69,19 @@ miru uninstall # remove miru config
68
69
  **Supported:** Cursor · Claude Code · Gemini CLI · Kiro · OpenCode · GitHub Copilot · Codex · VS Code · Visual Studio (Windows) · Windsurf / Devin Desktop
69
70
 
70
71
 
71
- | IDE | MCP | Instructions / rules | Hooks *(experimental)* | Caveman *(experimental)* |
72
- | -------------- | ------------------------------------- | ------------------------------- | ------------------------------------------------- | ------------------------------------- |
73
- | Cursor | `~/.cursor/mcp.json` | `~/.cursor/rules/miru-code.mdc` | `~/.cursor/hooks.json` | `~/.cursor/skills/caveman/SKILL.md` |
74
- | Claude Code | `~/.claude.json` | `~/.claude/CLAUDE.md` | `~/.claude/settings.json` | `~/.claude/skills/caveman/SKILL.md` |
75
- | Gemini CLI | `~/.gemini/settings.json` | `~/.gemini/GEMINI.md` | `~/.gemini/settings.json` (`BeforeTool`) | — |
76
- | Kiro | `~/.kiro/settings/mcp.json` | `~/.kiro/steering/miru.md` | `~/.kiro/settings/hooks.json` | — |
77
- | OpenCode | `~/.config/opencode/opencode.json(c)` | `~/.config/opencode/AGENTS.md` | `~/.config/opencode/plugins/miru-search-guard.ts` | — |
78
- | GitHub Copilot | `~/.copilot/mcp-config.json` | — | `~/.copilot/hooks/miru-search.json` | — |
79
- | Codex | `~/.codex/config.toml` | `~/.codex/AGENTS.md` | `~/.codex/hooks.json` | — |
80
- | VS Code | `…/Code/User/mcp.json` | — | `~/.copilot/hooks/miru-search.json` | — |
81
- | Visual Studio | `%USERPROFILE%\.mcp.json` | — | `~/.copilot/hooks/miru-search.json` | — |
82
- | Windsurf | — | — | `~/.codeium/windsurf/hooks.json` | — |
72
+ | IDE | MCP | Instructions / rules | Hooks *(experimental)* | Caveman *(experimental)* | STE *(experimental)* |
73
+ | -------------- | ------------------------------------- | ------------------------------- | ------------------------------------------------- | --------------------------------------------------- | -------------------------------------------- |
74
+ | Cursor | `~/.cursor/mcp.json` | `~/.cursor/rules/miru-code.mdc` | `~/.cursor/hooks.json` | `~/.agents/skills/caveman/SKILL.md` | `~/.agents/skills/ste/SKILL.md` |
75
+ | Claude Code | `~/.claude.json` | `~/.claude/CLAUDE.md` | `~/.claude/settings.json` | `~/.claude/skills/caveman/SKILL.md` | `~/.claude/skills/ste/SKILL.md` |
76
+ | Gemini CLI | `~/.gemini/settings.json` | `~/.gemini/GEMINI.md` | `~/.gemini/settings.json` (`BeforeTool`) | `~/.agents/skills/caveman/SKILL.md` | `~/.agents/skills/ste/SKILL.md` |
77
+ | Kiro | `~/.kiro/settings/mcp.json` | `~/.kiro/steering/miru.md` | `~/.kiro/settings/hooks.json` | `~/.kiro/skills/caveman/SKILL.md` | `~/.kiro/skills/ste/SKILL.md` |
78
+ | OpenCode | `$XDG_CONFIG_HOME/opencode/opencode.json(c)` *(else `~/.config/opencode/…`)* | `…/AGENTS.md` | `…/plugins/miru-search-guard.ts` | `~/.agents/skills/caveman/SKILL.md` | `~/.agents/skills/ste/SKILL.md` |
79
+ | GitHub Copilot | `~/.copilot/mcp-config.json` | — | `~/.copilot/hooks/miru-search.json` | `~/.agents/skills/caveman/SKILL.md` | `~/.agents/skills/ste/SKILL.md` |
80
+ | Codex | `~/.codex/config.toml` | `~/.codex/AGENTS.md` | `~/.codex/hooks.json` | `~/.agents/skills/caveman/SKILL.md` | `~/.agents/skills/ste/SKILL.md` |
81
+ | VS Code | `…/Code/User/mcp.json` | — | `~/.copilot/hooks/miru-search.json` | `~/.agents/skills/caveman/SKILL.md` | `~/.agents/skills/ste/SKILL.md` |
82
+ | Visual Studio | `%USERPROFILE%\.mcp.json` | — | `~/.copilot/hooks/miru-search.json` | `~/.agents/skills/caveman/SKILL.md` | `~/.agents/skills/ste/SKILL.md` |
83
+ | Windsurf | — | — | `~/.codeium/windsurf/hooks.json` | `~/.agents/skills/caveman/SKILL.md` | `~/.agents/skills/ste/SKILL.md` |
84
+
83
85
 
84
86
  ### Plugin packaging
85
87
 
@@ -97,7 +99,18 @@ Current limitation:
97
99
 
98
100
  ### Search hooks
99
101
 
100
- Sub-agent files are also written where supported (see `miru install` plan). Windsurf hooks only *(experimental)* — no MCP entry yet. Caveman is an on-demand Agent Skill (default off): invoke with `/caveman` or “talk like caveman”; stop with “normal mode”.
102
+ Sub-agent files are also written where supported (see `miru install` plan). Windsurf hooks only *(experimental)* — no MCP entry yet. Caveman is an on-demand Agent Skill (default off): invoke with `/caveman` or “talk like caveman”; stop with “normal mode”. Invocation UI varies by IDE (`/caveman`, `$caveman`, `@caveman`, etc.). Most IDEs share `~/.agents/skills/caveman/SKILL.md` (including Copilot / VS Code / Visual Studio); Claude Code and Kiro keep vendor-native skill dirs. Ownership is tracked on the shared path so uninstalling one IDE keeps the skill while another still owns it; selecting all owners removes it once. STE is an on-demand Agent Skill (default off): invoke with `/ste` or “de-slop this”; keep articles and complete sentences. Most IDEs share `~/.agents/skills/ste/` the same way (ownership via `miru-owners.json`); Claude Code and Kiro keep vendor-native STE dirs.
103
+
104
+ <details>
105
+ <summary>STE writing <em>(experimental)</em></summary>
106
+
107
+ STE helps write **clear technical English** for docs, runbooks, errors, and release notes (pragmatic ASD-STE100-inspired rules). Invoke with `/ste` or “de-slop this”. Keep articles and complete sentences — not telegraph-style omission.
108
+
109
+ **Not ASD-certified.** Full dictionary compliance needs the official standard at [asd-ste100.org](https://www.asd-ste100.org). Miru does not ship the copyrighted ASD dictionary.
110
+
111
+ Not for marketing or brand copy. Off by default; enable STE in the installer for any supported IDE. Restart the IDE (or reload skills) after install. For Codex, install also sets `[features] skills = true` in `~/.codex/config.toml` (same as Caveman).
112
+
113
+ </details>
101
114
 
102
115
  <details>
103
116
  <summary>Caveman mode <em>(experimental)</em></summary>
@@ -106,7 +119,7 @@ Caveman compresses **live chat replies** (less filler, max meaning). Intensities
106
119
 
107
120
  Security / destructive warnings use clear normal prose (auto-clarity) — brevity never hides risk. Session token savings vary; the skill itself costs input tokens. No guaranteed %.
108
121
 
109
- Off by default at install time. Enable Caveman in the installer for Cursor and/or Claude Code.
122
+ Off by default at install time. Enable Caveman in the installer for any supported IDE. Restart the IDE (or reload skills) after install. For Codex, the installer also sets `[features] skills = true` in `~/.codex/config.toml` (required for Codex to load skills).
110
123
 
111
124
  </details>
112
125
 
@@ -222,6 +235,8 @@ Set `MIRU_AST_CHUNKING=0` to disable AST and use structural → lines only.
222
235
 
223
236
  ## CLI reference
224
237
 
238
+ Run `miru -h` for the command list, or `miru <command> -h` for details.
239
+
225
240
  | Command | Purpose |
226
241
  | ----------------------------------------- | --------------------------------------------------------------------------------- |
227
242
  | `miru setup` | Authenticate and store credentials |
@@ -392,7 +407,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for pre-commit hooks, commit message conv
392
407
 
393
408
  Local MCP: `"command": "bun", "args": ["/path/to/miru-code/src/cli.ts"]`
394
409
 
395
- `miru help` / setup print a framed wordmark on color terminals (`MIRU_QUIET=1` for subtitle only). Crane art lives in `src/brand-banner.ts`; regenerate with `bun run scripts/render-crane-art.ts` (ImageMagick). The crane is a registered mark of Takara.ai Ltd.
410
+ `miru -h` / setup print a framed wordmark on color terminals (`MIRU_QUIET=1` for subtitle only). Crane art lives in `src/brand-banner.ts`; regenerate with `bun run scripts/render-crane-art.ts` (ImageMagick). The crane is a registered mark of Takara.ai Ltd.
396
411
 
397
412
  ## Codex plugin in this repo
398
413
 
@@ -404,6 +419,15 @@ This repo includes a repo-local Codex plugin:
404
419
 
405
420
  The plugin intentionally launches the published package with `bunx @takara-ai/miru-code` instead of the checked-out source tree, so local source edits here do not affect the Codex plugin until a new package version is published.
406
421
 
422
+ ## Credits
423
+
424
+ Miru uses work by [MinishLab](https://github.com/MinishLab). Thank you to its authors.
425
+
426
+ - **[semble](https://github.com/MinishLab/semble)** (MIT). Miru ports parts of semble. These parts include the file walker, the index layout, the BM25 tokenizer, the hybrid search pipeline, and the ranking signals.
427
+ - **[potion-code](https://huggingface.co/minishlab/potion-code-16M)** and **[Model2Vec](https://github.com/MinishLab/model2vec)** (MIT). The WordPiece tokenizer in `tokenizer/tokenizer.json` comes from potion-code.
428
+
429
+ See [NOTICE](./NOTICE) for the licence text and the Model2Vec citation.
430
+
407
431
  ## License
408
432
 
409
433
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takara-ai/miru-code",
3
- "version": "1.6.0",
3
+ "version": "1.7.3",
4
4
  "description": "Miru (見る) — hybrid code search for agents. Bun, TypeScript, Takara embeddings.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,4 +1,5 @@
1
1
  import { envFirstString } from "../env.ts";
2
+ import { CredentialsError } from "./errors.ts";
2
3
  import type { StoredDeviceCodeCredentials } from "./types.ts";
3
4
 
4
5
  const DEFAULT_AUTH_BASE_URL = "https://auth.takara.ai";
@@ -239,7 +240,7 @@ export async function refreshDeviceAuthorization(
239
240
  options?: { config?: DeviceAuthConfig; fetchImpl?: typeof fetch },
240
241
  ): Promise<DeviceAuthorizationTokens> {
241
242
  if (!credentials.refresh_token?.trim()) {
242
- throw new Error(
243
+ throw new CredentialsError(
243
244
  "Stored device credentials cannot be refreshed because no refresh token exists.",
244
245
  );
245
246
  }
@@ -256,12 +257,14 @@ export async function refreshDeviceAuthorization(
256
257
  const payload = text ? (JSON.parse(text) as OAuthTokenSuccess | OAuthTokenError) : {};
257
258
 
258
259
  if (!response.ok) {
259
- throw new Error(`Device token refresh failed: ${parseTokenError(payload as OAuthTokenError)}`);
260
+ throw new CredentialsError(
261
+ `Device token refresh failed: ${parseTokenError(payload as OAuthTokenError)}`,
262
+ );
260
263
  }
261
264
 
262
265
  const success = payload as OAuthTokenSuccess;
263
266
  if (!success.access_token?.trim()) {
264
- throw new Error("Token refresh succeeded but did not return an access token.");
267
+ throw new CredentialsError("Token refresh succeeded but did not return an access token.");
265
268
  }
266
269
 
267
270
  return normalizeTokenSuccess({
@@ -289,8 +292,13 @@ export function openBrowserForDeviceLogin(url: string): boolean {
289
292
  ? ["cmd", "/c", "start", "", url]
290
293
  : ["xdg-open", url];
291
294
  try {
292
- const proc = Bun.spawn(command, { stdout: "ignore", stderr: "ignore" });
293
- void proc.exited;
295
+ const proc = Bun.spawn(command, {
296
+ stdin: "ignore",
297
+ stdout: "ignore",
298
+ stderr: "ignore",
299
+ });
300
+ // Detach so the spinner's event loop / process lifecycle can't kill `open`.
301
+ proc.unref();
294
302
  return true;
295
303
  } catch {
296
304
  return false;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Signals that Miru has no usable credentials — missing, expired, or revoked.
3
+ * Callers that can recover (the MCP `auth` tool, `miru setup`) branch on this
4
+ * instead of matching error text.
5
+ */
6
+ export class CredentialsError extends Error {
7
+ constructor(message: string, options?: { cause?: unknown }) {
8
+ super(message, options);
9
+ this.name = "CredentialsError";
10
+ }
11
+ }
12
+
13
+ /** True when `error`, or anything in its `cause` chain, is a CredentialsError. */
14
+ export function isCredentialsError(error: unknown): boolean {
15
+ let current: unknown = error;
16
+ // Bounded so a self-referencing cause chain cannot spin forever.
17
+ for (let depth = 0; depth < 10 && current instanceof Error; depth++) {
18
+ if (current instanceof CredentialsError) {
19
+ return true;
20
+ }
21
+ current = current.cause;
22
+ }
23
+ return false;
24
+ }
@@ -1,4 +1,4 @@
1
- import { hint, info, warn, writeStderr } from "../cli-ui.ts";
1
+ import { dim, warn, writeStderr } from "../cli-ui.ts";
2
2
  import { validateEmbeddingApiKey } from "../embeddings/validate.ts";
3
3
  import { promptConfirm } from "../installer/prompt.ts";
4
4
  import { promptHidden } from "../prompt.ts";
@@ -35,16 +35,10 @@ async function resolveAuthMode(options: AuthenticateOptions): Promise<AuthMode>
35
35
  if (options.apiKey) {
36
36
  return "api_key";
37
37
  }
38
- if (options.device) {
38
+ if (options.device || options.interactive) {
39
39
  return "device_code";
40
40
  }
41
- if (!options.interactive) {
42
- throw new Error("Choose an auth mode with `miru setup --device` or `miru setup --key TOKEN`.");
43
- }
44
- writeStderr("");
45
- info("Choose how to authenticate.");
46
- const useDevice = await promptConfirm("Use device code login?", true);
47
- return useDevice ? "device_code" : "api_key";
41
+ throw new Error("Choose an auth mode with `miru setup --device` or `miru setup --key TOKEN`.");
48
42
  }
49
43
 
50
44
  async function authenticateWithApiKey(
@@ -67,24 +61,26 @@ async function authenticateWithApiKey(
67
61
  async function authenticateWithDeviceCode(
68
62
  options: AuthenticateOptions,
69
63
  ): Promise<AuthenticatedCredentials> {
70
- const start = await startDeviceAuthorization();
71
- const verificationUrl = start.verificationUriComplete ?? start.verificationUri;
72
64
  writeStderr("");
73
- hint(`Visit ${verificationUrl}`);
74
- if (!start.verificationUriComplete) {
75
- hint(`Code: ${start.userCode}`);
76
- }
77
-
78
- const shouldOpenBrowser =
79
- options.interactive &&
80
- (process.env.MIRU_OPEN_BROWSER === undefined || process.env.MIRU_OPEN_BROWSER === "1");
81
- if (shouldOpenBrowser) {
82
- openBrowserForDeviceLogin(verificationUrl);
83
- }
84
-
85
- const spinner = new Spinner("Waiting for authentication");
65
+ const spinner = new Spinner("Authenticating");
86
66
  spinner.start();
87
67
  try {
68
+ const start = await startDeviceAuthorization();
69
+ const verificationUrl = start.verificationUriComplete ?? start.verificationUri;
70
+
71
+ const shouldOpenBrowser =
72
+ options.interactive &&
73
+ (process.env.MIRU_OPEN_BROWSER === undefined || process.env.MIRU_OPEN_BROWSER === "1");
74
+ if (shouldOpenBrowser) {
75
+ openBrowserForDeviceLogin(verificationUrl);
76
+ }
77
+
78
+ let visit = dim(` Visit ${verificationUrl}`);
79
+ if (!start.verificationUriComplete) {
80
+ visit += `\n${dim(` Code: ${start.userCode}`)}`;
81
+ }
82
+ spinner.follow(visit);
83
+
88
84
  const tokens = await pollDeviceAuthorization(start);
89
85
  spinner.succeed("");
90
86
  return {
package/src/cli.ts CHANGED
@@ -21,8 +21,9 @@ import {
21
21
  AGENT_IDS,
22
22
  formatUnknownAgent,
23
23
  printCommandHelp,
24
- printFullHelp,
24
+ printEnvironmentHelp,
25
25
  printMainHelp,
26
+ printSageMakerHelp,
26
27
  } from "./help.ts";
27
28
  import { getBenchmarkModeStatus, setBenchmarkMode } from "./installer/benchmark-mode.ts";
28
29
  import { runSearchGuardFromStdin } from "./installer/hooks/search-guard.ts";
@@ -81,6 +82,8 @@ const CLI_COMMANDS = new Set([
81
82
  "setup",
82
83
  "clear",
83
84
  "benchmark",
85
+ "env",
86
+ "environment",
84
87
  "hook-guard",
85
88
  "help",
86
89
  "-h",
@@ -415,7 +418,7 @@ async function runCli(argv: string[]): Promise<void> {
415
418
  }
416
419
 
417
420
  if (command === "-h" || command === "--help") {
418
- printFullHelp();
421
+ printMainHelp();
419
422
  return;
420
423
  }
421
424
 
@@ -426,7 +429,7 @@ async function runCli(argv: string[]): Promise<void> {
426
429
 
427
430
  if (command === "help") {
428
431
  const topic = rest[0];
429
- if (!topic) {
432
+ if (!topic || topic === "-h" || topic === "--help") {
430
433
  printMainHelp();
431
434
  return;
432
435
  }
@@ -434,6 +437,25 @@ async function runCli(argv: string[]): Promise<void> {
434
437
  return;
435
438
  }
436
439
 
440
+ if (command === "setup" && (rest.includes("-h") || rest.includes("--help"))) {
441
+ if (rest.includes("--sagemaker")) {
442
+ printSageMakerHelp();
443
+ } else {
444
+ printCommandHelp("setup");
445
+ }
446
+ return;
447
+ }
448
+
449
+ if (command === "env" || command === "environment") {
450
+ printEnvironmentHelp();
451
+ return;
452
+ }
453
+
454
+ if (rest[0] === "-h" || rest[0] === "--help") {
455
+ printCommandHelp(command);
456
+ return;
457
+ }
458
+
437
459
  if (command === "hook-guard") {
438
460
  process.exit(await runSearchGuardFromStdin());
439
461
  }
package/src/env.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { CredentialsError } from "./auth/errors.ts";
2
+
1
3
  export function envInt(name: string, fallback: number, min = 1): number {
2
4
  const raw = process.env[name];
3
5
  if (!raw) {
@@ -59,7 +61,7 @@ export function hasTakaraApiKeyInEnv(): boolean {
59
61
  export function resolveEmbeddingApiKey(): string {
60
62
  const key = process.env[TAKARA_API_KEY_ENV]?.trim() ?? "";
61
63
  if (!isUsableTakaraApiKey(key)) {
62
- throw new Error(
64
+ throw new CredentialsError(
63
65
  "Takara credentials required. If you're an agent with Miru MCP tools available, call the " +
64
66
  "`auth` tool to sign in. Otherwise run `miru setup`, or set TAKARA_API_KEY in your MCP " +
65
67
  "server env or .env.local.",
package/src/help.ts CHANGED
@@ -37,24 +37,23 @@ export function printMainHelp(): void {
37
37
  commandRow("locate", "Exact substring location in the index");
38
38
  commandRow("expand", "Adjacent chunks in the same file as a hit");
39
39
  commandRow("find-related", "Find chunks related to a file:line");
40
- commandRow("setup", "Authenticate with Takara and save local credentials");
40
+ commandRow("setup", "Authenticate with Takara");
41
41
  commandRow("install", "Configure miru across coding agents");
42
42
  commandRow("uninstall", "Remove miru agent configuration");
43
43
  commandRow("benchmark", "Turn MCP benchmark mode on or off");
44
44
  commandRow("init", "Write a project-local sub-agent file");
45
45
  commandRow("clear", "Remove cached index for a path");
46
- commandRow("help", "Show help for a command");
47
46
  divider();
48
47
 
49
48
  section("Quick start");
50
49
  writeStdout(" miru setup && miru install");
51
50
  writeStdout(' miru search "auth middleware" ./src');
52
51
  writeStdout("");
53
- hint("miru help <command> · miru -h for environment variables · miru -v for version");
52
+ hint("miru <command> -h · miru -v for version");
54
53
  writeStdout("");
55
54
  }
56
55
 
57
- export function printEnvHelp(): void {
56
+ export function printEnvironmentHelp(): void {
58
57
  section("Environment");
59
58
  writeStdout(` ${TAKARA_API_KEY_ENV}`);
60
59
  writeStdout(" Takara bearer token for embeddings");
@@ -67,6 +66,9 @@ export function printEnvHelp(): void {
67
66
  writeStdout(" MIRU_TOKENIZER_JSON");
68
67
  writeStdout(" Path to tokenizer.json (default: <package>/tokenizer/tokenizer.json)");
69
68
  writeStdout("");
69
+ }
70
+
71
+ export function printSageMakerHelp(): void {
70
72
  section("Self-hosted (AWS SageMaker)");
71
73
  writeStdout(" MIRU_SAGEMAKER_ENDPOINT_ARN");
72
74
  writeStdout(" arn:aws:sagemaker:<region>:<account-id>:endpoint/<name> — set this to");
@@ -90,13 +92,17 @@ export function printEnvHelp(): void {
90
92
  writeStdout("");
91
93
  }
92
94
 
93
- export function printFullHelp(): void {
94
- printMainHelp();
95
- printEnvHelp();
95
+ export function printEnvHelp(): void {
96
+ printEnvironmentHelp();
97
+ printSageMakerHelp();
96
98
  }
97
99
 
98
100
  export function printCommandHelp(command: string): void {
99
101
  switch (command) {
102
+ case "env":
103
+ case "environment":
104
+ printEnvironmentHelp();
105
+ return;
100
106
  case "search":
101
107
  commandHeader("search", "Hybrid semantic + keyword search.");
102
108
  section("Usage");
@@ -172,17 +178,23 @@ export function printCommandHelp(command: string): void {
172
178
  case "install":
173
179
  commandHeader("install", "Interactive global agent setup.");
174
180
  writeStdout("Configures MCP server, instructions, sub-agent files, and optional");
175
- writeStdout("experimental integrations (search hooks, Caveman skill) under");
181
+ writeStdout("experimental integrations (search hooks, Caveman, STE writing) under");
176
182
  writeStdout("your user config (~/.claude, ~/.cursor, etc.).");
177
183
  writeStdout("");
178
- writeStdout("Caveman = on-demand chat compression (/caveman). Off by default.");
184
+ writeStdout(
185
+ "Caveman = chat compression (/caveman). STE = clear docs (/ste). Off by default.",
186
+ );
187
+ writeStdout("Caveman is supported on all Miru installer IDEs (shared path for most IDEs).");
188
+ writeStdout(
189
+ "STE is supported on all Miru installer IDEs (same shared-path ownership model).",
190
+ );
179
191
  writeStdout("Run miru setup first, or set TAKARA_API_KEY for MCP env expansion.");
180
192
  writeStdout("");
181
193
  return;
182
194
  case "uninstall":
183
195
  commandHeader("uninstall", "Remove miru configuration from agents.");
184
196
  writeStdout("Removes MCP entries, marked instruction blocks, global sub-agents,");
185
- writeStdout("and selected optional integrations (hooks, Caveman skill).");
197
+ writeStdout("and selected optional integrations (hooks, Caveman, STE skills).");
186
198
  writeStdout("Also deletes the global benchmark report from Miru's state directory.");
187
199
  writeStdout("");
188
200
  return;