forked from EveryInc/compound-engineering-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsync-copilot.test.ts
More file actions
148 lines (121 loc) · 4.55 KB
/
Copy pathsync-copilot.test.ts
File metadata and controls
148 lines (121 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import { describe, expect, test } from "bun:test"
import { promises as fs } from "fs"
import path from "path"
import os from "os"
import { syncToCopilot } from "../src/sync/copilot"
import type { ClaudeHomeConfig } from "../src/parsers/claude-home"
describe("syncToCopilot", () => {
test("symlinks skills to .github/skills/", async () => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "sync-copilot-"))
const fixtureSkillDir = path.join(import.meta.dir, "fixtures", "sample-plugin", "skills", "skill-one")
const config: ClaudeHomeConfig = {
skills: [
{
name: "skill-one",
sourceDir: fixtureSkillDir,
skillPath: path.join(fixtureSkillDir, "SKILL.md"),
},
],
mcpServers: {},
}
await syncToCopilot(config, tempRoot)
const linkedSkillPath = path.join(tempRoot, "skills", "skill-one")
const linkedStat = await fs.lstat(linkedSkillPath)
expect(linkedStat.isSymbolicLink()).toBe(true)
})
test("skips skills with invalid names", async () => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "sync-copilot-invalid-"))
const config: ClaudeHomeConfig = {
skills: [
{
name: "../escape-attempt",
sourceDir: "/tmp/bad-skill",
skillPath: "/tmp/bad-skill/SKILL.md",
},
],
mcpServers: {},
}
await syncToCopilot(config, tempRoot)
const skillsDir = path.join(tempRoot, "skills")
const entries = await fs.readdir(skillsDir).catch(() => [])
expect(entries).toHaveLength(0)
})
test("merges MCP config with existing file", async () => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "sync-copilot-merge-"))
const mcpPath = path.join(tempRoot, "copilot-mcp-config.json")
await fs.writeFile(
mcpPath,
JSON.stringify({
mcpServers: {
existing: { type: "local", command: "node", args: ["server.js"], tools: ["*"] },
},
}, null, 2),
)
const config: ClaudeHomeConfig = {
skills: [],
mcpServers: {
context7: { url: "https://mcp.context7.com/mcp" },
},
}
await syncToCopilot(config, tempRoot)
const merged = JSON.parse(await fs.readFile(mcpPath, "utf8")) as {
mcpServers: Record<string, { command?: string; url?: string; type: string }>
}
expect(merged.mcpServers.existing?.command).toBe("node")
expect(merged.mcpServers.context7?.url).toBe("https://mcp.context7.com/mcp")
})
test("transforms MCP env var names to COPILOT_MCP_ prefix", async () => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "sync-copilot-env-"))
const config: ClaudeHomeConfig = {
skills: [],
mcpServers: {
server: {
command: "echo",
args: ["hello"],
env: { API_KEY: "secret", COPILOT_MCP_TOKEN: "already-prefixed" },
},
},
}
await syncToCopilot(config, tempRoot)
const mcpPath = path.join(tempRoot, "copilot-mcp-config.json")
const mcpConfig = JSON.parse(await fs.readFile(mcpPath, "utf8")) as {
mcpServers: Record<string, { env?: Record<string, string> }>
}
expect(mcpConfig.mcpServers.server?.env).toEqual({
COPILOT_MCP_API_KEY: "secret",
COPILOT_MCP_TOKEN: "already-prefixed",
})
})
test("writes MCP config with restricted permissions", async () => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "sync-copilot-perms-"))
const config: ClaudeHomeConfig = {
skills: [],
mcpServers: {
server: { command: "echo", args: ["hello"] },
},
}
await syncToCopilot(config, tempRoot)
const mcpPath = path.join(tempRoot, "copilot-mcp-config.json")
const stat = await fs.stat(mcpPath)
// Check owner read+write permission (0o600 = 33216 in decimal, masked to file perms)
const perms = stat.mode & 0o777
expect(perms).toBe(0o600)
})
test("does not write MCP config when no MCP servers", async () => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "sync-copilot-nomcp-"))
const fixtureSkillDir = path.join(import.meta.dir, "fixtures", "sample-plugin", "skills", "skill-one")
const config: ClaudeHomeConfig = {
skills: [
{
name: "skill-one",
sourceDir: fixtureSkillDir,
skillPath: path.join(fixtureSkillDir, "SKILL.md"),
},
],
mcpServers: {},
}
await syncToCopilot(config, tempRoot)
const mcpExists = await fs.access(path.join(tempRoot, "copilot-mcp-config.json")).then(() => true).catch(() => false)
expect(mcpExists).toBe(false)
})
})