@takara-ai/miru-code 0.3.0 → 0.4.0
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 +203 -164
- package/package.json +1 -1
- package/src/agents/claude.md +1 -1
- package/src/agents/copilot.md +1 -1
- package/src/agents/cursor.md +1 -1
- package/src/agents/gemini.md +1 -1
- package/src/agents/kiro.md +1 -1
- package/src/agents/opencode.md +1 -1
- package/src/cli-ui.ts +189 -0
- package/src/cli.ts +110 -33
- package/src/help.ts +115 -130
- package/src/installer/agents.ts +246 -0
- package/src/installer/config.ts +273 -0
- package/src/installer/installer.ts +256 -0
- package/src/installer/prompt.ts +77 -0
- package/src/setup.ts +17 -20
- package/src/spinner.ts +10 -4
package/src/help.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { AgentId } from "./agents.ts";
|
|
2
|
+
import {
|
|
3
|
+
brandTitle,
|
|
4
|
+
commandRow,
|
|
5
|
+
divider,
|
|
6
|
+
fail,
|
|
7
|
+
header,
|
|
8
|
+
hint,
|
|
9
|
+
section,
|
|
10
|
+
writeStdout,
|
|
11
|
+
} from "./cli-ui.ts";
|
|
2
12
|
|
|
3
13
|
export const AGENT_IDS: readonly AgentId[] = [
|
|
4
14
|
"claude",
|
|
@@ -12,44 +22,41 @@ export const AGENT_IDS: readonly AgentId[] = [
|
|
|
12
22
|
const AGENT_LIST = AGENT_IDS.join(", ");
|
|
13
23
|
|
|
14
24
|
export function printMainHelp(): void {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
process.stdout.write(`${lines.join("\n")}\n`);
|
|
25
|
+
header("hybrid code search for agents");
|
|
26
|
+
|
|
27
|
+
section("Usage");
|
|
28
|
+
writeStdout(" miru Start MCP server (stdio)");
|
|
29
|
+
writeStdout(" miru <command> [options]");
|
|
30
|
+
divider();
|
|
31
|
+
|
|
32
|
+
section("Commands");
|
|
33
|
+
commandRow("search", "Hybrid search over a codebase");
|
|
34
|
+
commandRow("find-related", "Find chunks related to a file:line");
|
|
35
|
+
commandRow("setup", "Save your Takara API key locally");
|
|
36
|
+
commandRow("install", "Configure miru across coding agents");
|
|
37
|
+
commandRow("uninstall", "Remove miru agent configuration");
|
|
38
|
+
commandRow("init", "Write a project-local sub-agent file");
|
|
39
|
+
commandRow("clear", "Remove cached index for a path");
|
|
40
|
+
commandRow("help", "Show help for a command");
|
|
41
|
+
divider();
|
|
42
|
+
|
|
43
|
+
section("Quick start");
|
|
44
|
+
writeStdout(" miru setup && miru install");
|
|
45
|
+
writeStdout(' miru search "auth middleware" ./src');
|
|
46
|
+
writeStdout("");
|
|
47
|
+
hint("miru help <command> · miru -h for environment variables");
|
|
48
|
+
writeStdout("");
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
export function printEnvHelp(): void {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
];
|
|
52
|
-
process.stdout.write(`${lines.join("\n")}\n`);
|
|
52
|
+
section("Environment");
|
|
53
|
+
writeStdout(" TAKARA_API_KEY");
|
|
54
|
+
writeStdout(" Takara bearer token for embeddings");
|
|
55
|
+
writeStdout(" MIRU_OPENAI_BASE_URL");
|
|
56
|
+
writeStdout(" Default: https://infer.dev.takara.ai/v1");
|
|
57
|
+
writeStdout(" MIRU_CONCURRENCY");
|
|
58
|
+
writeStdout(" Parallel workers (default: CPUs − 2)");
|
|
59
|
+
writeStdout("");
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
export function printFullHelp(): void {
|
|
@@ -57,117 +64,95 @@ export function printFullHelp(): void {
|
|
|
57
64
|
printEnvHelp();
|
|
58
65
|
}
|
|
59
66
|
|
|
67
|
+
function printCommandHeader(name: string, summary: string): void {
|
|
68
|
+
writeStdout("");
|
|
69
|
+
writeStdout(`${brandTitle()} ${name}`);
|
|
70
|
+
writeStdout(summary);
|
|
71
|
+
divider();
|
|
72
|
+
}
|
|
73
|
+
|
|
60
74
|
export function printCommandHelp(command: string): void {
|
|
61
75
|
switch (command) {
|
|
62
76
|
case "search":
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
" --content TYPE code | docs | config | all (default: code)",
|
|
74
|
-
"",
|
|
75
|
-
"Example:",
|
|
76
|
-
' miru search "where is auth" ./src -k 10 --content code docs',
|
|
77
|
-
"",
|
|
78
|
-
].join("\n"),
|
|
79
|
-
);
|
|
77
|
+
printCommandHeader("search", "Hybrid semantic + keyword search.");
|
|
78
|
+
section("Usage");
|
|
79
|
+
writeStdout(" miru search <query> [path] [options]");
|
|
80
|
+
section("Options");
|
|
81
|
+
writeStdout(" -k, --top-k N Number of results (default: 5)");
|
|
82
|
+
writeStdout(" --content TYPE code | docs | config | all");
|
|
83
|
+
writeStdout(" --json JSON output (default when piped)");
|
|
84
|
+
section("Example");
|
|
85
|
+
writeStdout(' miru search "where is auth" ./src -k 10 --content code docs');
|
|
86
|
+
writeStdout("");
|
|
80
87
|
return;
|
|
81
88
|
case "find-related":
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
" miru find-related <file> <line> [path] [-k N] [--content TYPE ...]",
|
|
89
|
-
"",
|
|
90
|
-
"Example:",
|
|
91
|
-
" miru find-related src/auth.ts 42 . -k 8",
|
|
92
|
-
"",
|
|
93
|
-
].join("\n"),
|
|
94
|
-
);
|
|
89
|
+
printCommandHeader("find-related", "Semantic neighbors of a file location.");
|
|
90
|
+
section("Usage");
|
|
91
|
+
writeStdout(" miru find-related <file> <line> [path] [options]");
|
|
92
|
+
section("Example");
|
|
93
|
+
writeStdout(" miru find-related src/auth.ts 42 . -k 8");
|
|
94
|
+
writeStdout("");
|
|
95
95
|
return;
|
|
96
96
|
case "setup":
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
97
|
+
printCommandHeader("setup", "Store and validate your Takara API key.");
|
|
98
|
+
section("Usage");
|
|
99
|
+
writeStdout(" miru setup [--key TOKEN] [--force] [--clear]");
|
|
100
|
+
section("Options");
|
|
101
|
+
writeStdout(" --key, -k TOKEN Non-interactive key entry");
|
|
102
|
+
writeStdout(" --force Replace an existing stored key");
|
|
103
|
+
writeStdout(" --clear Remove stored credentials");
|
|
104
|
+
writeStdout("");
|
|
105
|
+
return;
|
|
106
|
+
case "install":
|
|
107
|
+
printCommandHeader("install", "Interactive global agent setup.");
|
|
108
|
+
writeStdout("Configures MCP server, instructions, and sub-agent files under");
|
|
109
|
+
writeStdout("your user config (~/.claude, ~/.cursor, etc.).");
|
|
110
|
+
writeStdout("");
|
|
111
|
+
writeStdout("Run miru setup first, or set TAKARA_API_KEY for MCP env expansion.");
|
|
112
|
+
writeStdout("");
|
|
113
|
+
return;
|
|
114
|
+
case "uninstall":
|
|
115
|
+
printCommandHeader("uninstall", "Remove miru configuration from agents.");
|
|
116
|
+
writeStdout("Removes MCP entries, marked instruction blocks, and global sub-agents.");
|
|
117
|
+
writeStdout("");
|
|
115
118
|
return;
|
|
116
119
|
case "init":
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
` ${AGENT_LIST}`,
|
|
127
|
-
"",
|
|
128
|
-
"Examples:",
|
|
129
|
-
" miru init --agent cursor",
|
|
130
|
-
" miru init --agent claude --force",
|
|
131
|
-
"",
|
|
132
|
-
].join("\n"),
|
|
133
|
-
);
|
|
120
|
+
printCommandHeader("init", "Project-local sub-agent file.");
|
|
121
|
+
writeStdout("Prefer miru install for global setup. Use init to commit into a repo.");
|
|
122
|
+
section("Usage");
|
|
123
|
+
writeStdout(" miru init --agent AGENT [--force]");
|
|
124
|
+
section("Agents");
|
|
125
|
+
writeStdout(` ${AGENT_LIST}`);
|
|
126
|
+
section("Example");
|
|
127
|
+
writeStdout(" miru init --agent claude --force");
|
|
128
|
+
writeStdout("");
|
|
134
129
|
return;
|
|
135
130
|
case "clear":
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
" miru clear [path]",
|
|
143
|
-
"",
|
|
144
|
-
"Example:",
|
|
145
|
-
" miru clear .",
|
|
146
|
-
"",
|
|
147
|
-
].join("\n"),
|
|
148
|
-
);
|
|
131
|
+
printCommandHeader("clear", "Drop the on-disk index cache.");
|
|
132
|
+
section("Usage");
|
|
133
|
+
writeStdout(" miru clear [path]");
|
|
134
|
+
section("Example");
|
|
135
|
+
writeStdout(" miru clear .");
|
|
136
|
+
writeStdout("");
|
|
149
137
|
return;
|
|
150
138
|
case "mcp":
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
" miru [--ref BRANCH] [--content TYPE ...]",
|
|
158
|
-
"",
|
|
159
|
-
"Indexes a repo on the first search/find_related tool call (repo argument).",
|
|
160
|
-
"With no path, uses the process working directory.",
|
|
161
|
-
"",
|
|
162
|
-
].join("\n"),
|
|
163
|
-
);
|
|
139
|
+
printCommandHeader("mcp", "Stdio MCP server (default with no subcommand).");
|
|
140
|
+
section("Usage");
|
|
141
|
+
writeStdout(" miru [--ref BRANCH] [--content TYPE ...]");
|
|
142
|
+
writeStdout("");
|
|
143
|
+
writeStdout("Indexes on the first search/find_related tool call (repo argument).");
|
|
144
|
+
writeStdout("");
|
|
164
145
|
return;
|
|
165
146
|
default:
|
|
166
|
-
|
|
167
|
-
printMainHelp();
|
|
168
|
-
process.exit(1);
|
|
147
|
+
failHelp(command);
|
|
169
148
|
}
|
|
170
|
-
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function failHelp(command: string): never {
|
|
152
|
+
fail(`Unknown command: ${command}`);
|
|
153
|
+
writeStdout("");
|
|
154
|
+
printMainHelp();
|
|
155
|
+
process.exit(1);
|
|
171
156
|
}
|
|
172
157
|
|
|
173
158
|
export function formatUnknownAgent(agent: string): string {
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { AgentId } from "../agents.ts";
|
|
5
|
+
|
|
6
|
+
export type InstallAction =
|
|
7
|
+
| "created"
|
|
8
|
+
| "updated"
|
|
9
|
+
| "unchanged"
|
|
10
|
+
| "not-found"
|
|
11
|
+
| "removed"
|
|
12
|
+
| "error"
|
|
13
|
+
| "skipped";
|
|
14
|
+
|
|
15
|
+
export type InstallMode = "install" | "uninstall";
|
|
16
|
+
|
|
17
|
+
export const MIRU_START = "<!-- miru:start -->";
|
|
18
|
+
export const MIRU_END = "<!-- miru:end -->";
|
|
19
|
+
|
|
20
|
+
const HOME = homedir();
|
|
21
|
+
|
|
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
|
+
const STDIO_SERVER_CONFIG: Record<string, unknown> = {
|
|
29
|
+
command: "bunx",
|
|
30
|
+
args: ["@takara-ai/miru-code"],
|
|
31
|
+
type: "stdio",
|
|
32
|
+
env: MCP_ENV,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const BARE_STDIO_SERVER_CONFIG: Record<string, unknown> = {
|
|
36
|
+
command: "bunx",
|
|
37
|
+
args: ["@takara-ai/miru-code"],
|
|
38
|
+
env: MCP_ENV,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const OPENCODE_SERVER_CONFIG: Record<string, unknown> = {
|
|
42
|
+
command: ["bunx", "@takara-ai/miru-code"],
|
|
43
|
+
type: "local",
|
|
44
|
+
enabled: true,
|
|
45
|
+
environment: MCP_ENV,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const INSTRUCTIONS = `${MIRU_START}
|
|
49
|
+
## Miru Code Search
|
|
50
|
+
|
|
51
|
+
A \`miru\` MCP server is available with two tools:
|
|
52
|
+
- \`search\` — search the codebase with a natural-language or code query.
|
|
53
|
+
- \`find_related\` — find code similar to a specific file and line.
|
|
54
|
+
|
|
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
|
+
|
|
57
|
+
Set \`TAKARA_API_KEY\` in your MCP config (or run \`miru setup\` for CLI use). MCP does not inherit shell env reliably.
|
|
58
|
+
|
|
59
|
+
Pass \`--content docs\` to search documentation, \`--content config\` for config files, or \`--content all\` for everything.
|
|
60
|
+
|
|
61
|
+
For CLI fallback or sub-agents without MCP access:
|
|
62
|
+
|
|
63
|
+
\`\`\`bash
|
|
64
|
+
miru search "authentication flow" ./my-project
|
|
65
|
+
miru search "deployment guide" ./my-project --content docs
|
|
66
|
+
miru find-related src/auth.ts 42 ./my-project
|
|
67
|
+
miru search "save model to disk" ./my-project --top-k 10
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
First run builds a disk cache. The MCP server updates local indexes while it runs; after CLI-only use or large refactors, run \`miru clear <path>\`.
|
|
71
|
+
|
|
72
|
+
If \`miru\` is not on \`$PATH\`, use \`bunx @takara-ai/miru-code\` in its place.
|
|
73
|
+
|
|
74
|
+
### Workflow
|
|
75
|
+
|
|
76
|
+
1. Start with \`search\` (MCP) or \`miru search\` (CLI) to find relevant chunks.
|
|
77
|
+
2. Use \`--content docs\` / \`--content config\` / \`--content all\` when appropriate.
|
|
78
|
+
3. Inspect full files only when the chunk is not enough.
|
|
79
|
+
4. Optionally use \`find_related\` with a hit's \`file_path\` and \`line\`.
|
|
80
|
+
5. Use Grep only for exhaustive literal matches.
|
|
81
|
+
${MIRU_END}
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
export type McpConfigFormat = "json" | "toml";
|
|
85
|
+
|
|
86
|
+
export interface McpConfig {
|
|
87
|
+
path: string;
|
|
88
|
+
key: string;
|
|
89
|
+
memberKey: string;
|
|
90
|
+
entry: Record<string, unknown>;
|
|
91
|
+
format: McpConfigFormat;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface AgentTarget {
|
|
95
|
+
id: AgentId | "codex" | "vscode";
|
|
96
|
+
displayName: string;
|
|
97
|
+
binary: string | null;
|
|
98
|
+
configDir: string | null;
|
|
99
|
+
mcp: McpConfig | null;
|
|
100
|
+
instructionsPath: string | null;
|
|
101
|
+
subagentPath: string | null;
|
|
102
|
+
subagentId: AgentId | null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function opencodeMcpPath(): string {
|
|
106
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
107
|
+
const base = xdg ? join(xdg, "opencode") : join(HOME, ".config", "opencode");
|
|
108
|
+
const jsonc = join(base, "opencode.jsonc");
|
|
109
|
+
const json = join(base, "opencode.json");
|
|
110
|
+
if (existsSync(jsonc)) {
|
|
111
|
+
return jsonc;
|
|
112
|
+
}
|
|
113
|
+
if (existsSync(json)) {
|
|
114
|
+
return json;
|
|
115
|
+
}
|
|
116
|
+
return jsonc;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function vscodeMcpPath(): string {
|
|
120
|
+
if (process.platform === "darwin") {
|
|
121
|
+
return join(HOME, "Library", "Application Support", "Code", "User", "mcp.json");
|
|
122
|
+
}
|
|
123
|
+
if (process.platform === "win32") {
|
|
124
|
+
const appData = process.env.APPDATA ?? HOME;
|
|
125
|
+
return join(appData, "Code", "User", "mcp.json");
|
|
126
|
+
}
|
|
127
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? join(HOME, ".config");
|
|
128
|
+
return join(xdg, "Code", "User", "mcp.json");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function jsonMcp(path: string, key: string, entry: Record<string, unknown>): McpConfig {
|
|
132
|
+
return { path, key, memberKey: "miru", entry, format: "json" };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export const AGENT_TARGETS: AgentTarget[] = [
|
|
136
|
+
{
|
|
137
|
+
id: "claude",
|
|
138
|
+
displayName: "Claude Code",
|
|
139
|
+
binary: "claude",
|
|
140
|
+
configDir: join(HOME, ".claude"),
|
|
141
|
+
mcp: jsonMcp(join(HOME, ".claude.json"), "mcpServers", STDIO_SERVER_CONFIG),
|
|
142
|
+
instructionsPath: join(HOME, ".claude", "CLAUDE.md"),
|
|
143
|
+
subagentPath: join(HOME, ".claude", "agents", "miru-code.md"),
|
|
144
|
+
subagentId: "claude",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: "cursor",
|
|
148
|
+
displayName: "Cursor",
|
|
149
|
+
binary: "cursor",
|
|
150
|
+
configDir: join(HOME, ".cursor"),
|
|
151
|
+
mcp: jsonMcp(join(HOME, ".cursor", "mcp.json"), "mcpServers", STDIO_SERVER_CONFIG),
|
|
152
|
+
instructionsPath: null,
|
|
153
|
+
subagentPath: join(HOME, ".cursor", "agents", "miru-code.md"),
|
|
154
|
+
subagentId: "cursor",
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "gemini",
|
|
158
|
+
displayName: "Gemini CLI",
|
|
159
|
+
binary: "gemini",
|
|
160
|
+
configDir: join(HOME, ".gemini"),
|
|
161
|
+
mcp: jsonMcp(join(HOME, ".gemini", "settings.json"), "mcpServers", STDIO_SERVER_CONFIG),
|
|
162
|
+
instructionsPath: join(HOME, ".gemini", "GEMINI.md"),
|
|
163
|
+
subagentPath: join(HOME, ".gemini", "agents", "miru-code.md"),
|
|
164
|
+
subagentId: "gemini",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: "kiro",
|
|
168
|
+
displayName: "Kiro",
|
|
169
|
+
binary: "kiro",
|
|
170
|
+
configDir: join(HOME, ".kiro"),
|
|
171
|
+
mcp: jsonMcp(join(HOME, ".kiro", "settings", "mcp.json"), "mcpServers", {
|
|
172
|
+
...STDIO_SERVER_CONFIG,
|
|
173
|
+
env: {
|
|
174
|
+
...MCP_ENV,
|
|
175
|
+
PATH: "/usr/local/bin:/usr/bin:/bin",
|
|
176
|
+
},
|
|
177
|
+
}),
|
|
178
|
+
instructionsPath: join(HOME, ".kiro", "steering", "miru.md"),
|
|
179
|
+
subagentPath: join(HOME, ".kiro", "agents", "miru-code.md"),
|
|
180
|
+
subagentId: "kiro",
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: "opencode",
|
|
184
|
+
displayName: "OpenCode",
|
|
185
|
+
binary: "opencode",
|
|
186
|
+
configDir: join(HOME, ".config", "opencode"),
|
|
187
|
+
mcp: jsonMcp(opencodeMcpPath(), "mcp", OPENCODE_SERVER_CONFIG),
|
|
188
|
+
instructionsPath: join(HOME, ".config", "opencode", "AGENTS.md"),
|
|
189
|
+
subagentPath: join(HOME, ".config", "opencode", "agents", "miru-code.md"),
|
|
190
|
+
subagentId: "opencode",
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: "copilot",
|
|
194
|
+
displayName: "GitHub Copilot",
|
|
195
|
+
binary: null,
|
|
196
|
+
configDir: join(HOME, ".config", "github-copilot"),
|
|
197
|
+
mcp: jsonMcp(join(HOME, ".copilot", "mcp-config.json"), "mcpServers", BARE_STDIO_SERVER_CONFIG),
|
|
198
|
+
instructionsPath: null,
|
|
199
|
+
subagentPath: join(HOME, ".copilot", "agents", "miru-code.agent.md"),
|
|
200
|
+
subagentId: "copilot",
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: "codex",
|
|
204
|
+
displayName: "Codex",
|
|
205
|
+
binary: "codex",
|
|
206
|
+
configDir: join(HOME, ".codex"),
|
|
207
|
+
mcp: {
|
|
208
|
+
path: join(HOME, ".codex", "config.toml"),
|
|
209
|
+
key: "mcp_servers",
|
|
210
|
+
memberKey: "miru",
|
|
211
|
+
entry: {},
|
|
212
|
+
format: "toml",
|
|
213
|
+
},
|
|
214
|
+
instructionsPath: join(HOME, ".codex", "AGENTS.md"),
|
|
215
|
+
subagentPath: null,
|
|
216
|
+
subagentId: null,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: "vscode",
|
|
220
|
+
displayName: "VS Code",
|
|
221
|
+
binary: "code",
|
|
222
|
+
configDir: null,
|
|
223
|
+
mcp: jsonMcp(vscodeMcpPath(), "servers", STDIO_SERVER_CONFIG),
|
|
224
|
+
instructionsPath: null,
|
|
225
|
+
subagentPath: null,
|
|
226
|
+
subagentId: null,
|
|
227
|
+
},
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
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
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (agent.configDir) {
|
|
243
|
+
return Bun.file(agent.configDir).exists();
|
|
244
|
+
}
|
|
245
|
+
return false;
|
|
246
|
+
}
|