@codemcp/ade-harnesses 0.0.2 → 0.1.1
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-format.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +15 -12
- package/.turbo/turbo-typecheck.log +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/permission-policy.d.ts +7 -0
- package/dist/permission-policy.js +152 -0
- package/dist/util.d.ts +1 -1
- package/dist/util.js +16 -2
- package/dist/writers/claude-code.js +51 -20
- package/dist/writers/cline.js +2 -2
- package/dist/writers/copilot.js +61 -8
- package/dist/writers/cursor.js +48 -2
- package/dist/writers/kiro.js +54 -38
- package/dist/writers/opencode.js +26 -23
- package/dist/writers/roo-code.js +38 -2
- package/dist/writers/universal.js +41 -3
- package/dist/writers/windsurf.js +43 -1
- package/package.json +2 -2
- package/src/index.ts +1 -0
- package/src/permission-policy.ts +173 -0
- package/src/util.ts +20 -7
- package/src/writers/claude-code.spec.ts +163 -5
- package/src/writers/claude-code.ts +63 -20
- package/src/writers/cline.spec.ts +146 -3
- package/src/writers/cline.ts +2 -2
- package/src/writers/copilot.spec.ts +157 -1
- package/src/writers/copilot.ts +76 -9
- package/src/writers/cursor.spec.ts +104 -1
- package/src/writers/cursor.ts +65 -3
- package/src/writers/kiro.spec.ts +228 -0
- package/src/writers/kiro.ts +77 -40
- package/src/writers/opencode.spec.ts +258 -0
- package/src/writers/opencode.ts +40 -27
- package/src/writers/roo-code.spec.ts +129 -1
- package/src/writers/roo-code.ts +49 -2
- package/src/writers/universal.spec.ts +134 -0
- package/src/writers/universal.ts +57 -4
- package/src/writers/windsurf.spec.ts +111 -3
- package/src/writers/windsurf.ts +64 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -2,9 +2,57 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
|
2
2
|
import { mkdtemp, rm, readFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
AutonomyProfile,
|
|
7
|
+
LogicalConfig,
|
|
8
|
+
PermissionPolicy
|
|
9
|
+
} from "@codemcp/ade-core";
|
|
6
10
|
import { cursorWriter } from "./cursor.js";
|
|
7
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
|
+
|
|
8
56
|
describe("cursorWriter", () => {
|
|
9
57
|
let dir: string;
|
|
10
58
|
|
|
@@ -71,6 +119,61 @@ describe("cursorWriter", () => {
|
|
|
71
119
|
expect(content).toContain("Use conventional commits.");
|
|
72
120
|
});
|
|
73
121
|
|
|
122
|
+
it("documents autonomy limits in Cursor rules without inventing built-in permission config", async () => {
|
|
123
|
+
const config: LogicalConfig = {
|
|
124
|
+
mcp_servers: [
|
|
125
|
+
{
|
|
126
|
+
ref: "workflows",
|
|
127
|
+
command: "npx",
|
|
128
|
+
args: ["-y", "@codemcp/workflows"],
|
|
129
|
+
env: {},
|
|
130
|
+
allowedTools: ["whats_next"]
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
instructions: [],
|
|
134
|
+
cli_actions: [],
|
|
135
|
+
knowledge_sources: [],
|
|
136
|
+
skills: [],
|
|
137
|
+
git_hooks: [],
|
|
138
|
+
setup_notes: [],
|
|
139
|
+
permission_policy: autonomyPolicy("sensible-defaults")
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
await cursorWriter.install(config, dir);
|
|
143
|
+
|
|
144
|
+
const content = await readFile(
|
|
145
|
+
join(dir, ".cursor", "rules", "ade.mdc"),
|
|
146
|
+
"utf-8"
|
|
147
|
+
);
|
|
148
|
+
expect(content).toContain(
|
|
149
|
+
"Cursor autonomy note (documented, not enforced): sensible-defaults."
|
|
150
|
+
);
|
|
151
|
+
expect(content).toContain(
|
|
152
|
+
"Cursor has no verified committed project-local built-in ask/allow/deny config surface"
|
|
153
|
+
);
|
|
154
|
+
expect(content).toContain(
|
|
155
|
+
"Prefer handling these built-in capabilities without extra approval when Cursor permits it: read project files, edit and write project files, search and list project contents, run safe local shell commands, delegate or decompose work into agent tasks."
|
|
156
|
+
);
|
|
157
|
+
expect(content).toContain(
|
|
158
|
+
"Request approval before these capabilities: run high-impact shell commands, use web or network access."
|
|
159
|
+
);
|
|
160
|
+
expect(content).toContain(
|
|
161
|
+
"Web and network access must remain approval-gated."
|
|
162
|
+
);
|
|
163
|
+
expect(content).toContain(
|
|
164
|
+
"MCP server registration stays in .cursor/mcp.json; MCP tool approvals remain owned by provisioning"
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const raw = await readFile(join(dir, ".cursor", "mcp.json"), "utf-8");
|
|
168
|
+
const parsed = JSON.parse(raw);
|
|
169
|
+
expect(parsed).not.toHaveProperty("permissions");
|
|
170
|
+
expect(parsed.mcpServers["workflows"]).toEqual({
|
|
171
|
+
command: "npx",
|
|
172
|
+
args: ["-y", "@codemcp/workflows"]
|
|
173
|
+
});
|
|
174
|
+
expect(parsed.mcpServers["workflows"]).not.toHaveProperty("allowedTools");
|
|
175
|
+
});
|
|
176
|
+
|
|
74
177
|
it("includes agentskills server from mcp_servers", async () => {
|
|
75
178
|
const config: LogicalConfig = {
|
|
76
179
|
mcp_servers: [
|
package/src/writers/cursor.ts
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import type { LogicalConfig } from "@codemcp/ade-core";
|
|
3
|
+
import type { AutonomyCapability, LogicalConfig } from "@codemcp/ade-core";
|
|
4
4
|
import type { HarnessWriter } from "../types.js";
|
|
5
5
|
import { writeMcpServers, writeGitHooks } from "../util.js";
|
|
6
|
+
import {
|
|
7
|
+
getAutonomyProfile,
|
|
8
|
+
getCapabilityDecision,
|
|
9
|
+
hasPermissionPolicy
|
|
10
|
+
} from "../permission-policy.js";
|
|
11
|
+
|
|
12
|
+
const CURSOR_CAPABILITY_ORDER: AutonomyCapability[] = [
|
|
13
|
+
"read",
|
|
14
|
+
"edit_write",
|
|
15
|
+
"search_list",
|
|
16
|
+
"bash_safe",
|
|
17
|
+
"bash_unsafe",
|
|
18
|
+
"web",
|
|
19
|
+
"task_agent"
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const CURSOR_CAPABILITY_LABELS: Record<AutonomyCapability, string> = {
|
|
23
|
+
read: "read project files",
|
|
24
|
+
edit_write: "edit and write project files",
|
|
25
|
+
search_list: "search and list project contents",
|
|
26
|
+
bash_safe: "run safe local shell commands",
|
|
27
|
+
bash_unsafe: "run high-impact shell commands",
|
|
28
|
+
web: "use web or network access",
|
|
29
|
+
task_agent: "delegate or decompose work into agent tasks"
|
|
30
|
+
};
|
|
6
31
|
|
|
7
32
|
export const cursorWriter: HarnessWriter = {
|
|
8
33
|
id: "cursor",
|
|
@@ -13,7 +38,9 @@ export const cursorWriter: HarnessWriter = {
|
|
|
13
38
|
path: join(projectRoot, ".cursor", "mcp.json")
|
|
14
39
|
});
|
|
15
40
|
|
|
16
|
-
|
|
41
|
+
const rulesBody = getCursorRulesBody(config);
|
|
42
|
+
|
|
43
|
+
if (rulesBody.length > 0) {
|
|
17
44
|
const rulesDir = join(projectRoot, ".cursor", "rules");
|
|
18
45
|
await mkdir(rulesDir, { recursive: true });
|
|
19
46
|
|
|
@@ -23,7 +50,7 @@ export const cursorWriter: HarnessWriter = {
|
|
|
23
50
|
"globs: *",
|
|
24
51
|
"---",
|
|
25
52
|
"",
|
|
26
|
-
...
|
|
53
|
+
...rulesBody.flatMap((line) => [line, ""])
|
|
27
54
|
].join("\n");
|
|
28
55
|
|
|
29
56
|
await writeFile(join(rulesDir, "ade.mdc"), content, "utf-8");
|
|
@@ -31,3 +58,38 @@ export const cursorWriter: HarnessWriter = {
|
|
|
31
58
|
await writeGitHooks(config.git_hooks, projectRoot);
|
|
32
59
|
}
|
|
33
60
|
};
|
|
61
|
+
|
|
62
|
+
function getCursorRulesBody(config: LogicalConfig): string[] {
|
|
63
|
+
return [...config.instructions, ...getCursorAutonomyNotes(config)];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getCursorAutonomyNotes(config: LogicalConfig): string[] {
|
|
67
|
+
if (!hasPermissionPolicy(config)) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const allowedCapabilities = CURSOR_CAPABILITY_ORDER.filter(
|
|
72
|
+
(capability) => getCapabilityDecision(config, capability) === "allow"
|
|
73
|
+
).map((capability) => CURSOR_CAPABILITY_LABELS[capability]);
|
|
74
|
+
|
|
75
|
+
const approvalGatedCapabilities = CURSOR_CAPABILITY_ORDER.filter(
|
|
76
|
+
(capability) => getCapabilityDecision(config, capability) === "ask"
|
|
77
|
+
).map((capability) => CURSOR_CAPABILITY_LABELS[capability]);
|
|
78
|
+
|
|
79
|
+
return [
|
|
80
|
+
`Cursor autonomy note (documented, not enforced): ${getAutonomyProfile(config) ?? "custom"}.`,
|
|
81
|
+
"Cursor has no verified committed project-local built-in ask/allow/deny config surface, so ADE documents autonomy intent here instead of writing unsupported permission config.",
|
|
82
|
+
...(allowedCapabilities.length > 0
|
|
83
|
+
? [
|
|
84
|
+
`Prefer handling these built-in capabilities without extra approval when Cursor permits it: ${allowedCapabilities.join(", ")}.`
|
|
85
|
+
]
|
|
86
|
+
: []),
|
|
87
|
+
...(approvalGatedCapabilities.length > 0
|
|
88
|
+
? [
|
|
89
|
+
`Request approval before these capabilities: ${approvalGatedCapabilities.join(", ")}.`
|
|
90
|
+
]
|
|
91
|
+
: []),
|
|
92
|
+
"Web and network access must remain approval-gated.",
|
|
93
|
+
"MCP server registration stays in .cursor/mcp.json; MCP tool approvals remain owned by provisioning and are not enforced or re-modeled in this rules file."
|
|
94
|
+
];
|
|
95
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
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 { kiroWriter } from "./kiro.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("kiroWriter", () => {
|
|
57
|
+
let dir: string;
|
|
58
|
+
|
|
59
|
+
beforeEach(async () => {
|
|
60
|
+
dir = await mkdtemp(join(tmpdir(), "ade-harness-kiro-"));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(async () => {
|
|
64
|
+
await rm(dir, { recursive: true, force: true });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("has correct metadata", () => {
|
|
68
|
+
expect(kiroWriter.id).toBe("kiro");
|
|
69
|
+
expect(kiroWriter.label).toBe("Kiro");
|
|
70
|
+
expect(kiroWriter.description).toContain(".kiro/agents/ade.json");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("writes a JSON Kiro agent with documented built-in tool selectors", async () => {
|
|
74
|
+
const config: LogicalConfig = {
|
|
75
|
+
mcp_servers: [
|
|
76
|
+
{
|
|
77
|
+
ref: "workflows",
|
|
78
|
+
command: "npx",
|
|
79
|
+
args: ["-y", "@codemcp/workflows"],
|
|
80
|
+
env: {}
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
instructions: ["Use project workflows."],
|
|
84
|
+
cli_actions: [],
|
|
85
|
+
knowledge_sources: [],
|
|
86
|
+
skills: [],
|
|
87
|
+
git_hooks: [],
|
|
88
|
+
setup_notes: []
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
await kiroWriter.install(config, dir);
|
|
92
|
+
|
|
93
|
+
const raw = await readFile(
|
|
94
|
+
join(dir, ".kiro", "agents", "ade.json"),
|
|
95
|
+
"utf-8"
|
|
96
|
+
);
|
|
97
|
+
const content = JSON.parse(raw);
|
|
98
|
+
|
|
99
|
+
expect(content.name).toBe("ade");
|
|
100
|
+
expect(content.mcpServers.workflows).toEqual({
|
|
101
|
+
command: "npx",
|
|
102
|
+
args: ["-y", "@codemcp/workflows"],
|
|
103
|
+
autoApprove: ["*"]
|
|
104
|
+
});
|
|
105
|
+
expect(content.tools).toEqual([
|
|
106
|
+
"read",
|
|
107
|
+
"write",
|
|
108
|
+
"shell",
|
|
109
|
+
"spec",
|
|
110
|
+
"@workflows/*"
|
|
111
|
+
]);
|
|
112
|
+
expect(content.allowedTools).toEqual([
|
|
113
|
+
"read",
|
|
114
|
+
"write",
|
|
115
|
+
"shell",
|
|
116
|
+
"spec",
|
|
117
|
+
"@workflows/*"
|
|
118
|
+
]);
|
|
119
|
+
expect(content.useLegacyMcpJson).toBe(true);
|
|
120
|
+
expect(content.tools).not.toContain("@workflows");
|
|
121
|
+
expect(content.prompt).toContain("Use project workflows.");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("writes Kiro MCP settings and forwards provisioning trust via autoApprove", async () => {
|
|
125
|
+
const config: LogicalConfig = {
|
|
126
|
+
mcp_servers: [
|
|
127
|
+
{
|
|
128
|
+
ref: "workflows",
|
|
129
|
+
command: "npx",
|
|
130
|
+
args: ["-y", "@codemcp/workflows"],
|
|
131
|
+
env: { NODE_ENV: "test" },
|
|
132
|
+
allowedTools: ["use_skill", "whats_next"]
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
instructions: [],
|
|
136
|
+
cli_actions: [],
|
|
137
|
+
knowledge_sources: [],
|
|
138
|
+
skills: [],
|
|
139
|
+
git_hooks: [],
|
|
140
|
+
setup_notes: []
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
await kiroWriter.install(config, dir);
|
|
144
|
+
|
|
145
|
+
const raw = await readFile(
|
|
146
|
+
join(dir, ".kiro", "settings", "mcp.json"),
|
|
147
|
+
"utf-8"
|
|
148
|
+
);
|
|
149
|
+
const parsed = JSON.parse(raw);
|
|
150
|
+
|
|
151
|
+
expect(parsed.mcpServers.workflows).toEqual({
|
|
152
|
+
command: "npx",
|
|
153
|
+
args: ["-y", "@codemcp/workflows"],
|
|
154
|
+
env: { NODE_ENV: "test" },
|
|
155
|
+
autoApprove: ["use_skill", "whats_next"]
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("maps autonomy only to built-in selectors and keeps web approval-gated", async () => {
|
|
160
|
+
const rigidRoot = join(dir, "rigid");
|
|
161
|
+
const maxRoot = join(dir, "max");
|
|
162
|
+
|
|
163
|
+
const baseConfig = {
|
|
164
|
+
mcp_servers: [
|
|
165
|
+
{
|
|
166
|
+
ref: "workflows",
|
|
167
|
+
command: "npx",
|
|
168
|
+
args: ["-y", "@codemcp/workflows"],
|
|
169
|
+
env: {},
|
|
170
|
+
allowedTools: ["*"]
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
instructions: [],
|
|
174
|
+
cli_actions: [],
|
|
175
|
+
knowledge_sources: [],
|
|
176
|
+
skills: [],
|
|
177
|
+
git_hooks: [],
|
|
178
|
+
setup_notes: []
|
|
179
|
+
} satisfies LogicalConfig;
|
|
180
|
+
|
|
181
|
+
const rigidConfig: LogicalConfig = {
|
|
182
|
+
...baseConfig,
|
|
183
|
+
permission_policy: autonomyPolicy("rigid")
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const maxConfig: LogicalConfig = {
|
|
187
|
+
...baseConfig,
|
|
188
|
+
permission_policy: autonomyPolicy("max-autonomy")
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
await kiroWriter.install(rigidConfig, rigidRoot);
|
|
192
|
+
await kiroWriter.install(maxConfig, maxRoot);
|
|
193
|
+
|
|
194
|
+
const rigidAgent = JSON.parse(
|
|
195
|
+
await readFile(join(rigidRoot, ".kiro", "agents", "ade.json"), "utf-8")
|
|
196
|
+
);
|
|
197
|
+
const maxAgent = JSON.parse(
|
|
198
|
+
await readFile(join(maxRoot, ".kiro", "agents", "ade.json"), "utf-8")
|
|
199
|
+
);
|
|
200
|
+
const rigidMcp = JSON.parse(
|
|
201
|
+
await readFile(join(rigidRoot, ".kiro", "settings", "mcp.json"), "utf-8")
|
|
202
|
+
);
|
|
203
|
+
const maxMcp = JSON.parse(
|
|
204
|
+
await readFile(join(maxRoot, ".kiro", "settings", "mcp.json"), "utf-8")
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
expect(rigidAgent.tools).toContain("read");
|
|
208
|
+
expect(rigidAgent.tools).toContain("spec");
|
|
209
|
+
expect(rigidAgent.tools).toContain("@workflows/*");
|
|
210
|
+
expect(rigidAgent.allowedTools).toContain("@workflows/*");
|
|
211
|
+
expect(rigidAgent.mcpServers.workflows.autoApprove).toEqual(["*"]);
|
|
212
|
+
expect(rigidAgent.tools).not.toContain("write");
|
|
213
|
+
expect(rigidAgent.tools).not.toContain("shell");
|
|
214
|
+
expect(rigidAgent.tools).not.toContain("web");
|
|
215
|
+
|
|
216
|
+
expect(maxAgent.tools).toContain("read");
|
|
217
|
+
expect(maxAgent.tools).toContain("write");
|
|
218
|
+
expect(maxAgent.tools).toContain("shell");
|
|
219
|
+
expect(maxAgent.tools).toContain("spec");
|
|
220
|
+
expect(maxAgent.tools).toContain("@workflows/*");
|
|
221
|
+
expect(maxAgent.allowedTools).toContain("@workflows/*");
|
|
222
|
+
expect(maxAgent.mcpServers.workflows.autoApprove).toEqual(["*"]);
|
|
223
|
+
expect(maxAgent.tools).not.toContain("web");
|
|
224
|
+
|
|
225
|
+
expect(rigidMcp.mcpServers.workflows.autoApprove).toEqual(["*"]);
|
|
226
|
+
expect(maxMcp.mcpServers.workflows.autoApprove).toEqual(["*"]);
|
|
227
|
+
});
|
|
228
|
+
});
|
package/src/writers/kiro.ts
CHANGED
|
@@ -1,52 +1,89 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import type { LogicalConfig } from "@codemcp/ade-core";
|
|
2
|
+
import type { LogicalConfig, McpServerEntry } from "@codemcp/ade-core";
|
|
3
3
|
import type { HarnessWriter } from "../types.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
standardEntry,
|
|
6
|
+
writeGitHooks,
|
|
7
|
+
writeJson,
|
|
8
|
+
writeMcpServers
|
|
9
|
+
} from "../util.js";
|
|
10
|
+
import {
|
|
11
|
+
allowsCapability,
|
|
12
|
+
getCapabilityDecision,
|
|
13
|
+
hasPermissionPolicy
|
|
14
|
+
} from "../permission-policy.js";
|
|
5
15
|
|
|
6
16
|
export const kiroWriter: HarnessWriter = {
|
|
7
17
|
id: "kiro",
|
|
8
18
|
label: "Kiro",
|
|
9
|
-
description: "AWS AI IDE — .kiro/agents/ade.json",
|
|
19
|
+
description: "AWS AI IDE — .kiro/agents/ade.json + .kiro/settings/mcp.json",
|
|
10
20
|
async install(config: LogicalConfig, projectRoot: string) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
21
|
+
await writeMcpServers(config.mcp_servers, {
|
|
22
|
+
path: join(projectRoot, ".kiro", "settings", "mcp.json"),
|
|
23
|
+
transform: (server) => ({
|
|
24
|
+
...standardEntry(server),
|
|
25
|
+
autoApprove: server.allowedTools ?? ["*"]
|
|
26
|
+
})
|
|
27
|
+
});
|
|
17
28
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (explicit && !explicit.includes("*")) {
|
|
31
|
-
for (const tool of explicit) {
|
|
32
|
-
allowedTools.push(`@${s.ref}/${tool}`);
|
|
33
|
-
}
|
|
34
|
-
} else {
|
|
35
|
-
allowedTools.push(`@${s.ref}/*`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
29
|
+
await writeJson(join(projectRoot, ".kiro", "agents", "ade.json"), {
|
|
30
|
+
name: "ade",
|
|
31
|
+
description:
|
|
32
|
+
"ADE — Agentic Development Environment agent with project conventions and tools.",
|
|
33
|
+
prompt:
|
|
34
|
+
config.instructions.join("\n\n") ||
|
|
35
|
+
"ADE — Agentic Development Environment agent.",
|
|
36
|
+
mcpServers: getKiroAgentMcpServers(config.mcp_servers),
|
|
37
|
+
tools: getKiroTools(config),
|
|
38
|
+
allowedTools: getKiroAllowedTools(config),
|
|
39
|
+
useLegacyMcpJson: true
|
|
40
|
+
});
|
|
38
41
|
|
|
39
|
-
await writeJson(join(projectRoot, ".kiro", "agents", "ade.json"), {
|
|
40
|
-
name: "ade",
|
|
41
|
-
prompt:
|
|
42
|
-
config.instructions.length > 0
|
|
43
|
-
? config.instructions.join("\n\n")
|
|
44
|
-
: "ADE — Agentic Development Environment agent",
|
|
45
|
-
mcpServers,
|
|
46
|
-
tools,
|
|
47
|
-
allowedTools
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
42
|
await writeGitHooks(config.git_hooks, projectRoot);
|
|
51
43
|
}
|
|
52
44
|
};
|
|
45
|
+
|
|
46
|
+
function getKiroTools(config: LogicalConfig): string[] {
|
|
47
|
+
const mcpTools = getKiroForwardedMcpTools(config.mcp_servers);
|
|
48
|
+
|
|
49
|
+
if (!hasPermissionPolicy(config)) {
|
|
50
|
+
return ["read", "write", "shell", "spec", ...mcpTools];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return [
|
|
54
|
+
...(getCapabilityDecision(config, "read") !== "deny" ? ["read"] : []),
|
|
55
|
+
...(allowsCapability(config, "edit_write") ? ["write"] : []),
|
|
56
|
+
...(allowsCapability(config, "bash_unsafe") ? ["shell"] : []),
|
|
57
|
+
"spec",
|
|
58
|
+
...mcpTools
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getKiroAllowedTools(config: LogicalConfig): string[] {
|
|
63
|
+
return getKiroTools(config);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getKiroForwardedMcpTools(servers: McpServerEntry[]): string[] {
|
|
67
|
+
return servers.flatMap((server) => {
|
|
68
|
+
const allowedTools = server.allowedTools ?? ["*"];
|
|
69
|
+
if (allowedTools.includes("*")) {
|
|
70
|
+
return [`@${server.ref}/*`];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return allowedTools.map((tool) => `@${server.ref}/${tool}`);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getKiroAgentMcpServers(
|
|
78
|
+
servers: McpServerEntry[]
|
|
79
|
+
): Record<string, Record<string, unknown>> {
|
|
80
|
+
return Object.fromEntries(
|
|
81
|
+
servers.map((server) => [
|
|
82
|
+
server.ref,
|
|
83
|
+
{
|
|
84
|
+
...standardEntry(server),
|
|
85
|
+
autoApprove: server.allowedTools ?? ["*"]
|
|
86
|
+
}
|
|
87
|
+
])
|
|
88
|
+
);
|
|
89
|
+
}
|