@codemcp/ade-harnesses 0.0.2

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.
Files changed (60) hide show
  1. package/.prettierignore +1 -0
  2. package/.turbo/turbo-build.log +4 -0
  3. package/.turbo/turbo-format.log +6 -0
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +20 -0
  6. package/.turbo/turbo-typecheck.log +4 -0
  7. package/LICENSE +21 -0
  8. package/dist/index.d.ts +18 -0
  9. package/dist/index.js +39 -0
  10. package/dist/skills-installer.d.ts +13 -0
  11. package/dist/skills-installer.js +46 -0
  12. package/dist/types.d.ts +11 -0
  13. package/dist/types.js +1 -0
  14. package/dist/util.d.ts +53 -0
  15. package/dist/util.js +130 -0
  16. package/dist/writers/claude-code.d.ts +2 -0
  17. package/dist/writers/claude-code.js +45 -0
  18. package/dist/writers/cline.d.ts +2 -0
  19. package/dist/writers/cline.js +15 -0
  20. package/dist/writers/copilot.d.ts +2 -0
  21. package/dist/writers/copilot.js +28 -0
  22. package/dist/writers/cursor.d.ts +2 -0
  23. package/dist/writers/cursor.js +27 -0
  24. package/dist/writers/kiro.d.ts +2 -0
  25. package/dist/writers/kiro.js +46 -0
  26. package/dist/writers/opencode.d.ts +2 -0
  27. package/dist/writers/opencode.js +45 -0
  28. package/dist/writers/roo-code.d.ts +2 -0
  29. package/dist/writers/roo-code.js +15 -0
  30. package/dist/writers/universal.d.ts +2 -0
  31. package/dist/writers/universal.js +22 -0
  32. package/dist/writers/windsurf.d.ts +2 -0
  33. package/dist/writers/windsurf.js +15 -0
  34. package/eslint.config.mjs +40 -0
  35. package/package.json +35 -0
  36. package/src/index.spec.ts +45 -0
  37. package/src/index.ts +46 -0
  38. package/src/skills-installer.ts +54 -0
  39. package/src/types.ts +12 -0
  40. package/src/util.ts +208 -0
  41. package/src/writers/claude-code.spec.ts +162 -0
  42. package/src/writers/claude-code.ts +64 -0
  43. package/src/writers/cline.spec.ts +69 -0
  44. package/src/writers/cline.ts +24 -0
  45. package/src/writers/copilot.spec.ts +102 -0
  46. package/src/writers/copilot.ts +38 -0
  47. package/src/writers/cursor.spec.ts +116 -0
  48. package/src/writers/cursor.ts +33 -0
  49. package/src/writers/kiro.ts +52 -0
  50. package/src/writers/opencode.ts +54 -0
  51. package/src/writers/roo-code.spec.ts +69 -0
  52. package/src/writers/roo-code.ts +24 -0
  53. package/src/writers/universal.ts +31 -0
  54. package/src/writers/windsurf.spec.ts +70 -0
  55. package/src/writers/windsurf.ts +27 -0
  56. package/tsconfig.build.json +8 -0
  57. package/tsconfig.json +7 -0
  58. package/tsconfig.tsbuildinfo +1 -0
  59. package/tsconfig.vitest.json +7 -0
  60. package/vitest.config.ts +5 -0
