@microck/canonfig 2.0.0 → 2.1.0

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 (57) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/cli.js +1 -1
  3. package/dist/harness-configuration/adapters/amp.js +87 -0
  4. package/dist/harness-configuration/adapters/antigravity.js +50 -0
  5. package/dist/harness-configuration/adapters/claude.js +43 -0
  6. package/dist/harness-configuration/adapters/codex.js +60 -0
  7. package/dist/harness-configuration/adapters/copilot.js +79 -0
  8. package/dist/harness-configuration/adapters/cursor.js +67 -0
  9. package/dist/harness-configuration/adapters/descriptor.js +10 -0
  10. package/dist/harness-configuration/adapters/devin.js +66 -0
  11. package/dist/harness-configuration/adapters/droid.js +32 -0
  12. package/dist/harness-configuration/adapters/grok.js +44 -0
  13. package/dist/harness-configuration/adapters/hermes.js +105 -0
  14. package/dist/harness-configuration/adapters/index.js +50 -0
  15. package/dist/harness-configuration/adapters/kilo.js +18 -0
  16. package/dist/harness-configuration/adapters/kimi.js +148 -0
  17. package/dist/harness-configuration/adapters/omp.js +64 -0
  18. package/dist/harness-configuration/adapters/open-code-family.js +94 -0
  19. package/dist/harness-configuration/adapters/opencode.js +18 -0
  20. package/dist/harness-configuration/adapters/pi.js +93 -0
  21. package/dist/harness-configuration/adapters/qwen.js +164 -0
  22. package/dist/harness-configuration/adapters/shared-common.js +66 -0
  23. package/dist/harness-configuration/adapters/shared-documents.js +117 -0
  24. package/dist/harness-configuration/adapters/shared-hooks.js +186 -0
  25. package/dist/harness-configuration/adapters/shared-mcp.js +214 -0
  26. package/dist/harness-configuration/adapters/shared.js +4 -0
  27. package/dist/harness-configuration/adapters/tools.js +24 -0
  28. package/dist/harness-configuration/cli-arguments.js +105 -0
  29. package/dist/harness-configuration/cli-output.js +77 -0
  30. package/dist/harness-configuration/cli.js +196 -0
  31. package/dist/harness-configuration/core/compiler.js +175 -0
  32. package/dist/harness-configuration/core/config.js +74 -0
  33. package/dist/harness-configuration/core/diff.js +60 -0
  34. package/dist/harness-configuration/core/doctor.js +40 -0
  35. package/dist/harness-configuration/core/errors.js +13 -0
  36. package/dist/harness-configuration/core/filesystem.js +172 -0
  37. package/dist/harness-configuration/core/frontmatter.js +49 -0
  38. package/dist/harness-configuration/core/hash.js +4 -0
  39. package/dist/harness-configuration/core/path.js +50 -0
  40. package/dist/harness-configuration/core/planner.js +255 -0
  41. package/dist/harness-configuration/core/render-cleanup.js +91 -0
  42. package/dist/harness-configuration/core/render-json.js +195 -0
  43. package/dist/harness-configuration/core/render-text.js +134 -0
  44. package/dist/harness-configuration/core/render-utils.js +202 -0
  45. package/dist/harness-configuration/core/render.js +54 -0
  46. package/dist/harness-configuration/core/scaffold.js +103 -0
  47. package/dist/harness-configuration/core/schema-components.js +167 -0
  48. package/dist/harness-configuration/core/schema-config.js +98 -0
  49. package/dist/harness-configuration/core/schema-runtime.js +143 -0
  50. package/dist/harness-configuration/core/schema-types.js +8 -0
  51. package/dist/harness-configuration/core/schema.js +13 -0
  52. package/dist/harness-configuration/core/state.js +42 -0
  53. package/dist/harness-configuration/core/types.js +5 -0
  54. package/dist/harness-configuration/core/validation.js +113 -0
  55. package/dist/harness-configuration/templates/runtime.js +212 -0
  56. package/dist/runtime/main.js +20 -14
  57. package/package.json +5 -5
package/README.md CHANGED
@@ -55,7 +55,7 @@ Required; the current executor never fetches or builds source code.
55
55
  Install the exact public package version on Linux, macOS, or Windows:
56
56
 
57
57
  ```bash
58
- npm install --global @microck/canonfig@2.0.0
58
+ npm install --global @microck/canonfig@2.1.0
59
59
  canonfig --version
60
60
  ```
61
61
 
