@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 +39 -15
- package/package.json +1 -1
- package/src/auth/device.ts +13 -5
- package/src/auth/errors.ts +24 -0
- package/src/auth/providers.ts +20 -24
- package/src/cli.ts +25 -3
- package/src/env.ts +3 -1
- package/src/help.ts +22 -10
- package/src/installer/agents.ts +116 -21
- package/src/installer/caveman-shared.ts +51 -0
- package/src/installer/config.ts +73 -0
- package/src/installer/copilot-family.ts +8 -0
- package/src/installer/hooks/install.ts +3 -14
- package/src/installer/installer.ts +279 -18
- package/src/installer/prompt.ts +15 -8
- package/src/installer/search-policy.ts +2 -1
- package/src/installer/shared-skill.ts +200 -0
- package/src/installer/style-packs/ste/skill.ts +218 -0
- package/src/mcp/auth-tool.ts +58 -28
- package/src/mcp/index-cache.ts +2 -1
- package/src/mcp/server.ts +5 -5
- package/src/spinner.ts +33 -6
package/src/installer/agents.ts
CHANGED
|
@@ -9,6 +9,52 @@ function copilotHooksPath(home: string): string {
|
|
|
9
9
|
return join(home, ".copilot", "hooks", "miru-search.json");
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/** Shared Copilot / VS Code / Visual Studio config root (MCP, hooks, agents). */
|
|
13
|
+
export function copilotHomeDir(home: string): string {
|
|
14
|
+
return join(home, ".copilot");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Vendors that do not scan `~/.agents/skills/` and need a native skill dir
|
|
19
|
+
* (Caveman + STE).
|
|
20
|
+
*/
|
|
21
|
+
export type NativeSkillVendor = "claude" | "kiro";
|
|
22
|
+
|
|
23
|
+
/** @deprecated Use NativeSkillVendor. */
|
|
24
|
+
export type NativeCavemanVendor = NativeSkillVendor;
|
|
25
|
+
|
|
26
|
+
function skillMd(home: string, rootDir: string, skillName: string): string {
|
|
27
|
+
return join(home, rootDir, "skills", skillName, "SKILL.md");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function skillDir(home: string, rootDir: string, skillName: string): string {
|
|
31
|
+
return join(home, rootDir, "skills", skillName);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Cross-agent Agent Skills root (`~/.agents/skills/…`).
|
|
36
|
+
* Used by Cursor, Gemini, OpenCode, Codex, Windsurf, and Copilot-family IDEs.
|
|
37
|
+
* Claude Code and Kiro still use vendor-native skill dirs.
|
|
38
|
+
*/
|
|
39
|
+
export function agentsCavemanSkillPath(home: string): string {
|
|
40
|
+
return skillMd(home, ".agents", "caveman");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Claude Code / Kiro native Caveman skill path. */
|
|
44
|
+
export function nativeCavemanSkillPath(home: string, vendor: NativeSkillVendor): string {
|
|
45
|
+
return skillMd(home, `.${vendor}`, "caveman");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Shared STE skill directory under `~/.agents/skills/ste`. */
|
|
49
|
+
export function agentsSteSkillDir(home: string): string {
|
|
50
|
+
return skillDir(home, ".agents", "ste");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Claude Code / Kiro native STE skill directory. */
|
|
54
|
+
export function nativeSteSkillDir(home: string, vendor: NativeSkillVendor): string {
|
|
55
|
+
return skillDir(home, `.${vendor}`, "ste");
|
|
56
|
+
}
|
|
57
|
+
|
|
12
58
|
function kiroHooksPath(home: string): string {
|
|
13
59
|
return join(home, ".kiro", "settings", "hooks.json");
|
|
14
60
|
}
|
|
@@ -17,10 +63,14 @@ function windsurfHooksPath(home: string): string {
|
|
|
17
63
|
return join(home, ".codeium", "windsurf", "hooks.json");
|
|
18
64
|
}
|
|
19
65
|
|
|
20
|
-
|
|
66
|
+
/** OpenCode config root (`$XDG_CONFIG_HOME/opencode` or `~/.config/opencode`). */
|
|
67
|
+
export function opencodeConfigDir(home: string): string {
|
|
21
68
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
22
|
-
|
|
23
|
-
|
|
69
|
+
return xdg ? join(xdg, "opencode") : join(home, ".config", "opencode");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function opencodePluginPath(home: string): string {
|
|
73
|
+
return join(opencodeConfigDir(home), "plugins", "miru-search-guard.ts");
|
|
24
74
|
}
|
|
25
75
|
|
|
26
76
|
export type InstallAction =
|
|
@@ -92,13 +142,19 @@ export interface AgentTarget {
|
|
|
92
142
|
hooksFormat: HooksFormat | null;
|
|
93
143
|
subagentPath: string | null;
|
|
94
144
|
subagentId: AgentId | null;
|
|
95
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* On-demand Caveman Agent Skill (`…/skills/caveman/SKILL.md`), or null if
|
|
147
|
+
* unsupported.
|
|
148
|
+
*/
|
|
96
149
|
cavemanSkillPath: string | null;
|
|
150
|
+
/**
|
|
151
|
+
* On-demand STE skill directory (`…/skills/ste/`), or null if unsupported.
|
|
152
|
+
*/
|
|
153
|
+
steSkillDir: string | null;
|
|
97
154
|
}
|
|
98
155
|
|
|
99
156
|
export function opencodeMcpPath(): string {
|
|
100
|
-
const
|
|
101
|
-
const base = xdg ? join(xdg, "opencode") : join(HOME, ".config", "opencode");
|
|
157
|
+
const base = opencodeConfigDir(HOME);
|
|
102
158
|
const jsonc = join(base, "opencode.jsonc");
|
|
103
159
|
const json = join(base, "opencode.json");
|
|
104
160
|
if (existsSync(jsonc)) {
|
|
@@ -165,6 +221,9 @@ function jsonMcp(path: string, key: string, entry: Record<string, unknown>): Mcp
|
|
|
165
221
|
return { path, key, memberKey: "miru", entry, format: "json" };
|
|
166
222
|
}
|
|
167
223
|
|
|
224
|
+
const SHARED_CAVEMAN_SKILL = agentsCavemanSkillPath(HOME);
|
|
225
|
+
const SHARED_STE_SKILL_DIR = agentsSteSkillDir(HOME);
|
|
226
|
+
|
|
168
227
|
export const AGENT_TARGETS: AgentTarget[] = [
|
|
169
228
|
{
|
|
170
229
|
id: "claude",
|
|
@@ -178,7 +237,8 @@ export const AGENT_TARGETS: AgentTarget[] = [
|
|
|
178
237
|
hooksFormat: "claude",
|
|
179
238
|
subagentPath: join(HOME, ".claude", "agents", "miru-code.md"),
|
|
180
239
|
subagentId: "claude",
|
|
181
|
-
cavemanSkillPath:
|
|
240
|
+
cavemanSkillPath: nativeCavemanSkillPath(HOME, "claude"),
|
|
241
|
+
steSkillDir: nativeSteSkillDir(HOME, "claude"),
|
|
182
242
|
},
|
|
183
243
|
{
|
|
184
244
|
id: "cursor",
|
|
@@ -192,7 +252,8 @@ export const AGENT_TARGETS: AgentTarget[] = [
|
|
|
192
252
|
hooksFormat: "cursor",
|
|
193
253
|
subagentPath: join(HOME, ".cursor", "agents", "miru-code.md"),
|
|
194
254
|
subagentId: "cursor",
|
|
195
|
-
cavemanSkillPath:
|
|
255
|
+
cavemanSkillPath: SHARED_CAVEMAN_SKILL,
|
|
256
|
+
steSkillDir: SHARED_STE_SKILL_DIR,
|
|
196
257
|
},
|
|
197
258
|
{
|
|
198
259
|
id: "gemini",
|
|
@@ -206,7 +267,8 @@ export const AGENT_TARGETS: AgentTarget[] = [
|
|
|
206
267
|
hooksFormat: "gemini",
|
|
207
268
|
subagentPath: join(HOME, ".gemini", "agents", "miru-code.md"),
|
|
208
269
|
subagentId: "gemini",
|
|
209
|
-
cavemanSkillPath:
|
|
270
|
+
cavemanSkillPath: SHARED_CAVEMAN_SKILL,
|
|
271
|
+
steSkillDir: SHARED_STE_SKILL_DIR,
|
|
210
272
|
},
|
|
211
273
|
{
|
|
212
274
|
id: "kiro",
|
|
@@ -220,35 +282,42 @@ export const AGENT_TARGETS: AgentTarget[] = [
|
|
|
220
282
|
hooksFormat: "kiro",
|
|
221
283
|
subagentPath: join(HOME, ".kiro", "agents", "miru-code.md"),
|
|
222
284
|
subagentId: "kiro",
|
|
223
|
-
cavemanSkillPath:
|
|
285
|
+
cavemanSkillPath: nativeCavemanSkillPath(HOME, "kiro"),
|
|
286
|
+
steSkillDir: nativeSteSkillDir(HOME, "kiro"),
|
|
224
287
|
},
|
|
225
288
|
{
|
|
226
289
|
id: "opencode",
|
|
227
290
|
displayName: "OpenCode",
|
|
228
291
|
binary: "opencode",
|
|
229
|
-
configDir:
|
|
292
|
+
configDir: opencodeConfigDir(HOME),
|
|
230
293
|
mcp: jsonMcp(opencodeMcpPath(), "mcp", OPENCODE_SERVER_CONFIG),
|
|
231
|
-
instructionsPath: join(HOME, "
|
|
294
|
+
instructionsPath: join(opencodeConfigDir(HOME), "AGENTS.md"),
|
|
232
295
|
cursorRulesPath: null,
|
|
233
296
|
hooksPath: opencodePluginPath(HOME),
|
|
234
297
|
hooksFormat: "opencode",
|
|
235
|
-
subagentPath: join(HOME, "
|
|
298
|
+
subagentPath: join(opencodeConfigDir(HOME), "agents", "miru-code.md"),
|
|
236
299
|
subagentId: "opencode",
|
|
237
|
-
cavemanSkillPath:
|
|
300
|
+
cavemanSkillPath: SHARED_CAVEMAN_SKILL,
|
|
301
|
+
steSkillDir: SHARED_STE_SKILL_DIR,
|
|
238
302
|
},
|
|
239
303
|
{
|
|
240
304
|
id: "copilot",
|
|
241
305
|
displayName: "GitHub Copilot",
|
|
242
306
|
binary: null,
|
|
243
307
|
configDir: join(HOME, ".config", "github-copilot"),
|
|
244
|
-
mcp: jsonMcp(
|
|
308
|
+
mcp: jsonMcp(
|
|
309
|
+
join(copilotHomeDir(HOME), "mcp-config.json"),
|
|
310
|
+
"mcpServers",
|
|
311
|
+
BARE_STDIO_SERVER_CONFIG,
|
|
312
|
+
),
|
|
245
313
|
instructionsPath: null,
|
|
246
314
|
cursorRulesPath: null,
|
|
247
315
|
hooksPath: copilotHooksPath(HOME),
|
|
248
316
|
hooksFormat: "vscode",
|
|
249
|
-
subagentPath: join(HOME, "
|
|
317
|
+
subagentPath: join(copilotHomeDir(HOME), "agents", "miru-code.agent.md"),
|
|
250
318
|
subagentId: "copilot",
|
|
251
|
-
cavemanSkillPath:
|
|
319
|
+
cavemanSkillPath: SHARED_CAVEMAN_SKILL,
|
|
320
|
+
steSkillDir: SHARED_STE_SKILL_DIR,
|
|
252
321
|
},
|
|
253
322
|
{
|
|
254
323
|
id: "codex",
|
|
@@ -268,7 +337,8 @@ export const AGENT_TARGETS: AgentTarget[] = [
|
|
|
268
337
|
hooksFormat: "claude",
|
|
269
338
|
subagentPath: null,
|
|
270
339
|
subagentId: null,
|
|
271
|
-
cavemanSkillPath:
|
|
340
|
+
cavemanSkillPath: SHARED_CAVEMAN_SKILL,
|
|
341
|
+
steSkillDir: SHARED_STE_SKILL_DIR,
|
|
272
342
|
},
|
|
273
343
|
{
|
|
274
344
|
id: "vscode",
|
|
@@ -282,7 +352,8 @@ export const AGENT_TARGETS: AgentTarget[] = [
|
|
|
282
352
|
hooksFormat: "vscode",
|
|
283
353
|
subagentPath: null,
|
|
284
354
|
subagentId: null,
|
|
285
|
-
cavemanSkillPath:
|
|
355
|
+
cavemanSkillPath: SHARED_CAVEMAN_SKILL,
|
|
356
|
+
steSkillDir: SHARED_STE_SKILL_DIR,
|
|
286
357
|
},
|
|
287
358
|
{
|
|
288
359
|
id: "windsurf",
|
|
@@ -296,7 +367,8 @@ export const AGENT_TARGETS: AgentTarget[] = [
|
|
|
296
367
|
hooksFormat: "windsurf",
|
|
297
368
|
subagentPath: null,
|
|
298
369
|
subagentId: null,
|
|
299
|
-
cavemanSkillPath:
|
|
370
|
+
cavemanSkillPath: SHARED_CAVEMAN_SKILL,
|
|
371
|
+
steSkillDir: SHARED_STE_SKILL_DIR,
|
|
300
372
|
},
|
|
301
373
|
{
|
|
302
374
|
id: "visualstudio",
|
|
@@ -310,7 +382,8 @@ export const AGENT_TARGETS: AgentTarget[] = [
|
|
|
310
382
|
hooksFormat: "vscode",
|
|
311
383
|
subagentPath: null,
|
|
312
384
|
subagentId: null,
|
|
313
|
-
cavemanSkillPath:
|
|
385
|
+
cavemanSkillPath: SHARED_CAVEMAN_SKILL,
|
|
386
|
+
steSkillDir: SHARED_STE_SKILL_DIR,
|
|
314
387
|
},
|
|
315
388
|
];
|
|
316
389
|
|
|
@@ -337,6 +410,10 @@ export async function isAgentDetected(agent: AgentTarget): Promise<boolean> {
|
|
|
337
410
|
}
|
|
338
411
|
return false;
|
|
339
412
|
}
|
|
413
|
+
// Copilot-specific artifacts only — ~/.copilot alone is shared with VS Code / VS.
|
|
414
|
+
if (agent.id === "copilot") {
|
|
415
|
+
return isCopilotInstalled(HOME);
|
|
416
|
+
}
|
|
340
417
|
if (agent.binary && (await commandOnPath(agent.binary))) {
|
|
341
418
|
return true;
|
|
342
419
|
}
|
|
@@ -345,3 +422,21 @@ export async function isAgentDetected(agent: AgentTarget): Promise<boolean> {
|
|
|
345
422
|
}
|
|
346
423
|
return false;
|
|
347
424
|
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* True when Copilot-specific config exists. Does not treat shared
|
|
428
|
+
* `~/.copilot` hooks (also used by VS Code / Visual Studio) as enough.
|
|
429
|
+
*/
|
|
430
|
+
export function isCopilotInstalled(home: string = HOME): boolean {
|
|
431
|
+
if (existsSync(join(home, ".config", "github-copilot"))) {
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
434
|
+
const copilot = copilotHomeDir(home);
|
|
435
|
+
if (existsSync(join(copilot, "mcp-config.json"))) {
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
if (existsSync(join(copilot, "agents"))) {
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AgentTarget, InstallAction } from "./agents.ts";
|
|
2
|
+
import {
|
|
3
|
+
ensureSharedSkillOwnersOnInstall,
|
|
4
|
+
isSharedSkillPath,
|
|
5
|
+
removeSkillMdAndOwners,
|
|
6
|
+
resolveSharedSkillUninstall,
|
|
7
|
+
type SharedSkillCtx,
|
|
8
|
+
type SharedSkillUninstallDecision,
|
|
9
|
+
SKILL_OWNERS_FILE,
|
|
10
|
+
type SkillPathOf,
|
|
11
|
+
} from "./shared-skill.ts";
|
|
12
|
+
|
|
13
|
+
/** @deprecated Prefer SKILL_OWNERS_FILE — same sidecar name for all shared skills. */
|
|
14
|
+
export const CAVEMAN_OWNERS_FILE = SKILL_OWNERS_FILE;
|
|
15
|
+
|
|
16
|
+
/** Context for shared `~/.agents/skills` Caveman install/uninstall. */
|
|
17
|
+
export type CavemanSharedCtx = SharedSkillCtx;
|
|
18
|
+
|
|
19
|
+
export type SharedCavemanUninstallDecision = SharedSkillUninstallDecision;
|
|
20
|
+
|
|
21
|
+
const cavemanPathOf: SkillPathOf = (agent) => agent.cavemanSkillPath;
|
|
22
|
+
|
|
23
|
+
export function isSharedCavemanPath(path: string, allAgents: AgentTarget[]): boolean {
|
|
24
|
+
return isSharedSkillPath(path, allAgents, cavemanPathOf);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function ensureSharedCavemanOwnersOnInstall(
|
|
28
|
+
skillDir: string,
|
|
29
|
+
skillPath: string,
|
|
30
|
+
agent: AgentTarget,
|
|
31
|
+
ctx: CavemanSharedCtx,
|
|
32
|
+
): Promise<void> {
|
|
33
|
+
return ensureSharedSkillOwnersOnInstall(skillDir, skillPath, agent, ctx, cavemanPathOf);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function resolveSharedCavemanUninstall(
|
|
37
|
+
path: string,
|
|
38
|
+
skillDir: string,
|
|
39
|
+
agent: AgentTarget,
|
|
40
|
+
ctx: CavemanSharedCtx,
|
|
41
|
+
): Promise<SharedCavemanUninstallDecision> {
|
|
42
|
+
return resolveSharedSkillUninstall(path, skillDir, agent, ctx, cavemanPathOf);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Remove SKILL.md and owners sidecar; leave sibling skill folders untouched. */
|
|
46
|
+
export async function removeCavemanSkillFiles(
|
|
47
|
+
path: string,
|
|
48
|
+
skillDir: string,
|
|
49
|
+
): Promise<Extract<InstallAction, "removed" | "not-found">> {
|
|
50
|
+
return removeSkillMdAndOwners(path, skillDir);
|
|
51
|
+
}
|
package/src/installer/config.ts
CHANGED
|
@@ -616,3 +616,76 @@ export async function removeTomlBlock(path: string): Promise<InstallAction> {
|
|
|
616
616
|
await Bun.write(path, `${remaining}\n`);
|
|
617
617
|
return "removed";
|
|
618
618
|
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Ensure Codex loads Agent Skills: `[features] skills = true` in config.toml.
|
|
622
|
+
* Does not disable the flag on uninstall (other skills may still need it).
|
|
623
|
+
*/
|
|
624
|
+
export function codexSkillsFeatureEnabled(text: string): boolean {
|
|
625
|
+
const lines = text.split("\n");
|
|
626
|
+
let inFeatures = false;
|
|
627
|
+
for (const line of lines) {
|
|
628
|
+
const tableKey = line.split("#")[0]?.trim() ?? "";
|
|
629
|
+
if (tableKey.startsWith("[") && tableKey.endsWith("]")) {
|
|
630
|
+
inFeatures = tableKey === "[features]";
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
if (!inFeatures) {
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
const value = line.split("#")[0]?.match(/^\s*skills\s*=\s*(true|false)\s*$/i)?.[1];
|
|
637
|
+
if (value?.toLowerCase() === "true") {
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
export async function ensureCodexSkillsFeature(path: string): Promise<InstallAction> {
|
|
645
|
+
const existed = await Bun.file(path).exists();
|
|
646
|
+
const existing = existed ? await Bun.file(path).text() : "";
|
|
647
|
+
if (codexSkillsFeatureEnabled(existing)) {
|
|
648
|
+
return "unchanged";
|
|
649
|
+
}
|
|
650
|
+
const lines = existing.length > 0 ? existing.split("\n") : [];
|
|
651
|
+
|
|
652
|
+
let featuresIdx = -1;
|
|
653
|
+
let skillsIdx = -1;
|
|
654
|
+
let inFeatures = false;
|
|
655
|
+
|
|
656
|
+
for (let i = 0; i < lines.length; i++) {
|
|
657
|
+
const tableKey = (lines[i] ?? "").split("#")[0]?.trim() ?? "";
|
|
658
|
+
if (tableKey.startsWith("[") && tableKey.endsWith("]")) {
|
|
659
|
+
if (tableKey === "[features]") {
|
|
660
|
+
inFeatures = true;
|
|
661
|
+
featuresIdx = i;
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
inFeatures = false;
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
if (inFeatures && /^\s*skills\s*=/.test(lines[i] ?? "")) {
|
|
668
|
+
skillsIdx = i;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (featuresIdx >= 0 && skillsIdx >= 0) {
|
|
673
|
+
lines[skillsIdx] = "skills = true";
|
|
674
|
+
const next = `${lines.join("\n").replace(/\n+$/, "")}\n`;
|
|
675
|
+
await Bun.write(path, next);
|
|
676
|
+
return "updated";
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (featuresIdx >= 0) {
|
|
680
|
+
lines.splice(featuresIdx + 1, 0, "skills = true");
|
|
681
|
+
const next = `${lines.join("\n").replace(/\n+$/, "")}\n`;
|
|
682
|
+
await Bun.write(path, next);
|
|
683
|
+
return "updated";
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const block = "[features]\nskills = true\n";
|
|
687
|
+
const base = existing.replace(/\n+$/, "");
|
|
688
|
+
const next = base.length > 0 ? `${base}\n\n${block}` : block;
|
|
689
|
+
await Bun.write(path, next.endsWith("\n") ? next : `${next}\n`);
|
|
690
|
+
return existed ? "updated" : "created";
|
|
691
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Agents that share `~/.copilot` hooks. Caveman uses `~/.agents/skills`. */
|
|
2
|
+
export const COPILOT_FAMILY_IDS = ["copilot", "vscode", "visualstudio"] as const;
|
|
3
|
+
|
|
4
|
+
export type CopilotFamilyId = (typeof COPILOT_FAMILY_IDS)[number];
|
|
5
|
+
|
|
6
|
+
export function isCopilotFamilyId(id: string): id is CopilotFamilyId {
|
|
7
|
+
return (COPILOT_FAMILY_IDS as readonly string[]).includes(id);
|
|
8
|
+
}
|
|
@@ -2,11 +2,11 @@ import { unlink } from "node:fs/promises";
|
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import type { HooksFormat, InstallAction } from "../agents.ts";
|
|
4
4
|
import { stripJsonComments } from "../config.ts";
|
|
5
|
+
import { isCopilotFamilyId } from "../copilot-family.ts";
|
|
5
6
|
|
|
6
7
|
const HOOK_GUARD_MARKER = "hook-guard";
|
|
7
8
|
const MIRU_HOOK_MARKER = "miru-search";
|
|
8
9
|
const VSCODE_OWNERS_KEY = "miruOwners";
|
|
9
|
-
const SHARED_VSCODE_HOOK_OWNERS = ["copilot", "vscode", "visualstudio"] as const;
|
|
10
10
|
|
|
11
11
|
const MATCHERS = {
|
|
12
12
|
cursor: "Grep|Glob|Shell|SemanticSearch",
|
|
@@ -346,22 +346,11 @@ export async function mergeVscodeHooks(path: string, owner?: string): Promise<In
|
|
|
346
346
|
|
|
347
347
|
const currentOwners = Array.isArray(parsed[VSCODE_OWNERS_KEY])
|
|
348
348
|
? (parsed[VSCODE_OWNERS_KEY] as unknown[]).filter(
|
|
349
|
-
(item): item is string =>
|
|
350
|
-
typeof item === "string" &&
|
|
351
|
-
SHARED_VSCODE_HOOK_OWNERS.includes(item as (typeof SHARED_VSCODE_HOOK_OWNERS)[number]),
|
|
349
|
+
(item): item is string => typeof item === "string" && isCopilotFamilyId(item),
|
|
352
350
|
)
|
|
353
351
|
: [];
|
|
354
352
|
const nextOwners = owner
|
|
355
|
-
? Array.from(
|
|
356
|
-
new Set([
|
|
357
|
-
...currentOwners,
|
|
358
|
-
...(SHARED_VSCODE_HOOK_OWNERS.includes(
|
|
359
|
-
owner as (typeof SHARED_VSCODE_HOOK_OWNERS)[number],
|
|
360
|
-
)
|
|
361
|
-
? [owner]
|
|
362
|
-
: []),
|
|
363
|
-
]),
|
|
364
|
-
)
|
|
353
|
+
? Array.from(new Set([...currentOwners, ...(isCopilotFamilyId(owner) ? [owner] : [])]))
|
|
365
354
|
: currentOwners;
|
|
366
355
|
|
|
367
356
|
const next = {
|