@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
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Rosetta Stone — Built-in Compatibility Profiles
3
+ *
4
+ * Explicit, complete compatibility profiles for every built-in harness variant,
5
+ * seeded from `ASSET_HARNESS_COMPATIBILITY` and `CAPABILITY_MATRIX`. These
6
+ * profiles are the single authority during migration; later the legacy constants
7
+ * will be generated from these contracts so there is one source of truth.
8
+ *
9
+ * CONSTRAINTS:
10
+ * - NO filesystem, process, clock, random, Git, or network imports
11
+ * - Pure data only — no side effects
12
+ * - Profiles MUST cover ALL 19 canonical capabilities
13
+ * - Deterministic ordering via codePointCompare
14
+ *
15
+ * Requirements: 7.1, 7.2, 7.9, 14.5
16
+ */
17
+
18
+ import {
19
+ CAPABILITY_MATRIX,
20
+ type HarnessCapabilityName,
21
+ } from "../../adapters/capabilities";
22
+ import {
23
+ ASSET_HARNESS_COMPATIBILITY,
24
+ type CompatibilityLevel,
25
+ } from "../../compatibility";
26
+ import type {
27
+ CanonicalCapability,
28
+ HarnessName,
29
+ RosettaCompatibilityEntry,
30
+ RosettaCompatibilityProfile,
31
+ } from "../../schemas";
32
+ import { CanonicalCapabilitySchema } from "../../schemas";
33
+ import { codePointCompare } from "../contracts";
34
+
35
+ // ═══════════════════════════════════════════════════════════════════════════════
36
+ // Constants
37
+ // ═══════════════════════════════════════════════════════════════════════════════
38
+
39
+ /** All canonical capabilities in schema-declared order */
40
+ const ALL_CAPABILITIES: readonly CanonicalCapability[] =
41
+ CanonicalCapabilitySchema.options;
42
+
43
+ /**
44
+ * Mapping from CAPABILITY_MATRIX keys to canonical capability names.
45
+ * Structural capabilities that map 1:1 from the legacy matrix.
46
+ */
47
+ const CAPABILITY_MATRIX_TO_CANONICAL: Readonly<
48
+ Record<HarnessCapabilityName, CanonicalCapability>
49
+ > = {
50
+ hooks: "hooks",
51
+ mcp: "mcp-servers",
52
+ path_scoping: "path-scoping",
53
+ workflows: "workflows",
54
+ toggleable_rules: "toggleable-rules",
55
+ agents: "agent",
56
+ file_match_inclusion: "file-match-inclusion",
57
+ system_prompt_merging: "system-prompt-merging",
58
+ };
59
+
60
+ /**
61
+ * Asset-type capabilities — their compatibility comes from
62
+ * ASSET_HARNESS_COMPATIBILITY rather than CAPABILITY_MATRIX.
63
+ */
64
+ const ASSET_TYPE_CAPABILITIES: readonly CanonicalCapability[] = [
65
+ "skill",
66
+ "power",
67
+ "rule",
68
+ "workflow",
69
+ "agent",
70
+ "prompt",
71
+ "template",
72
+ "reference-pack",
73
+ ];
74
+
75
+ /**
76
+ * Capabilities that are always fully supported regardless of harness
77
+ * (structural fundamentals that every harness can represent).
78
+ */
79
+ const ALWAYS_FULL_CAPABILITIES: readonly CanonicalCapability[] = [
80
+ "frontmatter",
81
+ "body",
82
+ "body-overrides",
83
+ "extra-fields",
84
+ ];
85
+
86
+ // ═══════════════════════════════════════════════════════════════════════════════
87
+ // Profile Builder
88
+ // ═══════════════════════════════════════════════════════════════════════════════
89
+
90
+ /**
91
+ * Converts a CompatibilityLevel from the legacy system to a degradation strategy.
92
+ * For partial/none levels that don't have explicit degradation info in the
93
+ * ASSET_HARNESS_COMPATIBILITY table, we use "inline" as the default strategy
94
+ * (the harness still produces output, just with reduced fidelity).
95
+ */
96
+ function levelToEntry(
97
+ level: CompatibilityLevel,
98
+ defaultDegradation: "inline" | "comment" | "omit" = "inline",
99
+ ): RosettaCompatibilityEntry {
100
+ switch (level) {
101
+ case "full":
102
+ return { support: "full" };
103
+ case "partial":
104
+ return { support: "partial", degradation: defaultDegradation };
105
+ case "none":
106
+ return { support: "none", degradation: defaultDegradation };
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Constructs a complete `RosettaCompatibilityProfile` for a given harness/variant
112
+ * combination by consulting both existing compatibility sources:
113
+ * - `CAPABILITY_MATRIX` for structural capabilities (hooks, mcp-servers, etc.)
114
+ * - `ASSET_HARNESS_COMPATIBILITY` for asset-type capabilities (skill, power, etc.)
115
+ *
116
+ * @param harness - The harness name to build a profile for
117
+ * @param _variant - Optional variant (reserved for future variant-specific overrides)
118
+ * @returns A complete RosettaCompatibilityProfile covering all 19 canonical capabilities
119
+ */
120
+ export function buildCompatibilityProfile(
121
+ harness: HarnessName,
122
+ _variant?: string,
123
+ ): RosettaCompatibilityProfile {
124
+ const profile: Record<string, RosettaCompatibilityEntry> = {};
125
+
126
+ // Get the harness's capability matrix row
127
+ const capRow = CAPABILITY_MATRIX[harness];
128
+
129
+ for (const cap of ALL_CAPABILITIES) {
130
+ // 1. Always-full capabilities (structural fundamentals)
131
+ if (
132
+ (ALWAYS_FULL_CAPABILITIES as readonly string[]).includes(cap as string)
133
+ ) {
134
+ profile[cap] = { support: "full" };
135
+ continue;
136
+ }
137
+
138
+ // 2. Structural capabilities from CAPABILITY_MATRIX
139
+ const matrixKey = Object.entries(CAPABILITY_MATRIX_TO_CANONICAL).find(
140
+ ([, canonical]) => canonical === cap,
141
+ )?.[0] as HarnessCapabilityName | undefined;
142
+
143
+ if (matrixKey && capRow[matrixKey]) {
144
+ const entry = capRow[matrixKey];
145
+ if (entry.support === "full") {
146
+ profile[cap] = { support: "full" };
147
+ } else {
148
+ profile[cap] = {
149
+ support: entry.support,
150
+ degradation: entry.degradation ?? "inline",
151
+ };
152
+ }
153
+ continue;
154
+ }
155
+
156
+ // 3. Asset-type capabilities from ASSET_HARNESS_COMPATIBILITY
157
+ if (
158
+ (ASSET_TYPE_CAPABILITIES as readonly string[]).includes(cap as string)
159
+ ) {
160
+ // The `agent` capability is special: CAPABILITY_MATRIX also has an `agents`
161
+ // entry. We prioritize the ASSET_HARNESS_COMPATIBILITY data for asset-type
162
+ // capabilities since it answers the build-level question.
163
+ const assetType = cap as keyof typeof ASSET_HARNESS_COMPATIBILITY;
164
+ const assetRow = ASSET_HARNESS_COMPATIBILITY[assetType];
165
+ if (assetRow) {
166
+ const level = assetRow[harness];
167
+ if (level) {
168
+ // Use "inline" for partial, "omit" for none as reasonable defaults
169
+ // matching the existing contracts.ts behavior
170
+ const degradation = level === "none" ? "omit" : "inline";
171
+ profile[cap] = levelToEntry(level, degradation);
172
+ } else {
173
+ // Unlisted means "full" per getCompatibility() semantics
174
+ profile[cap] = { support: "full" };
175
+ }
176
+ } else {
177
+ profile[cap] = { support: "full" };
178
+ }
179
+ continue;
180
+ }
181
+
182
+ // 4. Fallback: full support
183
+ profile[cap] = { support: "full" };
184
+ }
185
+
186
+ return profile as unknown as RosettaCompatibilityProfile;
187
+ }
188
+
189
+ // ═══════════════════════════════════════════════════════════════════════════════
190
+ // Per-Harness Profile Constants
191
+ // ═══════════════════════════════════════════════════════════════════════════════
192
+
193
+ /** Kiro steering variant profile */
194
+ export const KIRO_STEERING_PROFILE: RosettaCompatibilityProfile =
195
+ buildCompatibilityProfile("kiro", "steering");
196
+
197
+ /** Kiro power variant profile */
198
+ export const KIRO_POWER_PROFILE: RosettaCompatibilityProfile =
199
+ buildCompatibilityProfile("kiro", "power");
200
+
201
+ /** Claude Code (claude-md variant) profile */
202
+ export const CLAUDE_CODE_PROFILE: RosettaCompatibilityProfile =
203
+ buildCompatibilityProfile("claude-code", "claude-md");
204
+
205
+ /** Codex (agents-md variant) profile */
206
+ export const CODEX_PROFILE: RosettaCompatibilityProfile =
207
+ buildCompatibilityProfile("codex", "agents-md");
208
+
209
+ /** Copilot (instructions variant) profile */
210
+ export const COPILOT_PROFILE: RosettaCompatibilityProfile =
211
+ buildCompatibilityProfile("copilot", "instructions");
212
+
213
+ /** Cursor (rule variant) profile */
214
+ export const CURSOR_PROFILE: RosettaCompatibilityProfile =
215
+ buildCompatibilityProfile("cursor", "rule");
216
+
217
+ /** Windsurf (rule variant) profile */
218
+ export const WINDSURF_PROFILE: RosettaCompatibilityProfile =
219
+ buildCompatibilityProfile("windsurf", "rule");
220
+
221
+ /** Cline (rule variant) profile */
222
+ export const CLINE_PROFILE: RosettaCompatibilityProfile =
223
+ buildCompatibilityProfile("cline", "rule");
224
+
225
+ /** Q Developer (rule variant) profile */
226
+ export const QDEVELOPER_PROFILE: RosettaCompatibilityProfile =
227
+ buildCompatibilityProfile("qdeveloper", "rule");
228
+
229
+ // ═══════════════════════════════════════════════════════════════════════════════
230
+ // Lookup
231
+ // ═══════════════════════════════════════════════════════════════════════════════
232
+
233
+ /**
234
+ * Internal mapping from format-id/variant pairs to profile constants.
235
+ * Keys follow the pattern `formatId` or `formatId:variant`.
236
+ */
237
+ const PROFILE_LOOKUP: Readonly<Record<string, RosettaCompatibilityProfile>> = {
238
+ // Kiro variants
239
+ kiro: KIRO_STEERING_PROFILE,
240
+ "kiro:steering": KIRO_STEERING_PROFILE,
241
+ "kiro:power": KIRO_POWER_PROFILE,
242
+ // Claude Code
243
+ "claude-code": CLAUDE_CODE_PROFILE,
244
+ "claude-code:claude-md": CLAUDE_CODE_PROFILE,
245
+ // Codex
246
+ codex: CODEX_PROFILE,
247
+ "codex:agents-md": CODEX_PROFILE,
248
+ // Copilot
249
+ copilot: COPILOT_PROFILE,
250
+ "copilot:instructions": COPILOT_PROFILE,
251
+ // Cursor
252
+ cursor: CURSOR_PROFILE,
253
+ "cursor:rule": CURSOR_PROFILE,
254
+ // Windsurf
255
+ windsurf: WINDSURF_PROFILE,
256
+ "windsurf:rule": WINDSURF_PROFILE,
257
+ // Cline
258
+ cline: CLINE_PROFILE,
259
+ "cline:rule": CLINE_PROFILE,
260
+ // Q Developer
261
+ qdeveloper: QDEVELOPER_PROFILE,
262
+ "qdeveloper:rule": QDEVELOPER_PROFILE,
263
+ };
264
+
265
+ /**
266
+ * Looks up the appropriate built-in compatibility profile for a given
267
+ * format/variant pair.
268
+ *
269
+ * @param formatId - The format identifier (e.g., "kiro", "claude-code")
270
+ * @param variant - Optional variant name (e.g., "steering", "power", "rule")
271
+ * @returns The matching profile, or undefined if no built-in profile exists
272
+ */
273
+ export function getBuiltinProfile(
274
+ formatId: string,
275
+ variant?: string,
276
+ ): RosettaCompatibilityProfile | undefined {
277
+ // Try specific variant first
278
+ if (variant) {
279
+ const key = `${formatId}:${variant}`;
280
+ if (key in PROFILE_LOOKUP) {
281
+ return PROFILE_LOOKUP[key];
282
+ }
283
+ }
284
+ // Fall back to format-level default
285
+ if (formatId in PROFILE_LOOKUP) {
286
+ return PROFILE_LOOKUP[formatId];
287
+ }
288
+ return undefined;
289
+ }
290
+
291
+ /**
292
+ * Returns all built-in profile keys in deterministic code-point order.
293
+ * Useful for inventory/regression testing.
294
+ */
295
+ export function getAllBuiltinProfileKeys(): readonly string[] {
296
+ return Object.keys(PROFILE_LOOKUP).sort(codePointCompare);
297
+ }