package/dist/cli/cli.js CHANGED
@@ -11,7 +11,7 @@ import { renderCliResult, } from "./render.js";
11
11
  import { SourceCommands, } from "./source-commands.js";
12
12
  export const programName = "canonfig";
13
13
  export const programDisplayName = "Canonfig";
14
- export const programVersion = "2.0.0";
14
+ export const programVersion = "2.1.0";
15
15
  export const helpText = `${programDisplayName} ${programVersion}
16
16
 
17
17
  Usage: ${programName} <command> [options]
@@ -0,0 +1,87 @@
1
+ import { descriptor } from "./descriptor.js";
2
+ import { agentDocuments, commandSkillArtifacts, enabledHooks, hasEnabledMcpServers, ruleDocuments, ruleMarkdown, skillArtifacts, standardMcpMap, standardMcpProjectionDiagnostics, } from "./shared.js";
3
+ import { nativeTools } from "./tools.js";
4
+ import { AMP_PLUGIN_EVENT_MAP, ampPluginSource } from "../templates/runtime.js";
5
+ const AMP_PLUGIN_EVENTS = new Set(Object.keys(AMP_PLUGIN_EVENT_MAP));
6
+ export const ampAdapter = {
7
+ descriptor: descriptor("amp", "Amp", ["amp"], [
8
+ "https://ampcode.com/manual",
9
+ "https://ampcode.com/manual#mcp",
10
+ "https://ampcode.com/manual#agent-skills",
11
+ "https://ampcode.com/manual#plugins",
12
+ ], {
13
+ instructions: "portable",
14
+ rules: "translated",
15
+ skills: "portable",
16
+ mcp: "native",
17
+ hooks: "shim",
18
+ agents: "translated",
19
+ commands: "translated",
20
+ }, [
21
+ "Hooks and custom subagents compile into an Amp TypeScript plugin.",
22
+ "Commands compile to Agent Skills so they remain invokable without relying on an unstable command manifest.",
23
+ ]),
24
+ async build(context) {
25
+ const artifacts = [];
26
+ const diagnostics = [];
27
+ if (hasEnabledMcpServers(context)) {
28
+ diagnostics.push(...standardMcpProjectionDiagnostics(context, "amp", false));
29
+ artifacts.push({
30
+ kind: "json",
31
+ path: ".amp/settings.json",
32
+ owner: "amp",
33
+ operations: [{
34
+ kind: "managed-map",
35
+ path: ["amp.mcpServers"],
36
+ entries: standardMcpMap(context, false),
37
+ collision: "error",
38
+ }],
39
+ });
40
+ }
41
+ const agents = await agentDocuments(context);
42
+ const hooks = enabledHooks(context);
43
+ const supportedHooks = hooks.filter((hook) => AMP_PLUGIN_EVENTS.has(hook.event));
44
+ for (const hook of hooks) {
45
+ if (!AMP_PLUGIN_EVENTS.has(hook.event)) {
46
+ diagnostics.push({
47
+ level: "warning",
48
+ code: "HOOK_EVENT_UNSUPPORTED",
49
+ target: "amp",
50
+ message: `Amp's generated plugin cannot map hook event ${hook.event}; it was skipped.`,
51
+ });
52
+ }
53
+ }
54
+ if (supportedHooks.length > 0 || agents.length > 0) {
55
+ artifacts.push({
56
+ kind: "replace",
57
+ path: ".amp/plugins/canonfig.ts",
58
+ owner: "amp",
59
+ content: ampPluginSource(supportedHooks, agents.map(({ agent, content }) => ({ agent, content, tools: nativeTools("amp", agent) }))),
60
+ });
61
+ }
62
+ for (const { rule, content } of await ruleDocuments(context)) {
63
+ artifacts.push({
64
+ kind: "replace",
65
+ path: `.amp/rules/${rule.id}.md`,
66
+ owner: "amp",
67
+ content: ruleMarkdown(rule, content),
68
+ });
69
+ }
70
+ const commonSkillPaths = new Set((await skillArtifacts(context, ".agents/skills", "common")).map((artifact) => artifact.path));
71
+ for (const artifact of await commandSkillArtifacts(context, ".agents/skills", "amp")) {
72
+ if (commonSkillPaths.has(artifact.path)) {
73
+ diagnostics.push({
74
+ level: "error",
75
+ code: "TRANSLATED_SKILL_COLLISION",
76
+ target: "amp",
77
+ path: artifact.path,
78
+ message: `Amp command output collides with a canonical skill at ${artifact.path}; rename the skill or command.`,
79
+ });
80
+ }
81
+ else {
82
+ artifacts.push(artifact);
83
+ }
84
+ }
85
+ return { artifacts, diagnostics };
86
+ },
87
+ };
@@ -0,0 +1,50 @@
1
+ import { descriptor } from "./descriptor.js";
2
+ import { agentSkillArtifacts, antigravityHooks, antigravityMcpMap, commandSkillArtifacts, enabledHooks, hasEnabledMcpServers, ruleDocuments, ruleMarkdown, skillArtifacts, } from "./shared.js";
3
+ export const antigravityAdapter = {
4
+ descriptor: descriptor("antigravity", "Google Antigravity CLI", ["agy"], ["https://antigravity.google/docs/mcp", "https://antigravity.google/docs/hooks", "https://antigravity.google/docs/gcli-migration"], { instructions: "portable", rules: "native", skills: "portable", mcp: "native", hooks: "native", agents: "translated", commands: "translated" }, ["Canonical agents and commands compile to Agent Skills; Antigravity exposes subagents separately from portable agent manifests."]),
5
+ async build(context) {
6
+ const artifacts = [];
7
+ const diagnostics = [];
8
+ if (hasEnabledMcpServers(context)) {
9
+ artifacts.push({
10
+ kind: "json", path: ".agents/mcp_config.json", owner: "antigravity",
11
+ operations: [{ kind: "managed-map", path: ["mcpServers"], entries: antigravityMcpMap(context), collision: "error" }],
12
+ });
13
+ }
14
+ if (enabledHooks(context).length > 0) {
15
+ const compiled = antigravityHooks(context);
16
+ diagnostics.push(...compiled.diagnostics);
17
+ if (Object.keys(compiled.entries).length > 0) {
18
+ artifacts.push({
19
+ kind: "json", path: ".agents/hooks.json", owner: "antigravity",
20
+ operations: [{ kind: "managed-map", path: [], entries: compiled.entries, collision: "error" }],
21
+ });
22
+ }
23
+ }
24
+ for (const { rule, content } of await ruleDocuments(context)) {
25
+ artifacts.push({ kind: "replace", path: `.agents/rules/${rule.id}.md`, owner: "antigravity", content: ruleMarkdown(rule, content, { trigger: rule.paths.length ? "glob" : "always" }) });
26
+ }
27
+ const commonSkills = await skillArtifacts(context, ".agents/skills", "common");
28
+ artifacts.push(...commonSkills);
29
+ const occupiedSkillPaths = new Set(commonSkills.map((artifact) => artifact.path));
30
+ const translatedSkills = [
31
+ ...await agentSkillArtifacts(context, ".agents/skills", "antigravity"),
32
+ ...await commandSkillArtifacts(context, ".agents/skills", "antigravity"),
33
+ ];
34
+ for (const artifact of translatedSkills) {
35
+ if (occupiedSkillPaths.has(artifact.path)) {
36
+ diagnostics.push({
37
+ level: "error",
38
+ code: "TRANSLATED_SKILL_COLLISION",
39
+ target: "antigravity",
40
+ path: artifact.path,
41
+ message: `Antigravity translated skill output collides at ${artifact.path}; rename the canonical skill, agent, or command.`,
42
+ });
43
+ continue;
44
+ }
45
+ occupiedSkillPaths.add(artifact.path);
46
+ artifacts.push(artifact);
47
+ }
48
+ return { artifacts, diagnostics };
49
+ },
50
+ };
@@ -0,0 +1,43 @@
1
+ import { descriptor } from "./descriptor.js";
2
+ import { agentDocuments, agentMarkdown, claudeStyleHooks, commandDocuments, commandMarkdown, enabledHooks, enabledMcpServerEntries, ruleDocuments, ruleMarkdown, skillArtifacts, standardMcpProjectionDiagnostics, } from "./shared.js";
3
+ import { nativeTools } from "./tools.js";
4
+ export const claudeAdapter = {
5
+ descriptor: descriptor("claude-code", "Claude Code", ["claude"], ["https://docs.anthropic.com/en/docs/claude-code/settings", "https://docs.anthropic.com/en/docs/claude-code/hooks"], { instructions: "translated", rules: "native", skills: "native", mcp: "native", hooks: "native", agents: "native", commands: "native" }, ["CLAUDE.md is generated as a small bridge to the canonical AGENTS.md."]),
6
+ async build(context) {
7
+ const artifacts = [{
8
+ kind: "managed-text", path: "CLAUDE.md", owner: "claude-code", marker: "instructions-import",
9
+ comments: "html", placement: "start", content: "@AGENTS.md",
10
+ }];
11
+ const diagnostics = standardMcpProjectionDiagnostics(context, "claude-code");
12
+ artifacts.push(...await skillArtifacts(context, ".claude/skills", "claude-code"));
13
+ for (const { rule, content } of await ruleDocuments(context)) {
14
+ artifacts.push({ kind: "replace", path: `.claude/rules/${rule.id}.md`, owner: "claude-code", content: ruleMarkdown(rule, content, rule.paths.length ? { paths: rule.paths } : {}) });
15
+ }
16
+ if (enabledHooks(context).length > 0) {
17
+ const compiled = claudeStyleHooks(context);
18
+ diagnostics.push(...compiled.diagnostics);
19
+ if (Object.keys(compiled.hooks).length > 0) {
20
+ artifacts.push({
21
+ kind: "json", path: ".claude/settings.json", owner: "claude-code",
22
+ operations: [{ kind: "managed-hooks", path: ["hooks"], hooks: compiled.hooks, marker: ".canonfig/.runtime/hook-runner.mjs" }],
23
+ });
24
+ }
25
+ }
26
+ for (const { agent, content } of await agentDocuments(context)) {
27
+ const tools = nativeTools("claude-code", agent);
28
+ if (agent.tools.includes("mcp")) {
29
+ tools.push(...enabledMcpServerEntries(context).map(([name]) => `mcp__${name}`));
30
+ }
31
+ artifacts.push({
32
+ kind: "replace",
33
+ path: `.claude/agents/${agent.id}.md`,
34
+ owner: "claude-code",
35
+ content: agentMarkdown(agent, content, [...new Set(tools)]),
36
+ });
37
+ }
38
+ for (const { command, content } of await commandDocuments(context)) {
39
+ artifacts.push({ kind: "replace", path: `.claude/commands/${command.id}.md`, owner: "claude-code", content: commandMarkdown(command, content) });
40
+ }
41
+ return { artifacts, diagnostics };
42
+ },
43
+ };
@@ -0,0 +1,60 @@
1
+ import { descriptor } from "./descriptor.js";
2
+ import { agentDocuments, CODEX_EVENT_MAP, codexMcpDiagnostics, commandSkillArtifacts, enabledHooks, mcpToml, claudeStyleHooks, } from "./shared.js";
3
+ function tomlString(value) { return JSON.stringify(value); }
4
+ function tomlMultiline(value) {
5
+ const escaped = value.replaceAll("\\", "\\\\").replaceAll('"""', '\\"\\"\\"');
6
+ return `"""\n${escaped.trim()}\n"""`;
7
+ }
8
+ export const codexAdapter = {
9
+ descriptor: descriptor("codex", "OpenAI Codex", ["codex"], [
10
+ "https://developers.openai.com/codex/config-reference",
11
+ "https://developers.openai.com/codex/subagents",
12
+ "https://developers.openai.com/codex/hooks",
13
+ ], {
14
+ instructions: "portable",
15
+ rules: "translated",
16
+ skills: "portable",
17
+ mcp: "native",
18
+ hooks: "native",
19
+ agents: "native",
20
+ commands: "translated",
21
+ }, ["Commands compile to Agent Skills because Codex uses skills as the portable command surface."]),
22
+ async build(context) {
23
+ const artifacts = [];
24
+ const diagnostics = [];
25
+ const mcp = mcpToml(context);
26
+ if (mcp) {
27
+ diagnostics.push(...codexMcpDiagnostics(context));
28
+ artifacts.push({
29
+ kind: "toml", path: ".codex/config.toml", owner: "codex",
30
+ blocks: [{ marker: "mcp-servers", content: mcp }],
31
+ description: "Codex project MCP servers",
32
+ });
33
+ }
34
+ if (enabledHooks(context).length > 0) {
35
+ const compiled = claudeStyleHooks(context, CODEX_EVENT_MAP);
36
+ diagnostics.push(...compiled.diagnostics);
37
+ if (Object.keys(compiled.hooks).length > 0) {
38
+ artifacts.push({
39
+ kind: "json", path: ".codex/hooks.json", owner: "codex",
40
+ rootDefaults: { version: 1 },
41
+ operations: [{ kind: "managed-hooks", path: ["hooks"], hooks: compiled.hooks, marker: ".canonfig/.runtime/hook-runner.mjs" }],
42
+ description: "Codex lifecycle hooks",
43
+ });
44
+ }
45
+ }
46
+ for (const { agent, content } of await agentDocuments(context)) {
47
+ const lines = [
48
+ `name = ${tomlString(agent.id)}`,
49
+ `description = ${tomlString(agent.description)}`,
50
+ ...(agent.model === "inherit" ? [] : [`model = ${tomlString(agent.model)}`]),
51
+ `sandbox_mode = ${tomlString(agent.writable ? "workspace-write" : "read-only")}`,
52
+ `developer_instructions = ${tomlMultiline(content)}`,
53
+ "",
54
+ ];
55
+ artifacts.push({ kind: "replace", path: `.codex/agents/${agent.id}.toml`, owner: "codex", content: lines.join("\n") });
56
+ }
57
+ artifacts.push(...await commandSkillArtifacts(context, ".codex/skills", "codex"));
58
+ return { artifacts, diagnostics };
59
+ },
60
+ };
@@ -0,0 +1,79 @@
1
+ import { descriptor } from "./descriptor.js";
2
+ import { agentDocuments, commandSkillArtifacts, copilotHooks, enabledHooks, ruleDocuments, skillArtifacts, standardMcpProjectionDiagnostics, } from "./shared.js";
3
+ import { nativeTools } from "./tools.js";
4
+ import { markdownWithFrontmatter } from "../core/frontmatter.js";
5
+ export const copilotAdapter = {
6
+ descriptor: descriptor("copilot-cli", "GitHub Copilot CLI", ["copilot"], [
7
+ "https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-custom-instructions",
8
+ "https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-skills",
9
+ "https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-hooks",
10
+ "https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot",
11
+ ], {
12
+ instructions: "portable",
13
+ rules: "native",
14
+ skills: "native",
15
+ mcp: "portable",
16
+ hooks: "native",
17
+ agents: "native",
18
+ commands: "translated",
19
+ }, ["The shared repository .mcp.json is used for MCP; commands compile to Agent Skills."]),
20
+ async build(context) {
21
+ const artifacts = [];
22
+ const diagnostics = standardMcpProjectionDiagnostics(context, "copilot-cli");
23
+ const commonSkills = await skillArtifacts(context, ".github/skills", "copilot-cli");
24
+ artifacts.push(...commonSkills);
25
+ const occupiedSkillPaths = new Set(commonSkills.map((artifact) => artifact.path));
26
+ if (enabledHooks(context).length > 0) {
27
+ const compiled = copilotHooks(context);
28
+ diagnostics.push(...compiled.diagnostics);
29
+ if (Object.keys(compiled.hooks).length > 0) {
30
+ artifacts.push({
31
+ kind: "json",
32
+ path: ".github/hooks/canonfig.json",
33
+ owner: "copilot-cli",
34
+ rootDefaults: { version: 1 },
35
+ operations: [{ kind: "managed-hooks", path: ["hooks"], hooks: compiled.hooks, marker: ".canonfig/.runtime/hook-runner.mjs" }],
36
+ });
37
+ }
38
+ }
39
+ for (const { rule, content } of await ruleDocuments(context)) {
40
+ artifacts.push({
41
+ kind: "replace",
42
+ path: `.github/instructions/${rule.id}.instructions.md`,
43
+ owner: "copilot-cli",
44
+ content: markdownWithFrontmatter({
45
+ description: rule.description ?? `Canonfig rule: ${rule.id}`,
46
+ applyTo: rule.paths.length > 0 ? rule.paths.join(",") : "**",
47
+ }, content),
48
+ });
49
+ }
50
+ for (const { agent, content } of await agentDocuments(context)) {
51
+ artifacts.push({
52
+ kind: "replace",
53
+ path: `.github/agents/${agent.id}.agent.md`,
54
+ owner: "copilot-cli",
55
+ content: markdownWithFrontmatter({
56
+ name: agent.id,
57
+ description: agent.description,
58
+ ...(agent.model === "inherit" ? {} : { model: agent.model }),
59
+ tools: nativeTools("copilot-cli", agent),
60
+ }, content),
61
+ });
62
+ }
63
+ for (const artifact of await commandSkillArtifacts(context, ".github/skills", "copilot-cli")) {
64
+ if (occupiedSkillPaths.has(artifact.path)) {
65
+ diagnostics.push({
66
+ level: "error",
67
+ code: "TRANSLATED_SKILL_COLLISION",
68
+ target: "copilot-cli",
69
+ path: artifact.path,
70
+ message: `Copilot command output collides with a canonical skill at ${artifact.path}; rename the skill or command.`,
71
+ });
72
+ continue;
73
+ }
74
+ occupiedSkillPaths.add(artifact.path);
75
+ artifacts.push(artifact);
76
+ }
77
+ return { artifacts, diagnostics };
78
+ },
79
+ };
@@ -0,0 +1,67 @@
1
+ import { descriptor } from "./descriptor.js";
2
+ import { agentDocuments, agentMarkdown, commandDocuments, commandMarkdown, cursorHooks, enabledHooks, hasEnabledMcpServers, jsonMcpArtifact, ruleDocuments, skillArtifacts, standardMcpProjectionDiagnostics, } from "./shared.js";
3
+ import { nativeTools } from "./tools.js";
4
+ import { markdownWithFrontmatter } from "../core/frontmatter.js";
5
+ export const cursorAdapter = {
6
+ descriptor: descriptor("cursor", "Cursor", ["cursor-agent", "agent"], [
7
+ "https://cursor.com/docs/cli/reference/configuration",
8
+ "https://cursor.com/docs/context/rules",
9
+ "https://cursor.com/docs/skills",
10
+ "https://cursor.com/docs/agent/hooks",
11
+ ], {
12
+ instructions: "portable",
13
+ rules: "native",
14
+ skills: "native",
15
+ mcp: "native",
16
+ hooks: "native",
17
+ agents: "native",
18
+ commands: "native",
19
+ }, ["Hook event availability is Cursor-version dependent; unsupported canonical events produce diagnostics."]),
20
+ async build(context) {
21
+ const artifacts = [];
22
+ const diagnostics = [];
23
+ artifacts.push(...await skillArtifacts(context, ".cursor/skills", "cursor"));
24
+ if (hasEnabledMcpServers(context)) {
25
+ diagnostics.push(...standardMcpProjectionDiagnostics(context, "cursor"));
26
+ artifacts.push(jsonMcpArtifact(".cursor/mcp.json", "cursor", context));
27
+ }
28
+ if (enabledHooks(context).length > 0) {
29
+ const compiled = cursorHooks(context);
30
+ diagnostics.push(...compiled.diagnostics);
31
+ if (Object.keys(compiled.hooks).length > 0) {
32
+ artifacts.push({
33
+ kind: "json",
34
+ path: ".cursor/hooks.json",
35
+ owner: "cursor",
36
+ rootDefaults: { version: 1 },
37
+ operations: [{ kind: "managed-hooks", path: ["hooks"], hooks: compiled.hooks, marker: ".canonfig/.runtime/hook-runner.mjs" }],
38
+ });
39
+ }
40
+ }
41
+ for (const { rule, content } of await ruleDocuments(context)) {
42
+ const activation = rule.activation ?? (rule.paths.length > 0 ? "path" : "always");
43
+ artifacts.push({
44
+ kind: "replace",
45
+ path: `.cursor/rules/${rule.id}.mdc`,
46
+ owner: "cursor",
47
+ content: markdownWithFrontmatter({
48
+ description: rule.description ?? `Canonfig rule: ${rule.id}`,
49
+ globs: rule.paths.join(","),
50
+ alwaysApply: activation === "always",
51
+ }, content),
52
+ });
53
+ }
54
+ for (const { agent, content } of await agentDocuments(context)) {
55
+ artifacts.push({
56
+ kind: "replace",
57
+ path: `.cursor/agents/${agent.id}.md`,
58
+ owner: "cursor",
59
+ content: agentMarkdown(agent, content, nativeTools("cursor", agent)),
60
+ });
61
+ }
62
+ for (const { command, content } of await commandDocuments(context)) {
63
+ artifacts.push({ kind: "replace", path: `.cursor/commands/${command.id}.md`, owner: "cursor", content: commandMarkdown(command, content) });
64
+ }
65
+ return { artifacts, diagnostics };
66
+ },
67
+ };
@@ -0,0 +1,10 @@
1
+ export function descriptor(id, name, executables, docs, capabilities, notes = [], verifiedAt = "2026-08-18") {
2
+ return {
3
+ id, name, executables, docs, verifiedAt,
4
+ capabilities: {
5
+ instructions: "portable", rules: "translated", skills: "portable", mcp: "native",
6
+ hooks: "native", agents: "native", commands: "native", permissions: "unsupported", ...capabilities,
7
+ },
8
+ ...(notes.length ? { notes } : {}),
9
+ };
10
+ }
@@ -0,0 +1,66 @@
1
+ import { descriptor } from "./descriptor.js";
2
+ import { agentSkillArtifacts, claudeStyleHooks, commandSkillArtifacts, DEVIN_EVENT_MAP, enabledHooks, hasEnabledMcpServers, jsonMcpArtifact, ruleDocuments, ruleMarkdown, skillArtifacts, standardMcpProjectionDiagnostics, } from "./shared.js";
3
+ export const devinAdapter = {
4
+ descriptor: descriptor("devin", "Devin CLI / Devin Local", ["devin"], [
5
+ "https://docs.devin.ai/cli/extensibility/configuration",
6
+ "https://docs.devin.ai/cli/extensibility/rules",
7
+ "https://docs.devin.ai/cli/extensibility/mcp",
8
+ "https://docs.devin.ai/cli/extensibility/hooks",
9
+ "https://docs.devin.ai/cli/extensibility/skills",
10
+ ], {
11
+ instructions: "portable",
12
+ rules: "native",
13
+ skills: "native",
14
+ mcp: "native",
15
+ hooks: "native",
16
+ agents: "translated",
17
+ commands: "translated",
18
+ }, ["Canonical agents and commands compile to Devin-discoverable Agent Skills."]),
19
+ async build(context) {
20
+ const artifacts = [];
21
+ const diagnostics = [];
22
+ const skills = await skillArtifacts(context, ".devin/skills", "devin");
23
+ artifacts.push(...skills);
24
+ if (hasEnabledMcpServers(context)) {
25
+ diagnostics.push(...standardMcpProjectionDiagnostics(context, "devin"));
26
+ artifacts.push(jsonMcpArtifact(".devin/mcp.json", "devin", context));
27
+ }
28
+ if (enabledHooks(context).length > 0) {
29
+ const compiled = claudeStyleHooks(context, DEVIN_EVENT_MAP);
30
+ diagnostics.push(...compiled.diagnostics);
31
+ if (Object.keys(compiled.hooks).length > 0) {
32
+ artifacts.push({
33
+ kind: "json",
34
+ path: ".devin/hooks.v1.json",
35
+ owner: "devin",
36
+ rootDefaults: { version: 1 },
37
+ operations: [{ kind: "managed-hooks", path: ["hooks"], hooks: compiled.hooks, marker: ".canonfig/.runtime/hook-runner.mjs" }],
38
+ });
39
+ }
40
+ }
41
+ for (const { rule, content } of await ruleDocuments(context)) {
42
+ artifacts.push({ kind: "replace", path: `.devin/rules/${rule.id}.md`, owner: "devin", content: ruleMarkdown(rule, content) });
43
+ }
44
+ const occupiedSkillPaths = new Set(skills.map((artifact) => artifact.path));
45
+ const translated = [
46
+ ...await agentSkillArtifacts(context, ".devin/skills", "devin"),
47
+ ...await commandSkillArtifacts(context, ".devin/skills", "devin"),
48
+ ];
49
+ for (const artifact of translated) {
50
+ if (occupiedSkillPaths.has(artifact.path)) {
51
+ diagnostics.push({
52
+ level: "error",
53
+ code: "TRANSLATED_SKILL_COLLISION",
54
+ target: "devin",
55
+ path: artifact.path,
56
+ message: `Devin translated skill output collides with another skill at ${artifact.path}; rename the canonical skill, agent, or command.`,
57
+ });
58
+ }
59
+ else {
60
+ occupiedSkillPaths.add(artifact.path);
61
+ artifacts.push(artifact);
62
+ }
63
+ }
64
+ return { artifacts, diagnostics };
65
+ },
66
+ };
@@ -0,0 +1,32 @@
1
+ import { descriptor } from "./descriptor.js";
2
+ import { agentDocuments, agentMarkdown, claudeStyleHooks, commandDocuments, commandMarkdown, enabledHooks, hasEnabledMcpServers, jsonMcpArtifact, skillArtifacts, standardMcpProjectionDiagnostics, } from "./shared.js";
3
+ import { nativeTools } from "./tools.js";
4
+ export const droidAdapter = {
5
+ descriptor: descriptor("factory-droid", "Factory Droid CLI", ["droid"], ["https://docs.factory.ai/harness/hooks", "https://docs.factory.ai/harness/mcp", "https://docs.factory.ai/droid-cli/settings"], { rules: "portable", skills: "native" }),
6
+ async build(context) {
7
+ const artifacts = [];
8
+ const diagnostics = [];
9
+ artifacts.push(...await skillArtifacts(context, ".factory/skills", "factory-droid"));
10
+ if (hasEnabledMcpServers(context)) {
11
+ diagnostics.push(...standardMcpProjectionDiagnostics(context, "factory-droid"));
12
+ artifacts.push(jsonMcpArtifact(".factory/mcp.json", "factory-droid", context));
13
+ }
14
+ if (enabledHooks(context).length > 0) {
15
+ const compiled = claudeStyleHooks(context);
16
+ diagnostics.push(...compiled.diagnostics);
17
+ if (Object.keys(compiled.hooks).length > 0) {
18
+ artifacts.push({
19
+ kind: "json", path: ".factory/hooks.json", owner: "factory-droid",
20
+ operations: [{ kind: "managed-hooks", path: ["hooks"], hooks: compiled.hooks, marker: ".canonfig/.runtime/hook-runner.mjs" }],
21
+ });
22
+ }
23
+ }
24
+ for (const { agent, content } of await agentDocuments(context)) {
25
+ artifacts.push({ kind: "replace", path: `.factory/droids/${agent.id}.md`, owner: "factory-droid", content: agentMarkdown(agent, content, nativeTools("factory-droid", agent)) });
26
+ }
27
+ for (const { command, content } of await commandDocuments(context)) {
28
+ artifacts.push({ kind: "replace", path: `.factory/commands/${command.id}.md`, owner: "factory-droid", content: commandMarkdown(command, content) });
29
+ }
30
+ return { artifacts, diagnostics };
31
+ },
32
+ };
@@ -0,0 +1,44 @@
1
+ import { descriptor } from "./descriptor.js";
2
+ import { agentDocuments, agentMarkdown, claudeStyleHooks, commandDocuments, commandMarkdown, enabledHooks, GROK_EVENT_MAP, grokMcpToml, ruleDocuments, skillArtifacts, } from "./shared.js";
3
+ import { nativeTools } from "./tools.js";
4
+ export const grokAdapter = {
5
+ descriptor: descriptor("grok-build", "Grok Build CLI", ["grok"], ["https://docs.x.ai/build/cli/reference", "https://docs.x.ai/build/features/skills-plugins-marketplaces", "https://docs.x.ai/build/features/mcp-servers", "https://docs.x.ai/build/features/hooks"], { instructions: "portable", rules: "portable", skills: "native", mcp: "native", hooks: "native", agents: "native", commands: "native" }, ["Project hooks require Grok workspace hook trust."]),
6
+ async build(context) {
7
+ const artifacts = [];
8
+ const diagnostics = [];
9
+ artifacts.push(...await skillArtifacts(context, ".grok/skills", "grok-build"));
10
+ const mcp = grokMcpToml(context);
11
+ if (mcp)
12
+ artifacts.push({ kind: "toml", path: ".grok/config.toml", owner: "grok-build", blocks: [{ marker: "mcp-servers", content: mcp }] });
13
+ if (enabledHooks(context).length > 0) {
14
+ const compiled = claudeStyleHooks(context, GROK_EVENT_MAP);
15
+ diagnostics.push(...compiled.diagnostics);
16
+ if (Object.keys(compiled.hooks).length > 0) {
17
+ artifacts.push({
18
+ kind: "json", path: ".grok/hooks/canonfig.json", owner: "grok-build", rootDefaults: { version: 1 },
19
+ operations: [{ kind: "managed-hooks", path: ["hooks"], hooks: compiled.hooks, marker: ".canonfig/.runtime/hook-runner.mjs" }],
20
+ });
21
+ }
22
+ }
23
+ for (const { agent, content } of await agentDocuments(context)) {
24
+ artifacts.push({ kind: "replace", path: `.grok/agents/${agent.id}.md`, owner: "grok-build", content: agentMarkdown(agent, content, nativeTools("grok-build", agent)) });
25
+ }
26
+ for (const { command, content } of await commandDocuments(context)) {
27
+ artifacts.push({ kind: "replace", path: `.grok/commands/${command.id}.md`, owner: "grok-build", content: commandMarkdown(command, content) });
28
+ }
29
+ for (const { rule, content } of await ruleDocuments(context)) {
30
+ if (rule.paths.length > 0) {
31
+ diagnostics.push({
32
+ level: "warning",
33
+ code: "RULE_SCOPE_LOST",
34
+ target: "grok-build",
35
+ path: `.canonfig/${rule.file}`,
36
+ message: `Grok project rules cannot preserve Canonfig's path scope for ${rule.id}; the native rule file was skipped and the scoped AGENTS.md bridge remains authoritative.`,
37
+ });
38
+ continue;
39
+ }
40
+ artifacts.push({ kind: "replace", path: `.grok/rules/${rule.id}.md`, owner: "grok-build", content });
41
+ }
42
+ return { artifacts, diagnostics };
43
+ },
44
+ };