@thinkingsage/kanon 0.8.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 (199) hide show
  1. package/CHANGELOG.md +410 -0
  2. package/LICENSE +21 -0
  3. package/README.md +168 -0
  4. package/bridge/mcp-server.cjs +14171 -0
  5. package/package.json +98 -0
  6. package/src/adapters/capabilities.ts +178 -0
  7. package/src/adapters/claude-code.ts +110 -0
  8. package/src/adapters/cline.ts +98 -0
  9. package/src/adapters/codex.ts +173 -0
  10. package/src/adapters/copilot.ts +106 -0
  11. package/src/adapters/cursor.ts +97 -0
  12. package/src/adapters/degradation.ts +95 -0
  13. package/src/adapters/index.ts +324 -0
  14. package/src/adapters/kiro-frontmatter.ts +139 -0
  15. package/src/adapters/kiro-inclusion.ts +86 -0
  16. package/src/adapters/kiro.ts +412 -0
  17. package/src/adapters/qdeveloper.ts +115 -0
  18. package/src/adapters/types.ts +81 -0
  19. package/src/adapters/windsurf.ts +96 -0
  20. package/src/admin.ts +283 -0
  21. package/src/asset-conventions.ts +118 -0
  22. package/src/attribution-backfill.ts +319 -0
  23. package/src/attribution-report.ts +95 -0
  24. package/src/attribution.ts +239 -0
  25. package/src/backends/github.ts +194 -0
  26. package/src/backends/http.ts +122 -0
  27. package/src/backends/index.ts +39 -0
  28. package/src/backends/local.ts +47 -0
  29. package/src/backends/s3.ts +157 -0
  30. package/src/backends/types.ts +59 -0
  31. package/src/base-cache.ts +270 -0
  32. package/src/browse-ui.ts +3754 -0
  33. package/src/browse.ts +1038 -0
  34. package/src/build.ts +1108 -0
  35. package/src/catalog.ts +204 -0
  36. package/src/cli-deprecated.ts +29 -0
  37. package/src/cli.ts +773 -0
  38. package/src/collection-admin.ts +287 -0
  39. package/src/collection-builder.ts +464 -0
  40. package/src/collections.ts +116 -0
  41. package/src/compatibility.ts +105 -0
  42. package/src/config.ts +743 -0
  43. package/src/eval/rubrics/kiro-progressive-steering.ts +841 -0
  44. package/src/eval.ts +1169 -0
  45. package/src/file-writer.ts +61 -0
  46. package/src/format-registry.ts +141 -0
  47. package/src/guild/auto-updater.ts +163 -0
  48. package/src/guild/backend-resolver.ts +49 -0
  49. package/src/guild/cli.ts +592 -0
  50. package/src/guild/collection-expander.ts +47 -0
  51. package/src/guild/global-cache.ts +247 -0
  52. package/src/guild/hook-generator.ts +100 -0
  53. package/src/guild/manifest.ts +154 -0
  54. package/src/guild/path-utils.ts +12 -0
  55. package/src/guild/sync.ts +622 -0
  56. package/src/guild/version-resolver.ts +42 -0
  57. package/src/help/metadata.ts +445 -0
  58. package/src/help/renderer.ts +265 -0
  59. package/src/help/typo-suggester.ts +25 -0
  60. package/src/hooks/expression.ts +493 -0
  61. package/src/hooks/pipeline.ts +141 -0
  62. package/src/import.ts +773 -0
  63. package/src/importers/claude-code.ts +134 -0
  64. package/src/importers/cline.ts +103 -0
  65. package/src/importers/codex.ts +140 -0
  66. package/src/importers/copilot.ts +103 -0
  67. package/src/importers/cursor.ts +105 -0
  68. package/src/importers/index.ts +390 -0
  69. package/src/importers/kiro.ts +110 -0
  70. package/src/importers/qdeveloper.ts +103 -0
  71. package/src/importers/types.ts +54 -0
  72. package/src/importers/windsurf.ts +104 -0
  73. package/src/install.ts +1005 -0
  74. package/src/manifest-admin.ts +306 -0
  75. package/src/mcp-bridge.ts +240 -0
  76. package/src/mutation/delta.ts +50 -0
  77. package/src/mutation/history.ts +66 -0
  78. package/src/mutation/operators.ts +524 -0
  79. package/src/mutation/runner.ts +332 -0
  80. package/src/new.ts +106 -0
  81. package/src/outcomes/collision.ts +127 -0
  82. package/src/outcomes/normalize.ts +208 -0
  83. package/src/outcomes/registry.ts +173 -0
  84. package/src/parser.ts +446 -0
  85. package/src/provenance-backfill-cli.ts +319 -0
  86. package/src/provenance-backfill.ts +520 -0
  87. package/src/publish.ts +354 -0
  88. package/src/reconcile-orchestrator.ts +502 -0
  89. package/src/reconcile-report-renderer.ts +176 -0
  90. package/src/resolve-body.ts +15 -0
  91. package/src/rosetta/builtins/compatibility-profiles.ts +297 -0
  92. package/src/rosetta/builtins/contracts.ts +1033 -0
  93. package/src/rosetta/builtins/pretty-printers/claude-code-native.ts +122 -0
  94. package/src/rosetta/builtins/pretty-printers/cline-native.ts +50 -0
  95. package/src/rosetta/builtins/pretty-printers/codex-native.ts +127 -0
  96. package/src/rosetta/builtins/pretty-printers/copilot-native.ts +50 -0
  97. package/src/rosetta/builtins/pretty-printers/cursor-native.ts +50 -0
  98. package/src/rosetta/builtins/pretty-printers/index.ts +81 -0
  99. package/src/rosetta/builtins/pretty-printers/kiro-native.ts +166 -0
  100. package/src/rosetta/builtins/pretty-printers/kiro-power.ts +108 -0
  101. package/src/rosetta/builtins/pretty-printers/kiro-skill.ts +88 -0
  102. package/src/rosetta/builtins/pretty-printers/qdeveloper-native.ts +51 -0
  103. package/src/rosetta/builtins/pretty-printers/superpowers.ts +97 -0
  104. package/src/rosetta/builtins/pretty-printers/windsurf-native.ts +50 -0
  105. package/src/rosetta/builtins/sources/claude-code-native.ts +348 -0
  106. package/src/rosetta/builtins/sources/cline-native.ts +176 -0
  107. package/src/rosetta/builtins/sources/codex-native.ts +343 -0
  108. package/src/rosetta/builtins/sources/copilot-native.ts +178 -0
  109. package/src/rosetta/builtins/sources/cursor-native.ts +176 -0
  110. package/src/rosetta/builtins/sources/index.ts +95 -0
  111. package/src/rosetta/builtins/sources/kiro-native.ts +462 -0
  112. package/src/rosetta/builtins/sources/kiro-power.ts +285 -0
  113. package/src/rosetta/builtins/sources/kiro-skill.ts +230 -0
  114. package/src/rosetta/builtins/sources/qdeveloper-native.ts +181 -0
  115. package/src/rosetta/builtins/sources/superpowers.ts +240 -0
  116. package/src/rosetta/builtins/sources/windsurf-native.ts +176 -0
  117. package/src/rosetta/builtins/targets/claude-code.ts +181 -0
  118. package/src/rosetta/builtins/targets/cline.ts +87 -0
  119. package/src/rosetta/builtins/targets/codex.ts +226 -0
  120. package/src/rosetta/builtins/targets/copilot.ts +103 -0
  121. package/src/rosetta/builtins/targets/cursor.ts +87 -0
  122. package/src/rosetta/builtins/targets/index.ts +60 -0
  123. package/src/rosetta/builtins/targets/kiro.ts +278 -0
  124. package/src/rosetta/builtins/targets/qdeveloper.ts +103 -0
  125. package/src/rosetta/builtins/targets/windsurf.ts +87 -0
  126. package/src/rosetta/canonical.ts +729 -0
  127. package/src/rosetta/compatibility.ts +432 -0
  128. package/src/rosetta/contracts.ts +329 -0
  129. package/src/rosetta/detector.ts +724 -0
  130. package/src/rosetta/diagnostics.ts +630 -0
  131. package/src/rosetta/engine-bootstrap.ts +103 -0
  132. package/src/rosetta/engine.ts +744 -0
  133. package/src/rosetta/index.ts +381 -0
  134. package/src/rosetta/inspection.ts +530 -0
  135. package/src/rosetta/plan.ts +448 -0
  136. package/src/rosetta/provenance-digest.ts +369 -0
  137. package/src/rosetta/reconcile.ts +812 -0
  138. package/src/rosetta/redaction.ts +467 -0
  139. package/src/rosetta/registry.ts +712 -0
  140. package/src/rosetta/renderers.ts +571 -0
  141. package/src/rosetta/request-guard.ts +335 -0
  142. package/src/rosetta/resolution.ts +419 -0
  143. package/src/rosetta/source-accounting.ts +233 -0
  144. package/src/rosetta/templates.ts +129 -0
  145. package/src/rosetta-cli.ts +717 -0
  146. package/src/rosetta-docs-generator.ts +793 -0
  147. package/src/rosetta-profiles-cli.ts +367 -0
  148. package/src/schemas.ts +1712 -0
  149. package/src/spec-coordination.ts +1141 -0
  150. package/src/temper.ts +747 -0
  151. package/src/template-bundle-loader.ts +312 -0
  152. package/src/template-engine.ts +53 -0
  153. package/src/translation-application-policy.ts +496 -0
  154. package/src/translation-orchestrator.ts +1013 -0
  155. package/src/translation-plan-applier.ts +473 -0
  156. package/src/tutorial.ts +305 -0
  157. package/src/validate.ts +1093 -0
  158. package/src/versioning.ts +553 -0
  159. package/src/wizard.ts +660 -0
  160. package/src/workspace.ts +237 -0
  161. package/templates/eval-contexts/claude-code.md.njk +6 -0
  162. package/templates/eval-contexts/cline.md.njk +6 -0
  163. package/templates/eval-contexts/copilot.md.njk +6 -0
  164. package/templates/eval-contexts/cursor.md.njk +6 -0
  165. package/templates/eval-contexts/kiro.md.njk +10 -0
  166. package/templates/eval-contexts/qdeveloper.md.njk +6 -0
  167. package/templates/eval-contexts/windsurf.md.njk +6 -0
  168. package/templates/harness-adapters/_base/attribution-footer.md.njk +17 -0
  169. package/templates/harness-adapters/_base/base.md.njk +16 -0
  170. package/templates/harness-adapters/claude-code/claude.md.njk +1 -0
  171. package/templates/harness-adapters/claude-code/mcp.json.njk +1 -0
  172. package/templates/harness-adapters/claude-code/settings.json.njk +1 -0
  173. package/templates/harness-adapters/claude-code/skill-library-index.md.njk +13 -0
  174. package/templates/harness-adapters/claude-code/skill.md.njk +19 -0
  175. package/templates/harness-adapters/cline/hook.sh.njk +4 -0
  176. package/templates/harness-adapters/cline/mcp.json.njk +1 -0
  177. package/templates/harness-adapters/cline/rule.md.njk +1 -0
  178. package/templates/harness-adapters/codex/agents-md.md.njk +6 -0
  179. package/templates/harness-adapters/codex/agents-pointer.md.njk +16 -0
  180. package/templates/harness-adapters/codex/skill.md.njk +27 -0
  181. package/templates/harness-adapters/copilot/agents.md.njk +1 -0
  182. package/templates/harness-adapters/copilot/instructions.md.njk +1 -0
  183. package/templates/harness-adapters/copilot/scoped.md.njk +6 -0
  184. package/templates/harness-adapters/cursor/mcp.json.njk +1 -0
  185. package/templates/harness-adapters/cursor/rule.md.njk +6 -0
  186. package/templates/harness-adapters/kiro/hook.json.njk +1 -0
  187. package/templates/harness-adapters/kiro/mcp.json.njk +1 -0
  188. package/templates/harness-adapters/kiro/power-steering.md.njk +3 -0
  189. package/templates/harness-adapters/kiro/power.md.njk +12 -0
  190. package/templates/harness-adapters/kiro/steering.md.njk +16 -0
  191. package/templates/harness-adapters/qdeveloper/agent.md.njk +1 -0
  192. package/templates/harness-adapters/qdeveloper/mcp.json.njk +1 -0
  193. package/templates/harness-adapters/qdeveloper/rule.md.njk +1 -0
  194. package/templates/harness-adapters/windsurf/mcp.json.njk +1 -0
  195. package/templates/harness-adapters/windsurf/rule.md.njk +1 -0
  196. package/templates/harness-adapters/windsurf/workflow.md.njk +1 -0
  197. package/templates/knowledge/hooks.yaml.njk +4 -0
  198. package/templates/knowledge/knowledge.md.njk +53 -0
  199. package/templates/knowledge/mcp-servers.yaml.njk +2 -0
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@thinkingsage/kanon",
3
+ "version": "0.8.0",
4
+ "description": "Write knowledge once, compile to every AI coding assistant harness",
5
+ "type": "module",
6
+ "bin": {
7
+ "kanon": "./src/cli.ts",
8
+ "forge": "./src/cli-deprecated.ts"
9
+ },
10
+ "main": "./src/cli.ts",
11
+ "exports": {
12
+ ".": "./src/cli.ts",
13
+ "./schemas": "./src/schemas.ts",
14
+ "./parser": "./src/parser.ts",
15
+ "./adapters": "./src/adapters/index.ts"
16
+ },
17
+ "engines": {
18
+ "bun": ">=1.0.0"
19
+ },
20
+ "scripts": {
21
+ "dev": "bun run src/cli.ts",
22
+ "build": "bun build src/cli.ts --compile --external chromium-bidi --outfile forge",
23
+ "build:kanon": "bun build src/cli.ts --compile --external chromium-bidi --outfile kanon",
24
+ "build:bridge": "bun build src/mcp-bridge.ts --outfile bridge/mcp-server.cjs --target node --format cjs",
25
+ "build:skills": "bun run scripts/generate-plugin-skills.ts",
26
+ "build:souk-compass": "bun --cwd mcp-servers/souk-compass run build",
27
+ "prepublishOnly": "bun test && biome check src/cli.ts src/adapters/ src/backends/ src/guild/ src/help/ src/importers/ src/*.ts templates/ bridge/",
28
+ "test": "bun test",
29
+ "validate:rosetta": "bun run scripts/validate-rosetta.ts",
30
+ "lint": "biome check .",
31
+ "lint:fix": "biome check --write .",
32
+ "format": "biome format --write .",
33
+ "changelog:new": "bun run scripts/changelog-fragment.ts",
34
+ "changelog:compile": "bun run scripts/changelog-compile.ts",
35
+ "changelog:draft": "bun run scripts/changelog-compile.ts --draft",
36
+ "release": "bun run scripts/release-tag.ts",
37
+ "bundle:analyze": "rollup --config rollup.config.js",
38
+ "docs:rosetta": "bun run src/rosetta-docs-generator.ts"
39
+ },
40
+ "keywords": [
41
+ "ai",
42
+ "coding-assistant",
43
+ "knowledge",
44
+ "skills",
45
+ "kiro",
46
+ "claude",
47
+ "copilot",
48
+ "cursor",
49
+ "windsurf",
50
+ "cline",
51
+ "qdeveloper",
52
+ "cli",
53
+ "compiler",
54
+ "bun"
55
+ ],
56
+ "dependencies": {
57
+ "@clack/prompts": "^1.7.0",
58
+ "chalk": "^6.0.0",
59
+ "commander": "^15.0.0",
60
+ "gray-matter": "^4.0.3",
61
+ "js-yaml": "^5.4.1",
62
+ "nunjucks": "^3.2.4",
63
+ "promptfoo": "^0.122.0",
64
+ "tiktoken": "^1.0.22",
65
+ "zod": "^4.4.3"
66
+ },
67
+ "devDependencies": {
68
+ "@biomejs/biome": "^2.5.7",
69
+ "@codecov/rollup-plugin": "^2.0.1",
70
+ "@rollup/plugin-node-resolve": "^16.0.3",
71
+ "@rollup/plugin-typescript": "^12.3.0",
72
+ "@types/bun": "^1.3.14",
73
+ "@types/nunjucks": "^3.2.6",
74
+ "fast-check": "^4.9.0",
75
+ "rollup": "^4.62.4",
76
+ "typescript": "^7.0.2"
77
+ },
78
+ "license": "MIT",
79
+ "files": [
80
+ "src",
81
+ "!src/**/__tests__",
82
+ "!src/**/*.test.ts",
83
+ "!src/**/*.property.test.ts",
84
+ "templates",
85
+ "bridge/mcp-server.cjs",
86
+ "README.md",
87
+ "LICENSE",
88
+ "CHANGELOG.md"
89
+ ],
90
+ "repository": {
91
+ "type": "git",
92
+ "url": "https://github.com/jhu-sheridan-libraries/agentic-skill-forge.git"
93
+ },
94
+ "homepage": "https://github.com/jhu-sheridan-libraries/agentic-skill-forge#readme",
95
+ "bugs": {
96
+ "url": "https://github.com/jhu-sheridan-libraries/agentic-skill-forge/issues"
97
+ }
98
+ }
@@ -0,0 +1,178 @@
1
+ import { z } from "zod";
2
+ import type {
3
+ CapabilityEntry,
4
+ DegradationStrategy,
5
+ HarnessName,
6
+ } from "../schemas";
7
+ import { CapabilityEntrySchema, SUPPORTED_HARNESSES } from "../schemas";
8
+
9
+ // --- Capability Names ---
10
+
11
+ export const HARNESS_CAPABILITIES = [
12
+ "hooks",
13
+ "mcp",
14
+ "path_scoping",
15
+ "workflows",
16
+ "toggleable_rules",
17
+ "agents",
18
+ "file_match_inclusion",
19
+ "system_prompt_merging",
20
+ ] as const;
21
+
22
+ export type HarnessCapabilityName = (typeof HARNESS_CAPABILITIES)[number];
23
+
24
+ // --- Matrix Type ---
25
+
26
+ export type CapabilityMatrix = Record<
27
+ HarnessName,
28
+ Record<HarnessCapabilityName, CapabilityEntry>
29
+ >;
30
+
31
+ // --- Capability Matrix Constant ---
32
+
33
+ export const CAPABILITY_MATRIX: CapabilityMatrix = {
34
+ kiro: {
35
+ hooks: { support: "full" },
36
+ mcp: { support: "full" },
37
+ path_scoping: { support: "full" },
38
+ workflows: { support: "full" },
39
+ toggleable_rules: { support: "full" },
40
+ agents: { support: "partial", degradation: "inline" },
41
+ file_match_inclusion: { support: "full" },
42
+ system_prompt_merging: { support: "full" },
43
+ },
44
+ "claude-code": {
45
+ hooks: { support: "partial", degradation: "inline" },
46
+ mcp: { support: "full" },
47
+ path_scoping: { support: "none", degradation: "comment" },
48
+ workflows: { support: "none", degradation: "inline" },
49
+ toggleable_rules: { support: "none", degradation: "omit" },
50
+ agents: { support: "none", degradation: "omit" },
51
+ file_match_inclusion: { support: "none", degradation: "omit" },
52
+ system_prompt_merging: { support: "full" },
53
+ },
54
+ codex: {
55
+ // Codex has no canonical event-hook system (only the notify shell hook).
56
+ hooks: { support: "none", degradation: "inline" },
57
+ // MCP servers are first-class via ~/.codex/config.toml [mcp_servers].
58
+ mcp: { support: "full" },
59
+ // Directory scoping is via nested AGENTS.md, not glob file patterns.
60
+ path_scoping: { support: "none", degradation: "comment" },
61
+ // Workflows map naturally onto skill phase files under references/.
62
+ workflows: { support: "full" },
63
+ toggleable_rules: { support: "none", degradation: "omit" },
64
+ // Sub-agents exist via profiles / `codex exec`, but not as declarative files.
65
+ agents: { support: "partial", degradation: "inline" },
66
+ file_match_inclusion: { support: "none", degradation: "omit" },
67
+ // AGENTS.md is merged into the system prompt every session.
68
+ system_prompt_merging: { support: "full" },
69
+ },
70
+ copilot: {
71
+ hooks: { support: "none", degradation: "inline" },
72
+ mcp: { support: "none", degradation: "comment" },
73
+ path_scoping: { support: "full" },
74
+ workflows: { support: "none", degradation: "inline" },
75
+ toggleable_rules: { support: "none", degradation: "omit" },
76
+ agents: { support: "full" },
77
+ file_match_inclusion: { support: "full" },
78
+ system_prompt_merging: { support: "none", degradation: "inline" },
79
+ },
80
+ cursor: {
81
+ hooks: { support: "none", degradation: "inline" },
82
+ mcp: { support: "full" },
83
+ path_scoping: { support: "full" },
84
+ workflows: { support: "none", degradation: "inline" },
85
+ toggleable_rules: { support: "full" },
86
+ agents: { support: "none", degradation: "omit" },
87
+ file_match_inclusion: { support: "full" },
88
+ system_prompt_merging: { support: "none", degradation: "inline" },
89
+ },
90
+ windsurf: {
91
+ hooks: { support: "none", degradation: "inline" },
92
+ mcp: { support: "full" },
93
+ path_scoping: { support: "full" },
94
+ workflows: { support: "full" },
95
+ toggleable_rules: { support: "none", degradation: "omit" },
96
+ agents: { support: "none", degradation: "omit" },
97
+ file_match_inclusion: { support: "full" },
98
+ system_prompt_merging: { support: "none", degradation: "inline" },
99
+ },
100
+ cline: {
101
+ hooks: { support: "partial", degradation: "inline" },
102
+ mcp: { support: "full" },
103
+ path_scoping: { support: "none", degradation: "comment" },
104
+ workflows: { support: "none", degradation: "inline" },
105
+ toggleable_rules: { support: "none", degradation: "omit" },
106
+ agents: { support: "none", degradation: "omit" },
107
+ file_match_inclusion: { support: "none", degradation: "omit" },
108
+ system_prompt_merging: { support: "none", degradation: "inline" },
109
+ },
110
+ qdeveloper: {
111
+ hooks: { support: "none", degradation: "inline" },
112
+ mcp: { support: "full" },
113
+ path_scoping: { support: "full" },
114
+ workflows: { support: "none", degradation: "inline" },
115
+ toggleable_rules: { support: "none", degradation: "omit" },
116
+ agents: { support: "full" },
117
+ file_match_inclusion: { support: "full" },
118
+ system_prompt_merging: { support: "none", degradation: "inline" },
119
+ },
120
+ };
121
+
122
+ // --- Zod Validation at Module Load Time ---
123
+
124
+ const CapabilityRowSchema = z.record(
125
+ z.enum(HARNESS_CAPABILITIES),
126
+ CapabilityEntrySchema,
127
+ );
128
+
129
+ const CapabilityMatrixSchema = z.record(
130
+ z.enum(SUPPORTED_HARNESSES),
131
+ CapabilityRowSchema,
132
+ );
133
+
134
+ // Validate the matrix at module load time — throws if invalid
135
+ CapabilityMatrixSchema.parse(CAPABILITY_MATRIX);
136
+
137
+ // --- Query Functions ---
138
+
139
+ /** Get all capability entries for a harness */
140
+ export function getCapabilities(
141
+ harness: HarnessName,
142
+ ): Record<HarnessCapabilityName, CapabilityEntry> {
143
+ return CAPABILITY_MATRIX[harness];
144
+ }
145
+
146
+ /** Check if a specific capability is fully supported */
147
+ export function isSupported(
148
+ harness: HarnessName,
149
+ capability: HarnessCapabilityName,
150
+ ): boolean {
151
+ return CAPABILITY_MATRIX[harness][capability].support === "full";
152
+ }
153
+
154
+ /** Get degradation strategy for a capability, or undefined if fully supported */
155
+ export function getDegradation(
156
+ harness: HarnessName,
157
+ capability: HarnessCapabilityName,
158
+ ): DegradationStrategy | undefined {
159
+ const entry = CAPABILITY_MATRIX[harness][capability];
160
+ return entry.support === "full" ? undefined : entry.degradation;
161
+ }
162
+
163
+ /** Validate matrix harnesses are in sync with adapter registry and format registry */
164
+ export function validateMatrixSync(
165
+ matrixHarnesses: string[],
166
+ registryHarnesses: string[],
167
+ formatRegistryHarnesses: string[],
168
+ ): { missing: string[]; extra: string[] } {
169
+ const matrixSet = new Set(matrixHarnesses);
170
+ const allRequired = new Set([
171
+ ...registryHarnesses,
172
+ ...formatRegistryHarnesses,
173
+ ]);
174
+ return {
175
+ missing: [...allRequired].filter((h) => !matrixSet.has(h)),
176
+ extra: matrixHarnesses.filter((h) => !allRequired.has(h)),
177
+ };
178
+ }
@@ -0,0 +1,110 @@
1
+ import { resolveFormat } from "../format-registry";
2
+ import { renderTemplate } from "../template-engine";
3
+ import type { HarnessCapabilityName } from "./capabilities";
4
+ import { applyDegradation } from "./degradation";
5
+ import type { AdapterWarning, HarnessAdapter, OutputFile } from "./types";
6
+ import { buildMcpConfig } from "./types";
7
+
8
+ const SUPPORTED_CLAUDE_EVENTS = new Set(["agent_stop"]);
9
+
10
+ export const claudeCodeAdapter: HarnessAdapter = (
11
+ artifact,
12
+ templateEnv,
13
+ context?,
14
+ ) => {
15
+ const files: OutputFile[] = [];
16
+ const warnings: AdapterWarning[] = [];
17
+
18
+ // Capability degradation checks
19
+ if (context) {
20
+ const checks: Array<{
21
+ capability: HarnessCapabilityName;
22
+ hasFeature: boolean;
23
+ }> = [
24
+ { capability: "hooks", hasFeature: artifact.hooks.length > 0 },
25
+ { capability: "mcp", hasFeature: artifact.mcpServers.length > 0 },
26
+ { capability: "workflows", hasFeature: artifact.workflows.length > 0 },
27
+ ];
28
+ for (const { capability, hasFeature } of checks) {
29
+ if (!hasFeature) continue;
30
+ const entry = context.capabilities[capability];
31
+ if (entry.support === "full") continue;
32
+ if (context.strict) {
33
+ warnings.push({
34
+ artifactName: artifact.name,
35
+ harnessName: "claude-code",
36
+ message: `Strict mode: capability ${capability} not supported by harness claude-code`,
37
+ });
38
+ return { files, warnings };
39
+ }
40
+ const degradation = applyDegradation(
41
+ entry.degradation ?? "inline",
42
+ capability,
43
+ artifact,
44
+ "claude-code",
45
+ );
46
+ warnings.push(...degradation.warnings);
47
+ }
48
+ }
49
+
50
+ const harnessConfig = (artifact.frontmatter as Record<string, unknown>)[
51
+ "harness-config"
52
+ ] as Record<string, unknown> | undefined;
53
+ const claudeCodeConfig = (harnessConfig?.["claude-code"] ?? {}) as Record<
54
+ string,
55
+ unknown
56
+ >;
57
+ resolveFormat("claude-code", claudeCodeConfig);
58
+
59
+ // Generate CLAUDE.md
60
+ const claudeContent = renderTemplate(
61
+ templateEnv,
62
+ "claude-code/claude.md.njk",
63
+ { artifact },
64
+ );
65
+ files.push({ relativePath: "CLAUDE.md", content: claudeContent });
66
+
67
+ // Translate agent_stop + run_command hooks to .claude/settings.json
68
+ const stopHooks: Array<{ command: string }> = [];
69
+ for (const hook of artifact.hooks) {
70
+ if (!SUPPORTED_CLAUDE_EVENTS.has(hook.event)) {
71
+ warnings.push({
72
+ artifactName: artifact.name,
73
+ harnessName: "claude-code",
74
+ message: `Skipping hook "${hook.name}": event "${hook.event}" not supported by Claude Code`,
75
+ });
76
+ continue;
77
+ }
78
+ if (hook.action.type === "run_command") {
79
+ stopHooks.push({ command: hook.action.command });
80
+ }
81
+ }
82
+
83
+ if (stopHooks.length > 0) {
84
+ const settings = {
85
+ hooks: {
86
+ stop: stopHooks.map((h) => ({ type: "command", command: h.command })),
87
+ },
88
+ };
89
+ const settingsContent = renderTemplate(
90
+ templateEnv,
91
+ "claude-code/settings.json.njk",
92
+ { settings },
93
+ );
94
+ files.push({
95
+ relativePath: ".claude/settings.json",
96
+ content: settingsContent,
97
+ });
98
+ }
99
+
100
+ // Generate .claude/mcp.json
101
+ if (artifact.mcpServers.length > 0) {
102
+ const mcpConfig = buildMcpConfig(artifact.mcpServers);
103
+ const mcpContent = renderTemplate(templateEnv, "claude-code/mcp.json.njk", {
104
+ mcpConfig,
105
+ });
106
+ files.push({ relativePath: ".claude/mcp.json", content: mcpContent });
107
+ }
108
+
109
+ return { files, warnings };
110
+ };
@@ -0,0 +1,98 @@
1
+ import { resolveFormat } from "../format-registry";
2
+ import { renderTemplate } from "../template-engine";
3
+ import type { HarnessCapabilityName } from "./capabilities";
4
+ import { applyDegradation } from "./degradation";
5
+ import type { AdapterWarning, HarnessAdapter, OutputFile } from "./types";
6
+ import { buildMcpConfig } from "./types";
7
+
8
+ export const clineAdapter: HarnessAdapter = (
9
+ artifact,
10
+ templateEnv,
11
+ context?,
12
+ ) => {
13
+ const files: OutputFile[] = [];
14
+ const warnings: AdapterWarning[] = [];
15
+
16
+ // Capability degradation checks
17
+ if (context) {
18
+ const checks: Array<{
19
+ capability: HarnessCapabilityName;
20
+ hasFeature: boolean;
21
+ }> = [
22
+ { capability: "hooks", hasFeature: artifact.hooks.length > 0 },
23
+ { capability: "mcp", hasFeature: artifact.mcpServers.length > 0 },
24
+ { capability: "workflows", hasFeature: artifact.workflows.length > 0 },
25
+ ];
26
+ for (const { capability, hasFeature } of checks) {
27
+ if (!hasFeature) continue;
28
+ const entry = context.capabilities[capability];
29
+ if (entry.support === "full") continue;
30
+ if (context.strict) {
31
+ warnings.push({
32
+ artifactName: artifact.name,
33
+ harnessName: "cline",
34
+ message: `Strict mode: capability ${capability} not supported by harness cline`,
35
+ });
36
+ return { files, warnings };
37
+ }
38
+ const degradation = applyDegradation(
39
+ entry.degradation ?? "inline",
40
+ capability,
41
+ artifact,
42
+ "cline",
43
+ );
44
+ warnings.push(...degradation.warnings);
45
+ }
46
+ }
47
+
48
+ const harnessConfig = (artifact.frontmatter as Record<string, unknown>)[
49
+ "harness-config"
50
+ ] as Record<string, unknown> | undefined;
51
+ const clineConfig = (harnessConfig?.cline ?? {}) as Record<string, unknown>;
52
+ resolveFormat("cline", clineConfig);
53
+
54
+ // Generate .clinerules/<artifact>.md
55
+ const ruleContent = renderTemplate(templateEnv, "cline/rule.md.njk", {
56
+ artifact,
57
+ });
58
+ files.push({
59
+ relativePath: `.clinerules/${artifact.name}.md`,
60
+ content: ruleContent,
61
+ });
62
+
63
+ // Translate hooks to executable shell scripts
64
+ for (const hook of artifact.hooks) {
65
+ if (hook.action.type === "run_command") {
66
+ const hookContent = renderTemplate(templateEnv, "cline/hook.sh.njk", {
67
+ hook: {
68
+ name: hook.name,
69
+ description: hook.description || "",
70
+ command: hook.action.command,
71
+ },
72
+ });
73
+ const hookName = hook.name.toLowerCase().replace(/\s+/g, "-");
74
+ files.push({
75
+ relativePath: `.clinerules/hooks/${hookName}.sh`,
76
+ content: hookContent,
77
+ executable: true,
78
+ });
79
+ } else if (hook.action.type === "ask_agent") {
80
+ warnings.push({
81
+ artifactName: artifact.name,
82
+ harnessName: "cline",
83
+ message: `Skipping hook "${hook.name}": ask_agent actions are translated as shell scripts only for run_command`,
84
+ });
85
+ }
86
+ }
87
+
88
+ // Generate VS Code MCP configuration
89
+ if (artifact.mcpServers.length > 0) {
90
+ const mcpConfig = buildMcpConfig(artifact.mcpServers);
91
+ const mcpContent = renderTemplate(templateEnv, "cline/mcp.json.njk", {
92
+ mcpConfig,
93
+ });
94
+ files.push({ relativePath: ".clinerules/mcp.json", content: mcpContent });
95
+ }
96
+
97
+ return { files, warnings };
98
+ };
@@ -0,0 +1,173 @@
1
+ import { resolveFormat } from "../format-registry";
2
+ import type { McpServerDefinition } from "../schemas";
3
+ import { isStdioServer } from "../schemas";
4
+ import { renderTemplate } from "../template-engine";
5
+ import type { HarnessCapabilityName } from "./capabilities";
6
+ import { applyDegradation } from "./degradation";
7
+ import type { AdapterWarning, HarnessAdapter, OutputFile } from "./types";
8
+
9
+ /** TOML string literal — escape backslashes and double quotes. */
10
+ function tomlString(value: string): string {
11
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
12
+ }
13
+
14
+ /** TOML dotted-table key segment. */
15
+ function tomlKey(value: string): string {
16
+ return /^[A-Za-z0-9_-]+$/.test(value) ? value : tomlString(value);
17
+ }
18
+
19
+ /** TOML inline array of strings. */
20
+ function tomlStringArray(values: string[]): string {
21
+ return `[${values.map((v) => tomlString(v)).join(", ")}]`;
22
+ }
23
+
24
+ /** TOML inline table of string env vars. */
25
+ function tomlEnvTable(env: Record<string, string>): string {
26
+ const pairs = Object.entries(env).map(([k, v]) => `${k} = ${tomlString(v)}`);
27
+ return `{ ${pairs.join(", ")} }`;
28
+ }
29
+
30
+ /**
31
+ * Serialize canonical MCP server definitions into Codex `config.toml` form.
32
+ * Codex registers servers under `[mcp_servers.<name>]` tables.
33
+ */
34
+ function buildCodexMcpToml(servers: McpServerDefinition[]): string {
35
+ const blocks: string[] = [
36
+ "# Generated by Kanon — merge into ~/.codex/config.toml or .codex/config.toml",
37
+ ];
38
+ for (const server of servers) {
39
+ const lines = [`[mcp_servers.${tomlKey(server.name)}]`];
40
+ if (isStdioServer(server)) {
41
+ lines.push(`command = ${tomlString(server.command)}`);
42
+ if (server.args.length > 0) {
43
+ lines.push(`args = ${tomlStringArray(server.args)}`);
44
+ }
45
+ if (Object.keys(server.env).length > 0) {
46
+ lines.push(`env = ${tomlEnvTable(server.env)}`);
47
+ }
48
+ } else {
49
+ // URL-based (SSE / streamable HTTP) servers.
50
+ lines.push(`url = ${tomlString(server.url)}`);
51
+ if (server.env && Object.keys(server.env).length > 0) {
52
+ lines.push(`env = ${tomlEnvTable(server.env)}`);
53
+ }
54
+ }
55
+ if (server.timeout) {
56
+ lines.push(`startup_timeout_ms = ${server.timeout}`);
57
+ }
58
+ blocks.push(lines.join("\n"));
59
+ }
60
+ return `${blocks.join("\n\n")}\n`;
61
+ }
62
+
63
+ export const codexAdapter: HarnessAdapter = (
64
+ artifact,
65
+ templateEnv,
66
+ context?,
67
+ ) => {
68
+ const files: OutputFile[] = [];
69
+ const warnings: AdapterWarning[] = [];
70
+ const degradedSections: string[] = [];
71
+
72
+ // Capability degradation checks
73
+ if (context) {
74
+ const checks: Array<{
75
+ capability: HarnessCapabilityName;
76
+ hasFeature: boolean;
77
+ }> = [
78
+ { capability: "hooks", hasFeature: artifact.hooks.length > 0 },
79
+ { capability: "mcp", hasFeature: artifact.mcpServers.length > 0 },
80
+ { capability: "workflows", hasFeature: artifact.workflows.length > 0 },
81
+ ];
82
+ for (const { capability, hasFeature } of checks) {
83
+ if (!hasFeature) continue;
84
+ const entry = context.capabilities[capability];
85
+ if (entry.support === "full") continue;
86
+ const degradation = applyDegradation(
87
+ entry.degradation ?? "inline",
88
+ capability,
89
+ artifact,
90
+ "codex",
91
+ );
92
+ warnings.push(...degradation.warnings);
93
+ if (context.strict) {
94
+ warnings.push({
95
+ artifactName: artifact.name,
96
+ harnessName: "codex",
97
+ message: `Strict mode: capability ${capability} is degraded for harness codex`,
98
+ });
99
+ }
100
+ if (degradation.inlineText) {
101
+ degradedSections.push(degradation.inlineText);
102
+ }
103
+ }
104
+ }
105
+
106
+ const harnessConfig = (artifact.frontmatter as Record<string, unknown>)[
107
+ "harness-config"
108
+ ] as Record<string, unknown> | undefined;
109
+ const codexConfig = (harnessConfig?.codex ?? {}) as Record<string, unknown>;
110
+ const { format } = resolveFormat("codex", codexConfig);
111
+ const skillName = artifact.name;
112
+ const renderContext = {
113
+ artifact,
114
+ degradedSections,
115
+ };
116
+
117
+ if (format === "skill") {
118
+ // Native Codex skill discovery path + always-loaded AGENTS.md pointer.
119
+ const skillContent = renderTemplate(
120
+ templateEnv,
121
+ "codex/skill.md.njk",
122
+ renderContext,
123
+ );
124
+ files.push({
125
+ relativePath: `.codex/skills/${skillName}/SKILL.md`,
126
+ content: skillContent,
127
+ });
128
+
129
+ // Workflow phase files live under references/ for progressive disclosure.
130
+ for (const wf of artifact.workflows) {
131
+ files.push({
132
+ relativePath: `.codex/skills/${skillName}/references/${wf.filename}`,
133
+ content: wf.content,
134
+ });
135
+ }
136
+
137
+ const pointerContent = renderTemplate(
138
+ templateEnv,
139
+ "codex/agents-pointer.md.njk",
140
+ renderContext,
141
+ );
142
+ files.push({ relativePath: "AGENTS.md", content: pointerContent });
143
+ } else {
144
+ // "agents-md": a repo-wide AGENTS.md carrying the full body.
145
+ const agentsContent = renderTemplate(
146
+ templateEnv,
147
+ "codex/agents-md.md.njk",
148
+ renderContext,
149
+ );
150
+ files.push({ relativePath: "AGENTS.md", content: agentsContent });
151
+ }
152
+
153
+ // MCP servers → .codex/config.toml
154
+ if (artifact.mcpServers.length > 0) {
155
+ files.push({
156
+ relativePath: ".codex/config.toml",
157
+ content: buildCodexMcpToml(artifact.mcpServers),
158
+ });
159
+ }
160
+
161
+ // Codex has no canonical event-hook system (only the `notify` shell hook),
162
+ // so declarative hooks are skipped.
163
+ if (artifact.hooks.length > 0) {
164
+ warnings.push({
165
+ artifactName: artifact.name,
166
+ harnessName: "codex",
167
+ message:
168
+ "Codex has no declarative event-hook system; hook definitions were rendered as manual guidance",
169
+ });
170
+ }
171
+
172
+ return { files, warnings };
173
+ };