@@ -0,0 +1 @@
1
+ dist
@@ -0,0 +1,4 @@
1
+
2
+ > @codemcp/ade-harnesses@0.0.2 build /home/runner/work/ade/ade/packages/harnesses
3
+ > tsc -p tsconfig.build.json
4
+
@@ -0,0 +1,6 @@
1
+
2
+ > @codemcp/ade-harnesses@0.0.2 format /home/runner/work/ade/ade/packages/harnesses
3
+ > prettier --check .
4
+
5
+ Checking formatting...
6
+ All matched files use Prettier code style!
@@ -0,0 +1,4 @@
1
+
2
+ > @codemcp/ade-harnesses@0.0.2 lint /home/runner/work/ade/ade/packages/harnesses
3
+ > eslint .
4
+
@@ -0,0 +1,20 @@
1
+
2
+ > @codemcp/ade-harnesses@0.0.2 test /home/runner/work/ade/ade/packages/harnesses
3
+ > vitest --run
4
+
5
+
6
+  RUN  v3.2.4 /home/runner/work/ade/ade/packages/harnesses
7
+
8
+ ✓ src/writers/copilot.spec.ts (4 tests) 55ms
9
+ ✓ src/writers/cursor.spec.ts (5 tests) 82ms
10
+ ✓ src/writers/claude-code.spec.ts (6 tests) 107ms
11
+ ✓ src/writers/roo-code.spec.ts (3 tests) 31ms
12
+ ✓ src/writers/windsurf.spec.ts (3 tests) 82ms
13
+ ✓ src/writers/cline.spec.ts (3 tests) 82ms
14
+ ✓ src/index.spec.ts (4 tests) 9ms
15
+
16
+  Test Files  7 passed (7)
17
+  Tests  28 passed (28)
18
+  Start at  06:53:29
19
+  Duration  3.26s (transform 457ms, setup 0ms, collect 1.32s, tests 448ms, environment 4ms, prepare 2.22s)
20
+
@@ -0,0 +1,4 @@
1
+
2
+ > @codemcp/ade-harnesses@0.0.2 typecheck /home/runner/work/ade/ade/packages/harnesses
3
+ > tsc
4
+
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Luke Baker
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,18 @@
1
+ export type { HarnessWriter } from "./types.js";
2
+ export { installSkills } from "./skills-installer.js";
3
+ export { universalWriter } from "./writers/universal.js";
4
+ export { claudeCodeWriter } from "./writers/claude-code.js";
5
+ export { cursorWriter } from "./writers/cursor.js";
6
+ export { copilotWriter } from "./writers/copilot.js";
7
+ export { windsurfWriter } from "./writers/windsurf.js";
8
+ export { clineWriter } from "./writers/cline.js";
9
+ export { rooCodeWriter } from "./writers/roo-code.js";
10
+ export { kiroWriter } from "./writers/kiro.js";
11
+ export { opencodeWriter } from "./writers/opencode.js";
12
+ import type { HarnessWriter } from "./types.js";
13
+ /** All built-in harness writers, ordered for wizard display. */
14
+ export declare const allHarnessWriters: HarnessWriter[];
15
+ /** Look up a harness writer by id. */
16
+ export declare function getHarnessWriter(id: string): HarnessWriter | undefined;
17
+ /** All valid harness IDs. */
18
+ export declare function getHarnessIds(): string[];
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
1
+ export { installSkills } from "./skills-installer.js";
2
+ export { universalWriter } from "./writers/universal.js";
3
+ export { claudeCodeWriter } from "./writers/claude-code.js";
4
+ export { cursorWriter } from "./writers/cursor.js";
5
+ export { copilotWriter } from "./writers/copilot.js";
6
+ export { windsurfWriter } from "./writers/windsurf.js";
7
+ export { clineWriter } from "./writers/cline.js";
8
+ export { rooCodeWriter } from "./writers/roo-code.js";
9
+ export { kiroWriter } from "./writers/kiro.js";
10
+ export { opencodeWriter } from "./writers/opencode.js";
11
+ import { universalWriter } from "./writers/universal.js";
12
+ import { claudeCodeWriter } from "./writers/claude-code.js";
13
+ import { cursorWriter } from "./writers/cursor.js";
14
+ import { copilotWriter } from "./writers/copilot.js";
15
+ import { windsurfWriter } from "./writers/windsurf.js";
16
+ import { clineWriter } from "./writers/cline.js";
17
+ import { rooCodeWriter } from "./writers/roo-code.js";
18
+ import { kiroWriter } from "./writers/kiro.js";
19
+ import { opencodeWriter } from "./writers/opencode.js";
20
+ /** All built-in harness writers, ordered for wizard display. */
21
+ export const allHarnessWriters = [
22
+ universalWriter,
23
+ claudeCodeWriter,
24
+ cursorWriter,
25
+ copilotWriter,
26
+ windsurfWriter,
27
+ clineWriter,
28
+ rooCodeWriter,
29
+ kiroWriter,
30
+ opencodeWriter
31
+ ];
32
+ /** Look up a harness writer by id. */
33
+ export function getHarnessWriter(id) {
34
+ return allHarnessWriters.find((w) => w.id === id);
35
+ }
36
+ /** All valid harness IDs. */
37
+ export function getHarnessIds() {
38
+ return allHarnessWriters.map((w) => w.id);
39
+ }
@@ -0,0 +1,13 @@
1
+ import type { SkillDefinition } from "@codemcp/ade-core";
2
+ /**
3
+ * Install skills using the @codemcp/skills programmatic API.
4
+ *
5
+ * Inline skills are expected to already exist as SKILL.md files under
6
+ * `<projectRoot>/.ade/skills/<name>/` (written by the agent writer).
7
+ * This function calls `runAdd` with the local path for inline skills
8
+ * and the remote source for external skills.
9
+ *
10
+ * Note: `runAdd` uses `process.cwd()` to determine the install destination.
11
+ * This function changes cwd to `projectRoot` before calling `runAdd`.
12
+ */
13
+ export declare function installSkills(skills: SkillDefinition[], projectRoot: string): Promise<void>;
@@ -0,0 +1,46 @@
1
+ import { join } from "node:path";
2
+ import { runAdd } from "@codemcp/skills/api";
3
+ function isInlineSkill(skill) {
4
+ return "body" in skill;
5
+ }
6
+ /**
7
+ * Install skills using the @codemcp/skills programmatic API.
8
+ *
9
+ * Inline skills are expected to already exist as SKILL.md files under
10
+ * `<projectRoot>/.ade/skills/<name>/` (written by the agent writer).
11
+ * This function calls `runAdd` with the local path for inline skills
12
+ * and the remote source for external skills.
13
+ *
14
+ * Note: `runAdd` uses `process.cwd()` to determine the install destination.
15
+ * This function changes cwd to `projectRoot` before calling `runAdd`.
16
+ */
17
+ export async function installSkills(skills, projectRoot) {
18
+ if (skills.length === 0)
19
+ return;
20
+ const originalCwd = process.cwd();
21
+ process.chdir(projectRoot);
22
+ try {
23
+ for (const skill of skills) {
24
+ const source = isInlineSkill(skill)
25
+ ? join(projectRoot, ".ade", "skills", skill.name)
26
+ : skill.source;
27
+ try {
28
+ await runAdd([source], { yes: true, all: true });
29
+ }
30
+ catch (err) {
31
+ // runAdd may throw on network errors for external skills.
32
+ // Log and continue — inline skills should always succeed.
33
+ console.warn(`Warning: failed to install skill "${skill.name}" from ${source}:`, err instanceof Error ? err.message : err);
34
+ }
35
+ }
36
+ }
37
+ finally {
38
+ // Restore cwd only if the original directory still exists
39
+ try {
40
+ process.chdir(originalCwd);
41
+ }
42
+ catch {
43
+ // Original cwd may have been removed (e.g. in tests)
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,11 @@
1
+ import type { AgentWriterDef } from "@codemcp/ade-core";
2
+ /**
3
+ * A harness writer extends AgentWriterDef with metadata for display in the
4
+ * setup wizard and CLI help.
5
+ */
6
+ export interface HarnessWriter extends AgentWriterDef {
7
+ /** Human-readable label for the wizard (e.g. "Claude Code") */
8
+ label: string;
9
+ /** Short description shown as hint in the wizard */
10
+ description: string;
11
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/util.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ import type { GitHook, LogicalConfig, McpServerEntry } from "@codemcp/ade-core";
2
+ /** Read a JSON file, returning `{}` if missing or unparseable. */
3
+ export declare function readJsonOrEmpty(path: string): Promise<Record<string, unknown>>;
4
+ /** Write a JSON object with trailing newline. Creates parent dirs. */
5
+ export declare function writeJson(path: string, data: unknown): Promise<void>;
6
+ export type ServerTransform = (server: McpServerEntry) => Record<string, unknown>;
7
+ /** Standard mcpServers entry (cursor, universal, claude-code). */
8
+ export declare const standardEntry: ServerTransform;
9
+ /** Adds `type: "stdio"` (copilot). */
10
+ export declare const stdioEntry: ServerTransform;
11
+ /** Adds `alwaysAllow` (cline, roo-code, windsurf). */
12
+ export declare const alwaysAllowEntry: ServerTransform;
13
+ interface WriteMcpServersOpts {
14
+ /** Full path to the JSON file. */
15
+ path: string;
16
+ /** Key in the JSON that holds the server map. Default: `"mcpServers"`. */
17
+ key?: string;
18
+ /** Transform each McpServerEntry into the harness-specific shape. */
19
+ transform?: ServerTransform;
20
+ /** Extra top-level fields to merge (e.g. `$schema`). */
21
+ defaults?: Record<string, unknown>;
22
+ }
23
+ /**
24
+ * Merge MCP server entries into an existing JSON config file.
25
+ * Creates the file (and parent dirs) if missing.
26
+ */
27
+ export declare function writeMcpServers(servers: McpServerEntry[], opts: WriteMcpServersOpts): Promise<void>;
28
+ /**
29
+ * Write instructions as a plain text rules file.
30
+ * Skips if no instructions.
31
+ */
32
+ export declare function writeRulesFile(instructions: string[], path: string): Promise<void>;
33
+ interface AgentMdOpts {
34
+ /** Full path to the .md file. */
35
+ path: string;
36
+ /** Extra YAML frontmatter lines (after name/description, before `---`). */
37
+ extraFrontmatter?: string[];
38
+ /** Fallback body when instructions are empty. */
39
+ fallbackBody?: string;
40
+ }
41
+ /**
42
+ * Write an agent markdown file with YAML frontmatter.
43
+ * Shared by claude-code, copilot, and opencode.
44
+ */
45
+ export declare function writeAgentMd(config: LogicalConfig, opts: AgentMdOpts): Promise<void>;
46
+ /**
47
+ * Write git hook scripts to `.git/hooks/<phase>`.
48
+ * Files are created with executable permissions (0o755).
49
+ * No-op when the hooks array is empty.
50
+ */
51
+ export declare function writeGitHooks(hooks: GitHook[] | undefined, projectRoot: string): Promise<void>;
52
+ export declare function writeInlineSkills(config: LogicalConfig, projectRoot: string): Promise<void>;
53
+ export {};
package/dist/util.js ADDED
@@ -0,0 +1,130 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ // ---------------------------------------------------------------------------
4
+ // JSON helpers
5
+ // ---------------------------------------------------------------------------
6
+ /** Read a JSON file, returning `{}` if missing or unparseable. */
7
+ export async function readJsonOrEmpty(path) {
8
+ try {
9
+ return JSON.parse(await readFile(path, "utf-8"));
10
+ }
11
+ catch {
12
+ return {};
13
+ }
14
+ }
15
+ /** Write a JSON object with trailing newline. Creates parent dirs. */
16
+ export async function writeJson(path, data) {
17
+ await mkdir(dirname(path), { recursive: true });
18
+ await writeFile(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
19
+ }
20
+ // ---------------------------------------------------------------------------
21
+ // Server entry transform — each harness overrides only what differs
22
+ // ---------------------------------------------------------------------------
23
+ /** Minimal MCP entry: command + args + optional env. */
24
+ function baseEntry(server) {
25
+ return {
26
+ command: server.command,
27
+ args: server.args,
28
+ ...(Object.keys(server.env).length > 0 ? { env: server.env } : {})
29
+ };
30
+ }
31
+ /** Standard mcpServers entry (cursor, universal, claude-code). */
32
+ export const standardEntry = baseEntry;
33
+ /** Adds `type: "stdio"` (copilot). */
34
+ export const stdioEntry = (s) => ({
35
+ type: "stdio",
36
+ ...baseEntry(s)
37
+ });
38
+ /** Adds `alwaysAllow` (cline, roo-code, windsurf). */
39
+ export const alwaysAllowEntry = (s) => ({
40
+ ...baseEntry(s),
41
+ alwaysAllow: s.allowedTools ?? ["*"]
42
+ });
43
+ /**
44
+ * Merge MCP server entries into an existing JSON config file.
45
+ * Creates the file (and parent dirs) if missing.
46
+ */
47
+ export async function writeMcpServers(servers, opts) {
48
+ if (servers.length === 0)
49
+ return;
50
+ const key = opts.key ?? "mcpServers";
51
+ const transform = opts.transform ?? standardEntry;
52
+ const existing = await readJsonOrEmpty(opts.path);
53
+ const map = existing[key] ?? {};
54
+ for (const server of servers) {
55
+ map[server.ref] = transform(server);
56
+ }
57
+ const result = { ...(opts.defaults ?? {}), ...existing, [key]: map };
58
+ await writeJson(opts.path, result);
59
+ }
60
+ // ---------------------------------------------------------------------------
61
+ // Instructions → flat rules file (windsurf, cline, roo-code)
62
+ // ---------------------------------------------------------------------------
63
+ /**
64
+ * Write instructions as a plain text rules file.
65
+ * Skips if no instructions.
66
+ */
67
+ export async function writeRulesFile(instructions, path) {
68
+ if (instructions.length === 0)
69
+ return;
70
+ const lines = instructions.flatMap((i) => [i, ""]);
71
+ await mkdir(dirname(path), { recursive: true });
72
+ await writeFile(path, lines.join("\n"), "utf-8");
73
+ }
74
+ /**
75
+ * Write an agent markdown file with YAML frontmatter.
76
+ * Shared by claude-code, copilot, and opencode.
77
+ */
78
+ export async function writeAgentMd(config, opts) {
79
+ if (config.instructions.length === 0 && config.mcp_servers.length === 0)
80
+ return;
81
+ const fm = [
82
+ "---",
83
+ "name: ade",
84
+ "description: ADE — Agentic Development Environment agent with project conventions and tools"
85
+ ];
86
+ if (opts.extraFrontmatter) {
87
+ fm.push(...opts.extraFrontmatter);
88
+ }
89
+ fm.push("---");
90
+ const body = config.instructions.length > 0
91
+ ? config.instructions.join("\n\n")
92
+ : (opts.fallbackBody ?? "");
93
+ const content = fm.join("\n") + "\n\n" + body + "\n";
94
+ await mkdir(dirname(opts.path), { recursive: true });
95
+ await writeFile(opts.path, content, "utf-8");
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // Inline skill SKILL.md writer (used by claude-code)
99
+ // ---------------------------------------------------------------------------
100
+ // ---------------------------------------------------------------------------
101
+ // Git hook installer
102
+ // ---------------------------------------------------------------------------
103
+ /**
104
+ * Write git hook scripts to `.git/hooks/<phase>`.
105
+ * Files are created with executable permissions (0o755).
106
+ * No-op when the hooks array is empty.
107
+ */
108
+ export async function writeGitHooks(hooks, projectRoot) {
109
+ if (!hooks)
110
+ return;
111
+ for (const hook of hooks) {
112
+ const hookPath = join(projectRoot, ".git", "hooks", hook.phase);
113
+ await writeFile(hookPath, hook.script, { mode: 0o755 });
114
+ }
115
+ }
116
+ export async function writeInlineSkills(config, projectRoot) {
117
+ for (const skill of config.skills) {
118
+ if (!("body" in skill))
119
+ continue;
120
+ const skillDir = join(projectRoot, ".ade", "skills", skill.name);
121
+ await mkdir(skillDir, { recursive: true });
122
+ const frontmatter = [
123
+ "---",
124
+ `name: ${skill.name}`,
125
+ `description: ${skill.description}`,
126
+ "---"
127
+ ].join("\n");
128
+ await writeFile(join(skillDir, "SKILL.md"), `${frontmatter}\n\n${skill.body}\n`, "utf-8");
129
+ }
130
+ }
@@ -0,0 +1,2 @@
1
+ import type { HarnessWriter } from "../types.js";
2
+ export declare const claudeCodeWriter: HarnessWriter;
@@ -0,0 +1,45 @@
1
+ import { join } from "node:path";
2
+ import { readJsonOrEmpty, writeJson, writeMcpServers, writeAgentMd, writeInlineSkills, writeGitHooks } from "../util.js";
3
+ export const claudeCodeWriter = {
4
+ id: "claude-code",
5
+ label: "Claude Code",
6
+ description: "Anthropic's CLI agent — .claude/agents/ade.md + .mcp.json + .claude/settings.json",
7
+ async install(config, projectRoot) {
8
+ await writeAgentMd(config, {
9
+ path: join(projectRoot, ".claude", "agents", "ade.md"),
10
+ fallbackBody: "ADE — Agentic Development Environment agent."
11
+ });
12
+ await writeMcpServers(config.mcp_servers, {
13
+ path: join(projectRoot, ".mcp.json")
14
+ });
15
+ await writeClaudeSettings(config, projectRoot);
16
+ await writeInlineSkills(config, projectRoot);
17
+ await writeGitHooks(config.git_hooks, projectRoot);
18
+ }
19
+ };
20
+ async function writeClaudeSettings(config, projectRoot) {
21
+ const servers = config.mcp_servers;
22
+ if (servers.length === 0)
23
+ return;
24
+ const settingsPath = join(projectRoot, ".claude", "settings.json");
25
+ const existing = await readJsonOrEmpty(settingsPath);
26
+ const allowRules = [];
27
+ for (const server of servers) {
28
+ const allowed = server.allowedTools ?? ["*"];
29
+ if (allowed.includes("*")) {
30
+ allowRules.push(`MCP(${server.ref}:*)`);
31
+ }
32
+ else {
33
+ for (const tool of allowed) {
34
+ allowRules.push(`MCP(${server.ref}:${tool})`);
35
+ }
36
+ }
37
+ }
38
+ const existingPerms = existing.permissions ?? {};
39
+ const existingAllow = existingPerms.allow ?? [];
40
+ const mergedAllow = [...new Set([...existingAllow, ...allowRules])];
41
+ await writeJson(settingsPath, {
42
+ ...existing,
43
+ permissions: { ...existingPerms, allow: mergedAllow }
44
+ });
45
+ }
@@ -0,0 +1,2 @@
1
+ import type { HarnessWriter } from "../types.js";
2
+ export declare const clineWriter: HarnessWriter;
@@ -0,0 +1,15 @@
1
+ import { join } from "node:path";
2
+ import { writeMcpServers, alwaysAllowEntry, writeRulesFile, writeGitHooks } from "../util.js";
3
+ export const clineWriter = {
4
+ id: "cline",
5
+ label: "Cline",
6
+ description: "VS Code AI agent — .cline/mcp.json + .clinerules",
7
+ async install(config, projectRoot) {
8
+ await writeMcpServers(config.mcp_servers, {
9
+ path: join(projectRoot, ".cline", "mcp.json"),
10
+ transform: alwaysAllowEntry
11
+ });
12
+ await writeRulesFile(config.instructions, join(projectRoot, ".clinerules"));
13
+ await writeGitHooks(config.git_hooks, projectRoot);
14
+ }
15
+ };
@@ -0,0 +1,2 @@
1
+ import type { HarnessWriter } from "../types.js";
2
+ export declare const copilotWriter: HarnessWriter;
@@ -0,0 +1,28 @@
1
+ import { join } from "node:path";
2
+ import { writeMcpServers, stdioEntry, writeAgentMd, writeGitHooks } from "../util.js";
3
+ export const copilotWriter = {
4
+ id: "copilot",
5
+ label: "GitHub Copilot",
6
+ description: "VS Code + CLI — .vscode/mcp.json + .github/agents/ade.agent.md",
7
+ async install(config, projectRoot) {
8
+ await writeMcpServers(config.mcp_servers, {
9
+ path: join(projectRoot, ".vscode", "mcp.json"),
10
+ key: "servers",
11
+ transform: stdioEntry
12
+ });
13
+ const tools = [
14
+ "edit",
15
+ "search",
16
+ "runCommands",
17
+ "runTasks",
18
+ "fetch",
19
+ "githubRepo",
20
+ ...config.mcp_servers.map((s) => `${s.ref}/*`)
21
+ ];
22
+ await writeAgentMd(config, {
23
+ path: join(projectRoot, ".github", "agents", "ade.agent.md"),
24
+ extraFrontmatter: ["tools:", ...tools.map((t) => ` - ${t}`)]
25
+ });
26
+ await writeGitHooks(config.git_hooks, projectRoot);
27
+ }
28
+ };
@@ -0,0 +1,2 @@
1
+ import type { HarnessWriter } from "../types.js";
2
+ export declare const cursorWriter: HarnessWriter;
@@ -0,0 +1,27 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { writeMcpServers, writeGitHooks } from "../util.js";
4
+ export const cursorWriter = {
5
+ id: "cursor",
6
+ label: "Cursor",
7
+ description: "AI code editor — .cursor/mcp.json + .cursor/rules/",
8
+ async install(config, projectRoot) {
9
+ await writeMcpServers(config.mcp_servers, {
10
+ path: join(projectRoot, ".cursor", "mcp.json")
11
+ });
12
+ if (config.instructions.length > 0) {
13
+ const rulesDir = join(projectRoot, ".cursor", "rules");
14
+ await mkdir(rulesDir, { recursive: true });
15
+ const content = [
16
+ "---",
17
+ "description: ADE project conventions",
18
+ "globs: *",
19
+ "---",
20
+ "",
21
+ ...config.instructions.flatMap((i) => [i, ""])
22
+ ].join("\n");
23
+ await writeFile(join(rulesDir, "ade.mdc"), content, "utf-8");
24
+ }
25
+ await writeGitHooks(config.git_hooks, projectRoot);
26
+ }
27
+ };
@@ -0,0 +1,2 @@
1
+ import type { HarnessWriter } from "../types.js";
2
+ export declare const kiroWriter: HarnessWriter;
@@ -0,0 +1,46 @@
1
+ import { join } from "node:path";
2
+ import { standardEntry, writeJson, writeGitHooks } from "../util.js";
3
+ export const kiroWriter = {
4
+ id: "kiro",
5
+ label: "Kiro",
6
+ description: "AWS AI IDE — .kiro/agents/ade.json",
7
+ async install(config, projectRoot) {
8
+ const servers = config.mcp_servers;
9
+ if (servers.length > 0 || config.instructions.length > 0) {
10
+ const mcpServers = {};
11
+ for (const s of servers) {
12
+ mcpServers[s.ref] = standardEntry(s);
13
+ }
14
+ const tools = [
15
+ "execute_bash",
16
+ "fs_read",
17
+ "fs_write",
18
+ "knowledge",
19
+ "thinking",
20
+ ...Object.keys(mcpServers).map((n) => `@${n}`)
21
+ ];
22
+ const allowedTools = [];
23
+ for (const s of servers) {
24
+ const explicit = s.allowedTools;
25
+ if (explicit && !explicit.includes("*")) {
26
+ for (const tool of explicit) {
27
+ allowedTools.push(`@${s.ref}/${tool}`);
28
+ }
29
+ }
30
+ else {
31
+ allowedTools.push(`@${s.ref}/*`);
32
+ }
33
+ }
34
+ await writeJson(join(projectRoot, ".kiro", "agents", "ade.json"), {
35
+ name: "ade",
36
+ prompt: config.instructions.length > 0
37
+ ? config.instructions.join("\n\n")
38
+ : "ADE — Agentic Development Environment agent",
39
+ mcpServers,
40
+ tools,
41
+ allowedTools
42
+ });
43
+ }
44
+ await writeGitHooks(config.git_hooks, projectRoot);
45
+ }
46
+ };
@@ -0,0 +1,2 @@
1
+ import type { HarnessWriter } from "../types.js";
2
+ export declare const opencodeWriter: HarnessWriter;
@@ -0,0 +1,45 @@
1
+ import { join } from "node:path";
2
+ import { writeMcpServers, writeAgentMd, writeGitHooks } from "../util.js";
3
+ export const opencodeWriter = {
4
+ id: "opencode",
5
+ label: "OpenCode",
6
+ description: "Terminal AI agent — opencode.json + .opencode/agents/",
7
+ async install(config, projectRoot) {
8
+ await writeMcpServers(config.mcp_servers, {
9
+ path: join(projectRoot, "opencode.json"),
10
+ key: "mcp",
11
+ transform: (s) => ({
12
+ type: "local",
13
+ command: [s.command, ...s.args],
14
+ ...(Object.keys(s.env).length > 0 ? { env: s.env } : {})
15
+ }),
16
+ defaults: { $schema: "https://opencode.ai/config.json" }
17
+ });
18
+ const servers = config.mcp_servers;
19
+ const extraFm = [
20
+ "tools:",
21
+ " read: true",
22
+ " edit: approve",
23
+ " bash: approve"
24
+ ];
25
+ if (servers.length > 0) {
26
+ extraFm.push("mcp_servers:");
27
+ for (const s of servers) {
28
+ extraFm.push(` ${s.ref}:`);
29
+ extraFm.push(` command: [${[s.command, ...s.args].map((a) => `"${a}"`).join(", ")}]`);
30
+ if (Object.keys(s.env).length > 0) {
31
+ extraFm.push(" env:");
32
+ for (const [k, v] of Object.entries(s.env)) {
33
+ extraFm.push(` ${k}: "${v}"`);
34
+ }
35
+ }
36
+ }
37
+ }
38
+ await writeAgentMd(config, {
39
+ path: join(projectRoot, ".opencode", "agents", "ade.md"),
40
+ extraFrontmatter: extraFm,
41
+ fallbackBody: "ADE — Agentic Development Environment agent with project conventions and tools."
42
+ });
43
+ await writeGitHooks(config.git_hooks, projectRoot);
44
+ }
45
+ };
@@ -0,0 +1,2 @@
1
+ import type { HarnessWriter } from "../types.js";
2
+ export declare const rooCodeWriter: HarnessWriter;
@@ -0,0 +1,15 @@
1
+ import { join } from "node:path";
2
+ import { writeMcpServers, alwaysAllowEntry, writeRulesFile, writeGitHooks } from "../util.js";
3
+ export const rooCodeWriter = {
4
+ id: "roo-code",
5
+ label: "Roo Code",
6
+ description: "AI coding agent — .roo/mcp.json + .roorules",
7
+ async install(config, projectRoot) {
8
+ await writeMcpServers(config.mcp_servers, {
9
+ path: join(projectRoot, ".roo", "mcp.json"),
10
+ transform: alwaysAllowEntry
11
+ });
12
+ await writeRulesFile(config.instructions, join(projectRoot, ".roorules"));
13
+ await writeGitHooks(config.git_hooks, projectRoot);
14
+ }
15
+ };
@@ -0,0 +1,2 @@
1
+ import type { HarnessWriter } from "../types.js";
2
+ export declare const universalWriter: HarnessWriter;