@codemcp/ade-harnesses 0.2.0 → 0.2.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/dist/writers/kiro.js +1 -1
- package/package.json +20 -12
- package/.prettierignore +0 -1
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-format.log +0 -6
- package/.turbo/turbo-lint.log +0 -4
- package/.turbo/turbo-test.log +0 -23
- package/.turbo/turbo-typecheck.log +0 -4
- package/eslint.config.mjs +0 -40
- package/src/index.spec.ts +0 -45
- package/src/index.ts +0 -47
- package/src/permission-policy.ts +0 -173
- package/src/skills-installer.ts +0 -54
- package/src/types.ts +0 -12
- package/src/util.ts +0 -221
- package/src/writers/claude-code.spec.ts +0 -320
- package/src/writers/claude-code.ts +0 -107
- package/src/writers/cline.spec.ts +0 -212
- package/src/writers/cline.ts +0 -24
- package/src/writers/copilot.spec.ts +0 -258
- package/src/writers/copilot.ts +0 -105
- package/src/writers/cursor.spec.ts +0 -219
- package/src/writers/cursor.ts +0 -95
- package/src/writers/kiro.spec.ts +0 -228
- package/src/writers/kiro.ts +0 -89
- package/src/writers/opencode.spec.ts +0 -258
- package/src/writers/opencode.ts +0 -67
- package/src/writers/roo-code.spec.ts +0 -197
- package/src/writers/roo-code.ts +0 -71
- package/src/writers/universal.spec.ts +0 -134
- package/src/writers/universal.ts +0 -84
- package/src/writers/windsurf.spec.ts +0 -178
- package/src/writers/windsurf.ts +0 -89
- package/tsconfig.build.json +0 -8
- package/tsconfig.json +0 -7
- package/tsconfig.tsbuildinfo +0 -1
- package/tsconfig.vitest.json +0 -7
- package/vitest.config.ts +0 -5
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { mkdtemp, rm, readFile } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import type {
|
|
6
|
-
AutonomyProfile,
|
|
7
|
-
LogicalConfig,
|
|
8
|
-
PermissionPolicy
|
|
9
|
-
} from "@codemcp/ade-core";
|
|
10
|
-
import { clineWriter } from "./cline.js";
|
|
11
|
-
|
|
12
|
-
function autonomyPolicy(profile: AutonomyProfile): PermissionPolicy {
|
|
13
|
-
switch (profile) {
|
|
14
|
-
case "rigid":
|
|
15
|
-
return {
|
|
16
|
-
profile,
|
|
17
|
-
capabilities: {
|
|
18
|
-
read: "ask",
|
|
19
|
-
edit_write: "ask",
|
|
20
|
-
search_list: "ask",
|
|
21
|
-
bash_safe: "ask",
|
|
22
|
-
bash_unsafe: "ask",
|
|
23
|
-
web: "ask",
|
|
24
|
-
task_agent: "ask"
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
case "sensible-defaults":
|
|
28
|
-
return {
|
|
29
|
-
profile,
|
|
30
|
-
capabilities: {
|
|
31
|
-
read: "allow",
|
|
32
|
-
edit_write: "allow",
|
|
33
|
-
search_list: "allow",
|
|
34
|
-
bash_safe: "allow",
|
|
35
|
-
bash_unsafe: "ask",
|
|
36
|
-
web: "ask",
|
|
37
|
-
task_agent: "allow"
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
case "max-autonomy":
|
|
41
|
-
return {
|
|
42
|
-
profile,
|
|
43
|
-
capabilities: {
|
|
44
|
-
read: "allow",
|
|
45
|
-
edit_write: "allow",
|
|
46
|
-
search_list: "allow",
|
|
47
|
-
bash_safe: "allow",
|
|
48
|
-
bash_unsafe: "allow",
|
|
49
|
-
web: "ask",
|
|
50
|
-
task_agent: "allow"
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
describe("clineWriter", () => {
|
|
57
|
-
let dir: string;
|
|
58
|
-
|
|
59
|
-
beforeEach(async () => {
|
|
60
|
-
dir = await mkdtemp(join(tmpdir(), "ade-harness-cline-"));
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
afterEach(async () => {
|
|
64
|
-
await rm(dir, { recursive: true, force: true });
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("has correct metadata", () => {
|
|
68
|
-
expect(clineWriter.id).toBe("cline");
|
|
69
|
-
expect(clineWriter.label).toBe("Cline");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("writes cline_mcp_settings.json with MCP servers", async () => {
|
|
73
|
-
const config: LogicalConfig = {
|
|
74
|
-
mcp_servers: [
|
|
75
|
-
{
|
|
76
|
-
ref: "workflows",
|
|
77
|
-
command: "npx",
|
|
78
|
-
args: ["-y", "@codemcp/workflows"],
|
|
79
|
-
env: {}
|
|
80
|
-
}
|
|
81
|
-
],
|
|
82
|
-
instructions: [],
|
|
83
|
-
cli_actions: [],
|
|
84
|
-
knowledge_sources: [],
|
|
85
|
-
skills: [],
|
|
86
|
-
git_hooks: [],
|
|
87
|
-
setup_notes: []
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
await clineWriter.install(config, dir);
|
|
91
|
-
|
|
92
|
-
const raw = await readFile(join(dir, "cline_mcp_settings.json"), "utf-8");
|
|
93
|
-
const parsed = JSON.parse(raw);
|
|
94
|
-
expect(parsed.mcpServers["workflows"]).toEqual({
|
|
95
|
-
command: "npx",
|
|
96
|
-
args: ["-y", "@codemcp/workflows"],
|
|
97
|
-
alwaysAllow: ["*"]
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("forwards explicit MCP approvals unchanged from provisioning", async () => {
|
|
102
|
-
const config: LogicalConfig = {
|
|
103
|
-
mcp_servers: [
|
|
104
|
-
{
|
|
105
|
-
ref: "workflows",
|
|
106
|
-
command: "npx",
|
|
107
|
-
args: ["-y", "@codemcp/workflows"],
|
|
108
|
-
env: {},
|
|
109
|
-
allowedTools: ["whats_next", "proceed_to_phase"]
|
|
110
|
-
}
|
|
111
|
-
],
|
|
112
|
-
instructions: [],
|
|
113
|
-
cli_actions: [],
|
|
114
|
-
knowledge_sources: [],
|
|
115
|
-
skills: [],
|
|
116
|
-
git_hooks: [],
|
|
117
|
-
setup_notes: []
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
await clineWriter.install(config, dir);
|
|
121
|
-
|
|
122
|
-
const raw = await readFile(join(dir, "cline_mcp_settings.json"), "utf-8");
|
|
123
|
-
const parsed = JSON.parse(raw);
|
|
124
|
-
expect(parsed.mcpServers["workflows"]).toEqual({
|
|
125
|
-
command: "npx",
|
|
126
|
-
args: ["-y", "@codemcp/workflows"],
|
|
127
|
-
alwaysAllow: ["whats_next", "proceed_to_phase"]
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("writes .clinerules with instructions", async () => {
|
|
132
|
-
const config: LogicalConfig = {
|
|
133
|
-
mcp_servers: [],
|
|
134
|
-
instructions: ["Follow TDD."],
|
|
135
|
-
cli_actions: [],
|
|
136
|
-
knowledge_sources: [],
|
|
137
|
-
skills: [],
|
|
138
|
-
git_hooks: [],
|
|
139
|
-
setup_notes: []
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
await clineWriter.install(config, dir);
|
|
143
|
-
|
|
144
|
-
const content = await readFile(join(dir, ".clinerules"), "utf-8");
|
|
145
|
-
expect(content).toContain("Follow TDD.");
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("does not invent built-in auto-approval settings for autonomy profiles", async () => {
|
|
149
|
-
const rigidRoot = join(dir, "rigid");
|
|
150
|
-
const sensibleRoot = join(dir, "sensible");
|
|
151
|
-
const maxRoot = join(dir, "max");
|
|
152
|
-
|
|
153
|
-
const rigidConfig: LogicalConfig = {
|
|
154
|
-
mcp_servers: [
|
|
155
|
-
{
|
|
156
|
-
ref: "workflows",
|
|
157
|
-
command: "npx",
|
|
158
|
-
args: ["-y", "@codemcp/workflows"],
|
|
159
|
-
env: {}
|
|
160
|
-
}
|
|
161
|
-
],
|
|
162
|
-
instructions: ["Use approvals for risky actions."],
|
|
163
|
-
cli_actions: [],
|
|
164
|
-
knowledge_sources: [],
|
|
165
|
-
skills: [],
|
|
166
|
-
git_hooks: [],
|
|
167
|
-
setup_notes: [],
|
|
168
|
-
permission_policy: autonomyPolicy("rigid")
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
const sensibleConfig: LogicalConfig = {
|
|
172
|
-
...rigidConfig,
|
|
173
|
-
permission_policy: autonomyPolicy("sensible-defaults")
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const maxConfig: LogicalConfig = {
|
|
177
|
-
...rigidConfig,
|
|
178
|
-
permission_policy: autonomyPolicy("max-autonomy")
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
await clineWriter.install(rigidConfig, rigidRoot);
|
|
182
|
-
await clineWriter.install(sensibleConfig, sensibleRoot);
|
|
183
|
-
await clineWriter.install(maxConfig, maxRoot);
|
|
184
|
-
|
|
185
|
-
const rigidSettings = JSON.parse(
|
|
186
|
-
await readFile(join(rigidRoot, "cline_mcp_settings.json"), "utf-8")
|
|
187
|
-
);
|
|
188
|
-
const sensibleSettings = JSON.parse(
|
|
189
|
-
await readFile(join(sensibleRoot, "cline_mcp_settings.json"), "utf-8")
|
|
190
|
-
);
|
|
191
|
-
const maxSettings = JSON.parse(
|
|
192
|
-
await readFile(join(maxRoot, "cline_mcp_settings.json"), "utf-8")
|
|
193
|
-
);
|
|
194
|
-
const maxRules = await readFile(join(maxRoot, ".clinerules"), "utf-8");
|
|
195
|
-
|
|
196
|
-
expect(rigidSettings).toEqual(sensibleSettings);
|
|
197
|
-
expect(sensibleSettings).toEqual(maxSettings);
|
|
198
|
-
expect(maxSettings).toEqual({
|
|
199
|
-
mcpServers: {
|
|
200
|
-
workflows: {
|
|
201
|
-
command: "npx",
|
|
202
|
-
args: ["-y", "@codemcp/workflows"],
|
|
203
|
-
alwaysAllow: ["*"]
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
expect(maxRules).toContain("Use approvals for risky actions.");
|
|
208
|
-
expect(maxRules).not.toContain("browser_action");
|
|
209
|
-
expect(maxRules).not.toContain("execute_command");
|
|
210
|
-
expect(maxRules).not.toContain("web");
|
|
211
|
-
});
|
|
212
|
-
});
|
package/src/writers/cline.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
import type { LogicalConfig } from "@codemcp/ade-core";
|
|
3
|
-
import type { HarnessWriter } from "../types.js";
|
|
4
|
-
import {
|
|
5
|
-
writeMcpServers,
|
|
6
|
-
alwaysAllowEntry,
|
|
7
|
-
writeRulesFile,
|
|
8
|
-
writeGitHooks
|
|
9
|
-
} from "../util.js";
|
|
10
|
-
|
|
11
|
-
export const clineWriter: HarnessWriter = {
|
|
12
|
-
id: "cline",
|
|
13
|
-
label: "Cline",
|
|
14
|
-
description: "VS Code AI agent — cline_mcp_settings.json + .clinerules",
|
|
15
|
-
async install(config: LogicalConfig, projectRoot: string) {
|
|
16
|
-
await writeMcpServers(config.mcp_servers, {
|
|
17
|
-
path: join(projectRoot, "cline_mcp_settings.json"),
|
|
18
|
-
transform: alwaysAllowEntry
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
await writeRulesFile(config.instructions, join(projectRoot, ".clinerules"));
|
|
22
|
-
await writeGitHooks(config.git_hooks, projectRoot);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { mkdtemp, rm, readFile } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import type {
|
|
6
|
-
AutonomyProfile,
|
|
7
|
-
LogicalConfig,
|
|
8
|
-
PermissionPolicy
|
|
9
|
-
} from "@codemcp/ade-core";
|
|
10
|
-
import { copilotWriter } from "./copilot.js";
|
|
11
|
-
|
|
12
|
-
function autonomyPolicy(profile: AutonomyProfile): PermissionPolicy {
|
|
13
|
-
switch (profile) {
|
|
14
|
-
case "rigid":
|
|
15
|
-
return {
|
|
16
|
-
profile,
|
|
17
|
-
capabilities: {
|
|
18
|
-
read: "ask",
|
|
19
|
-
edit_write: "ask",
|
|
20
|
-
search_list: "ask",
|
|
21
|
-
bash_safe: "ask",
|
|
22
|
-
bash_unsafe: "ask",
|
|
23
|
-
web: "ask",
|
|
24
|
-
task_agent: "ask"
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
case "sensible-defaults":
|
|
28
|
-
return {
|
|
29
|
-
profile,
|
|
30
|
-
capabilities: {
|
|
31
|
-
read: "allow",
|
|
32
|
-
edit_write: "allow",
|
|
33
|
-
search_list: "allow",
|
|
34
|
-
bash_safe: "allow",
|
|
35
|
-
bash_unsafe: "ask",
|
|
36
|
-
web: "ask",
|
|
37
|
-
task_agent: "allow"
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
case "max-autonomy":
|
|
41
|
-
return {
|
|
42
|
-
profile,
|
|
43
|
-
capabilities: {
|
|
44
|
-
read: "allow",
|
|
45
|
-
edit_write: "allow",
|
|
46
|
-
search_list: "allow",
|
|
47
|
-
bash_safe: "allow",
|
|
48
|
-
bash_unsafe: "allow",
|
|
49
|
-
web: "ask",
|
|
50
|
-
task_agent: "allow"
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
describe("copilotWriter", () => {
|
|
57
|
-
let dir: string;
|
|
58
|
-
|
|
59
|
-
beforeEach(async () => {
|
|
60
|
-
dir = await mkdtemp(join(tmpdir(), "ade-harness-copilot-"));
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
afterEach(async () => {
|
|
64
|
-
await rm(dir, { recursive: true, force: true });
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("has correct metadata", () => {
|
|
68
|
-
expect(copilotWriter.id).toBe("copilot");
|
|
69
|
-
expect(copilotWriter.label).toBe("GitHub Copilot");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("writes .vscode/mcp.json with 'servers' key and type field", async () => {
|
|
73
|
-
const config: LogicalConfig = {
|
|
74
|
-
mcp_servers: [
|
|
75
|
-
{
|
|
76
|
-
ref: "workflows",
|
|
77
|
-
command: "npx",
|
|
78
|
-
args: ["-y", "@codemcp/workflows"],
|
|
79
|
-
env: {}
|
|
80
|
-
}
|
|
81
|
-
],
|
|
82
|
-
instructions: [],
|
|
83
|
-
cli_actions: [],
|
|
84
|
-
knowledge_sources: [],
|
|
85
|
-
skills: [],
|
|
86
|
-
git_hooks: [],
|
|
87
|
-
setup_notes: []
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
await copilotWriter.install(config, dir);
|
|
91
|
-
|
|
92
|
-
const raw = await readFile(join(dir, ".vscode", "mcp.json"), "utf-8");
|
|
93
|
-
const parsed = JSON.parse(raw);
|
|
94
|
-
// Copilot uses "servers", not "mcpServers"
|
|
95
|
-
expect(parsed.servers["workflows"]).toEqual({
|
|
96
|
-
type: "stdio",
|
|
97
|
-
command: "npx",
|
|
98
|
-
args: ["-y", "@codemcp/workflows"]
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("does not write copilot-instructions.md (prefers agent definition)", async () => {
|
|
103
|
-
const config: LogicalConfig = {
|
|
104
|
-
mcp_servers: [],
|
|
105
|
-
instructions: ["Follow TDD."],
|
|
106
|
-
cli_actions: [],
|
|
107
|
-
knowledge_sources: [],
|
|
108
|
-
skills: [],
|
|
109
|
-
git_hooks: [],
|
|
110
|
-
setup_notes: []
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
await copilotWriter.install(config, dir);
|
|
114
|
-
|
|
115
|
-
await expect(
|
|
116
|
-
readFile(join(dir, ".github", "copilot-instructions.md"), "utf-8")
|
|
117
|
-
).rejects.toThrow();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("writes dedicated .github/agents/ade.agent.md with agent definition", async () => {
|
|
121
|
-
const config: LogicalConfig = {
|
|
122
|
-
mcp_servers: [
|
|
123
|
-
{
|
|
124
|
-
ref: "workflows",
|
|
125
|
-
command: "npx",
|
|
126
|
-
args: ["-y", "@codemcp/workflows"],
|
|
127
|
-
env: {}
|
|
128
|
-
}
|
|
129
|
-
],
|
|
130
|
-
instructions: ["Follow TDD."],
|
|
131
|
-
cli_actions: [],
|
|
132
|
-
knowledge_sources: [],
|
|
133
|
-
skills: [],
|
|
134
|
-
git_hooks: [],
|
|
135
|
-
setup_notes: []
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
await copilotWriter.install(config, dir);
|
|
139
|
-
|
|
140
|
-
const content = await readFile(
|
|
141
|
-
join(dir, ".github", "agents", "ade.agent.md"),
|
|
142
|
-
"utf-8"
|
|
143
|
-
);
|
|
144
|
-
expect(content).toContain("name: ade");
|
|
145
|
-
expect(content).toContain("tools:");
|
|
146
|
-
expect(content).toContain(" - workflows/*");
|
|
147
|
-
expect(content).toContain("mcp-servers:");
|
|
148
|
-
expect(content).toContain(" workflows:");
|
|
149
|
-
expect(content).toContain(" type: stdio");
|
|
150
|
-
expect(content).toContain(' command: "npx"');
|
|
151
|
-
expect(content).toContain(' args: ["-y","@codemcp/workflows"]');
|
|
152
|
-
expect(content).toContain(' tools: ["*"]');
|
|
153
|
-
expect(content).toContain(" - read");
|
|
154
|
-
expect(content).toContain(" - edit");
|
|
155
|
-
expect(content).toContain(" - search");
|
|
156
|
-
expect(content).toContain(" - execute");
|
|
157
|
-
expect(content).toContain(" - agent");
|
|
158
|
-
expect(content).toContain(" - web");
|
|
159
|
-
expect(content).not.toContain("runCommands");
|
|
160
|
-
expect(content).not.toContain("runTasks");
|
|
161
|
-
expect(content).not.toContain("fetch");
|
|
162
|
-
expect(content).not.toContain("githubRepo");
|
|
163
|
-
expect(content).toContain("Follow TDD.");
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("derives the tools allowlist from autonomy while keeping web access approval-gated", async () => {
|
|
167
|
-
const rigidRoot = join(dir, "rigid");
|
|
168
|
-
const sensibleRoot = join(dir, "sensible");
|
|
169
|
-
const maxRoot = join(dir, "max");
|
|
170
|
-
|
|
171
|
-
const rigidConfig: LogicalConfig = {
|
|
172
|
-
mcp_servers: [
|
|
173
|
-
{
|
|
174
|
-
ref: "workflows",
|
|
175
|
-
command: "npx",
|
|
176
|
-
args: ["-y", "@codemcp/workflows"],
|
|
177
|
-
env: {}
|
|
178
|
-
}
|
|
179
|
-
],
|
|
180
|
-
instructions: [],
|
|
181
|
-
cli_actions: [],
|
|
182
|
-
knowledge_sources: [],
|
|
183
|
-
skills: [],
|
|
184
|
-
git_hooks: [],
|
|
185
|
-
setup_notes: [],
|
|
186
|
-
permission_policy: autonomyPolicy("rigid")
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
const sensibleConfig: LogicalConfig = {
|
|
190
|
-
...rigidConfig,
|
|
191
|
-
mcp_servers: [
|
|
192
|
-
{
|
|
193
|
-
ref: "workflows",
|
|
194
|
-
command: "npx",
|
|
195
|
-
args: ["-y", "@codemcp/workflows"],
|
|
196
|
-
env: {},
|
|
197
|
-
allowedTools: ["whats_next", "proceed_to_phase"]
|
|
198
|
-
}
|
|
199
|
-
],
|
|
200
|
-
permission_policy: autonomyPolicy("sensible-defaults")
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const maxConfig: LogicalConfig = {
|
|
204
|
-
...rigidConfig,
|
|
205
|
-
permission_policy: autonomyPolicy("max-autonomy")
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
await copilotWriter.install(rigidConfig, rigidRoot);
|
|
209
|
-
await copilotWriter.install(sensibleConfig, sensibleRoot);
|
|
210
|
-
await copilotWriter.install(maxConfig, maxRoot);
|
|
211
|
-
|
|
212
|
-
const rigidAgent = await readFile(
|
|
213
|
-
join(rigidRoot, ".github", "agents", "ade.agent.md"),
|
|
214
|
-
"utf-8"
|
|
215
|
-
);
|
|
216
|
-
const sensibleAgent = await readFile(
|
|
217
|
-
join(sensibleRoot, ".github", "agents", "ade.agent.md"),
|
|
218
|
-
"utf-8"
|
|
219
|
-
);
|
|
220
|
-
const maxAgent = await readFile(
|
|
221
|
-
join(maxRoot, ".github", "agents", "ade.agent.md"),
|
|
222
|
-
"utf-8"
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
expect(rigidAgent).not.toContain(" - server/workflows/*");
|
|
226
|
-
expect(rigidAgent).toContain(" - workflows/*");
|
|
227
|
-
expect(rigidAgent).not.toContain(" - read");
|
|
228
|
-
expect(rigidAgent).not.toContain(" - edit");
|
|
229
|
-
expect(rigidAgent).not.toContain(" - search");
|
|
230
|
-
expect(rigidAgent).not.toContain(" - execute");
|
|
231
|
-
expect(rigidAgent).not.toContain(" - agent");
|
|
232
|
-
expect(rigidAgent).not.toContain(" - web");
|
|
233
|
-
|
|
234
|
-
expect(sensibleAgent).toContain(" - read");
|
|
235
|
-
expect(sensibleAgent).toContain(" - edit");
|
|
236
|
-
expect(sensibleAgent).toContain(" - search");
|
|
237
|
-
expect(sensibleAgent).toContain(" - agent");
|
|
238
|
-
expect(sensibleAgent).not.toContain(" - execute");
|
|
239
|
-
expect(sensibleAgent).not.toContain(" - todo");
|
|
240
|
-
expect(sensibleAgent).not.toContain(" - web");
|
|
241
|
-
expect(sensibleAgent).toContain(" - workflows/whats_next");
|
|
242
|
-
expect(sensibleAgent).toContain(" - workflows/proceed_to_phase");
|
|
243
|
-
expect(sensibleAgent).not.toContain(" - workflows/*");
|
|
244
|
-
expect(sensibleAgent).toContain(
|
|
245
|
-
' tools: ["whats_next","proceed_to_phase"]'
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
expect(maxAgent).toContain(" - read");
|
|
249
|
-
expect(maxAgent).toContain(" - edit");
|
|
250
|
-
expect(maxAgent).toContain(" - search");
|
|
251
|
-
expect(maxAgent).toContain(" - execute");
|
|
252
|
-
expect(maxAgent).toContain(" - agent");
|
|
253
|
-
expect(maxAgent).toContain(" - todo");
|
|
254
|
-
expect(maxAgent).not.toContain(" - web");
|
|
255
|
-
expect(maxAgent).toContain(" - workflows/*");
|
|
256
|
-
expect(maxAgent).toContain("mcp-servers:");
|
|
257
|
-
});
|
|
258
|
-
});
|
package/src/writers/copilot.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
import type { LogicalConfig, McpServerEntry } from "@codemcp/ade-core";
|
|
3
|
-
import type { HarnessWriter } from "../types.js";
|
|
4
|
-
import {
|
|
5
|
-
writeMcpServers,
|
|
6
|
-
stdioEntry,
|
|
7
|
-
writeAgentMd,
|
|
8
|
-
writeGitHooks
|
|
9
|
-
} from "../util.js";
|
|
10
|
-
import {
|
|
11
|
-
allowsCapability,
|
|
12
|
-
hasPermissionPolicy,
|
|
13
|
-
keepsWebOnAsk
|
|
14
|
-
} from "../permission-policy.js";
|
|
15
|
-
|
|
16
|
-
export const copilotWriter: HarnessWriter = {
|
|
17
|
-
id: "copilot",
|
|
18
|
-
label: "GitHub Copilot",
|
|
19
|
-
description: "VS Code + CLI — .vscode/mcp.json + .github/agents/ade.agent.md",
|
|
20
|
-
async install(config: LogicalConfig, projectRoot: string) {
|
|
21
|
-
await writeMcpServers(config.mcp_servers, {
|
|
22
|
-
path: join(projectRoot, ".vscode", "mcp.json"),
|
|
23
|
-
key: "servers",
|
|
24
|
-
transform: stdioEntry
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const tools = [
|
|
28
|
-
...getBuiltInTools(config),
|
|
29
|
-
...getForwardedMcpTools(config.mcp_servers)
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
await writeAgentMd(config, {
|
|
33
|
-
path: join(projectRoot, ".github", "agents", "ade.agent.md"),
|
|
34
|
-
extraFrontmatter: [
|
|
35
|
-
"tools:",
|
|
36
|
-
...tools.map((t) => ` - ${t}`),
|
|
37
|
-
...renderCopilotAgentMcpServers(config.mcp_servers)
|
|
38
|
-
]
|
|
39
|
-
});
|
|
40
|
-
await writeGitHooks(config.git_hooks, projectRoot);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
function getBuiltInTools(config: LogicalConfig): string[] {
|
|
45
|
-
if (!hasPermissionPolicy(config)) {
|
|
46
|
-
return ["read", "edit", "search", "execute", "agent", "web"];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return [
|
|
50
|
-
...(allowsCapability(config, "read") ? ["read"] : []),
|
|
51
|
-
...(allowsCapability(config, "edit_write") ? ["edit"] : []),
|
|
52
|
-
...(allowsCapability(config, "search_list") ? ["search"] : []),
|
|
53
|
-
...(allowsCapability(config, "bash_unsafe") ? ["execute"] : []),
|
|
54
|
-
...(allowsCapability(config, "task_agent") ? ["agent"] : []),
|
|
55
|
-
...(allowsCapability(config, "task_agent") &&
|
|
56
|
-
allowsCapability(config, "bash_unsafe")
|
|
57
|
-
? ["todo"]
|
|
58
|
-
: []),
|
|
59
|
-
...(!keepsWebOnAsk(config) && allowsCapability(config, "web")
|
|
60
|
-
? ["web"]
|
|
61
|
-
: [])
|
|
62
|
-
];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function getForwardedMcpTools(servers: McpServerEntry[]): string[] {
|
|
66
|
-
return servers.flatMap((server) => {
|
|
67
|
-
const allowedTools = server.allowedTools ?? ["*"];
|
|
68
|
-
if (allowedTools.includes("*")) {
|
|
69
|
-
return [`${server.ref}/*`];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return allowedTools.map((tool) => `${server.ref}/${tool}`);
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function renderCopilotAgentMcpServers(servers: McpServerEntry[]): string[] {
|
|
77
|
-
if (servers.length === 0) {
|
|
78
|
-
return [];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const lines = ["mcp-servers:"];
|
|
82
|
-
|
|
83
|
-
for (const server of servers) {
|
|
84
|
-
lines.push(` ${formatYamlKey(server.ref)}:`);
|
|
85
|
-
lines.push(" type: stdio");
|
|
86
|
-
lines.push(` command: ${JSON.stringify(server.command)}`);
|
|
87
|
-
lines.push(` args: ${JSON.stringify(server.args)}`);
|
|
88
|
-
lines.push(` tools: ${JSON.stringify(server.allowedTools ?? ["*"])}`);
|
|
89
|
-
|
|
90
|
-
if (Object.keys(server.env).length > 0) {
|
|
91
|
-
lines.push(" env:");
|
|
92
|
-
for (const [key, value] of Object.entries(server.env)) {
|
|
93
|
-
lines.push(` ${formatYamlKey(key)}: ${JSON.stringify(value)}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return lines;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function formatYamlKey(value: string): string {
|
|
102
|
-
return /^[A-Za-z_][A-Za-z0-9_-]*$/.test(value)
|
|
103
|
-
? value
|
|
104
|
-
: JSON.stringify(value);
|
|
105
|
-
}
|