@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.
- package/CHANGELOG.md +410 -0
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/bridge/mcp-server.cjs +14171 -0
- package/package.json +98 -0
- package/src/adapters/capabilities.ts +178 -0
- package/src/adapters/claude-code.ts +110 -0
- package/src/adapters/cline.ts +98 -0
- package/src/adapters/codex.ts +173 -0
- package/src/adapters/copilot.ts +106 -0
- package/src/adapters/cursor.ts +97 -0
- package/src/adapters/degradation.ts +95 -0
- package/src/adapters/index.ts +324 -0
- package/src/adapters/kiro-frontmatter.ts +139 -0
- package/src/adapters/kiro-inclusion.ts +86 -0
- package/src/adapters/kiro.ts +412 -0
- package/src/adapters/qdeveloper.ts +115 -0
- package/src/adapters/types.ts +81 -0
- package/src/adapters/windsurf.ts +96 -0
- package/src/admin.ts +283 -0
- package/src/asset-conventions.ts +118 -0
- package/src/attribution-backfill.ts +319 -0
- package/src/attribution-report.ts +95 -0
- package/src/attribution.ts +239 -0
- package/src/backends/github.ts +194 -0
- package/src/backends/http.ts +122 -0
- package/src/backends/index.ts +39 -0
- package/src/backends/local.ts +47 -0
- package/src/backends/s3.ts +157 -0
- package/src/backends/types.ts +59 -0
- package/src/base-cache.ts +270 -0
- package/src/browse-ui.ts +3754 -0
- package/src/browse.ts +1038 -0
- package/src/build.ts +1108 -0
- package/src/catalog.ts +204 -0
- package/src/cli-deprecated.ts +29 -0
- package/src/cli.ts +773 -0
- package/src/collection-admin.ts +287 -0
- package/src/collection-builder.ts +464 -0
- package/src/collections.ts +116 -0
- package/src/compatibility.ts +105 -0
- package/src/config.ts +743 -0
- package/src/eval/rubrics/kiro-progressive-steering.ts +841 -0
- package/src/eval.ts +1169 -0
- package/src/file-writer.ts +61 -0
- package/src/format-registry.ts +141 -0
- package/src/guild/auto-updater.ts +163 -0
- package/src/guild/backend-resolver.ts +49 -0
- package/src/guild/cli.ts +592 -0
- package/src/guild/collection-expander.ts +47 -0
- package/src/guild/global-cache.ts +247 -0
- package/src/guild/hook-generator.ts +100 -0
- package/src/guild/manifest.ts +154 -0
- package/src/guild/path-utils.ts +12 -0
- package/src/guild/sync.ts +622 -0
- package/src/guild/version-resolver.ts +42 -0
- package/src/help/metadata.ts +445 -0
- package/src/help/renderer.ts +265 -0
- package/src/help/typo-suggester.ts +25 -0
- package/src/hooks/expression.ts +493 -0
- package/src/hooks/pipeline.ts +141 -0
- package/src/import.ts +773 -0
- package/src/importers/claude-code.ts +134 -0
- package/src/importers/cline.ts +103 -0
- package/src/importers/codex.ts +140 -0
- package/src/importers/copilot.ts +103 -0
- package/src/importers/cursor.ts +105 -0
- package/src/importers/index.ts +390 -0
- package/src/importers/kiro.ts +110 -0
- package/src/importers/qdeveloper.ts +103 -0
- package/src/importers/types.ts +54 -0
- package/src/importers/windsurf.ts +104 -0
- package/src/install.ts +1005 -0
- package/src/manifest-admin.ts +306 -0
- package/src/mcp-bridge.ts +240 -0
- package/src/mutation/delta.ts +50 -0
- package/src/mutation/history.ts +66 -0
- package/src/mutation/operators.ts +524 -0
- package/src/mutation/runner.ts +332 -0
- package/src/new.ts +106 -0
- package/src/outcomes/collision.ts +127 -0
- package/src/outcomes/normalize.ts +208 -0
- package/src/outcomes/registry.ts +173 -0
- package/src/parser.ts +446 -0
- package/src/provenance-backfill-cli.ts +319 -0
- package/src/provenance-backfill.ts +520 -0
- package/src/publish.ts +354 -0
- package/src/reconcile-orchestrator.ts +502 -0
- package/src/reconcile-report-renderer.ts +176 -0
- package/src/resolve-body.ts +15 -0
- package/src/rosetta/builtins/compatibility-profiles.ts +297 -0
- package/src/rosetta/builtins/contracts.ts +1033 -0
- package/src/rosetta/builtins/pretty-printers/claude-code-native.ts +122 -0
- package/src/rosetta/builtins/pretty-printers/cline-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/codex-native.ts +127 -0
- package/src/rosetta/builtins/pretty-printers/copilot-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/cursor-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/index.ts +81 -0
- package/src/rosetta/builtins/pretty-printers/kiro-native.ts +166 -0
- package/src/rosetta/builtins/pretty-printers/kiro-power.ts +108 -0
- package/src/rosetta/builtins/pretty-printers/kiro-skill.ts +88 -0
- package/src/rosetta/builtins/pretty-printers/qdeveloper-native.ts +51 -0
- package/src/rosetta/builtins/pretty-printers/superpowers.ts +97 -0
- package/src/rosetta/builtins/pretty-printers/windsurf-native.ts +50 -0
- package/src/rosetta/builtins/sources/claude-code-native.ts +348 -0
- package/src/rosetta/builtins/sources/cline-native.ts +176 -0
- package/src/rosetta/builtins/sources/codex-native.ts +343 -0
- package/src/rosetta/builtins/sources/copilot-native.ts +178 -0
- package/src/rosetta/builtins/sources/cursor-native.ts +176 -0
- package/src/rosetta/builtins/sources/index.ts +95 -0
- package/src/rosetta/builtins/sources/kiro-native.ts +462 -0
- package/src/rosetta/builtins/sources/kiro-power.ts +285 -0
- package/src/rosetta/builtins/sources/kiro-skill.ts +230 -0
- package/src/rosetta/builtins/sources/qdeveloper-native.ts +181 -0
- package/src/rosetta/builtins/sources/superpowers.ts +240 -0
- package/src/rosetta/builtins/sources/windsurf-native.ts +176 -0
- package/src/rosetta/builtins/targets/claude-code.ts +181 -0
- package/src/rosetta/builtins/targets/cline.ts +87 -0
- package/src/rosetta/builtins/targets/codex.ts +226 -0
- package/src/rosetta/builtins/targets/copilot.ts +103 -0
- package/src/rosetta/builtins/targets/cursor.ts +87 -0
- package/src/rosetta/builtins/targets/index.ts +60 -0
- package/src/rosetta/builtins/targets/kiro.ts +278 -0
- package/src/rosetta/builtins/targets/qdeveloper.ts +103 -0
- package/src/rosetta/builtins/targets/windsurf.ts +87 -0
- package/src/rosetta/canonical.ts +729 -0
- package/src/rosetta/compatibility.ts +432 -0
- package/src/rosetta/contracts.ts +329 -0
- package/src/rosetta/detector.ts +724 -0
- package/src/rosetta/diagnostics.ts +630 -0
- package/src/rosetta/engine-bootstrap.ts +103 -0
- package/src/rosetta/engine.ts +744 -0
- package/src/rosetta/index.ts +381 -0
- package/src/rosetta/inspection.ts +530 -0
- package/src/rosetta/plan.ts +448 -0
- package/src/rosetta/provenance-digest.ts +369 -0
- package/src/rosetta/reconcile.ts +812 -0
- package/src/rosetta/redaction.ts +467 -0
- package/src/rosetta/registry.ts +712 -0
- package/src/rosetta/renderers.ts +571 -0
- package/src/rosetta/request-guard.ts +335 -0
- package/src/rosetta/resolution.ts +419 -0
- package/src/rosetta/source-accounting.ts +233 -0
- package/src/rosetta/templates.ts +129 -0
- package/src/rosetta-cli.ts +717 -0
- package/src/rosetta-docs-generator.ts +793 -0
- package/src/rosetta-profiles-cli.ts +367 -0
- package/src/schemas.ts +1712 -0
- package/src/spec-coordination.ts +1141 -0
- package/src/temper.ts +747 -0
- package/src/template-bundle-loader.ts +312 -0
- package/src/template-engine.ts +53 -0
- package/src/translation-application-policy.ts +496 -0
- package/src/translation-orchestrator.ts +1013 -0
- package/src/translation-plan-applier.ts +473 -0
- package/src/tutorial.ts +305 -0
- package/src/validate.ts +1093 -0
- package/src/versioning.ts +553 -0
- package/src/wizard.ts +660 -0
- package/src/workspace.ts +237 -0
- package/templates/eval-contexts/claude-code.md.njk +6 -0
- package/templates/eval-contexts/cline.md.njk +6 -0
- package/templates/eval-contexts/copilot.md.njk +6 -0
- package/templates/eval-contexts/cursor.md.njk +6 -0
- package/templates/eval-contexts/kiro.md.njk +10 -0
- package/templates/eval-contexts/qdeveloper.md.njk +6 -0
- package/templates/eval-contexts/windsurf.md.njk +6 -0
- package/templates/harness-adapters/_base/attribution-footer.md.njk +17 -0
- package/templates/harness-adapters/_base/base.md.njk +16 -0
- package/templates/harness-adapters/claude-code/claude.md.njk +1 -0
- package/templates/harness-adapters/claude-code/mcp.json.njk +1 -0
- package/templates/harness-adapters/claude-code/settings.json.njk +1 -0
- package/templates/harness-adapters/claude-code/skill-library-index.md.njk +13 -0
- package/templates/harness-adapters/claude-code/skill.md.njk +19 -0
- package/templates/harness-adapters/cline/hook.sh.njk +4 -0
- package/templates/harness-adapters/cline/mcp.json.njk +1 -0
- package/templates/harness-adapters/cline/rule.md.njk +1 -0
- package/templates/harness-adapters/codex/agents-md.md.njk +6 -0
- package/templates/harness-adapters/codex/agents-pointer.md.njk +16 -0
- package/templates/harness-adapters/codex/skill.md.njk +27 -0
- package/templates/harness-adapters/copilot/agents.md.njk +1 -0
- package/templates/harness-adapters/copilot/instructions.md.njk +1 -0
- package/templates/harness-adapters/copilot/scoped.md.njk +6 -0
- package/templates/harness-adapters/cursor/mcp.json.njk +1 -0
- package/templates/harness-adapters/cursor/rule.md.njk +6 -0
- package/templates/harness-adapters/kiro/hook.json.njk +1 -0
- package/templates/harness-adapters/kiro/mcp.json.njk +1 -0
- package/templates/harness-adapters/kiro/power-steering.md.njk +3 -0
- package/templates/harness-adapters/kiro/power.md.njk +12 -0
- package/templates/harness-adapters/kiro/steering.md.njk +16 -0
- package/templates/harness-adapters/qdeveloper/agent.md.njk +1 -0
- package/templates/harness-adapters/qdeveloper/mcp.json.njk +1 -0
- package/templates/harness-adapters/qdeveloper/rule.md.njk +1 -0
- package/templates/harness-adapters/windsurf/mcp.json.njk +1 -0
- package/templates/harness-adapters/windsurf/rule.md.njk +1 -0
- package/templates/harness-adapters/windsurf/workflow.md.njk +1 -0
- package/templates/knowledge/hooks.yaml.njk +4 -0
- package/templates/knowledge/knowledge.md.njk +53 -0
- package/templates/knowledge/mcp-servers.yaml.njk +2 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rosetta Stone — Built-in Source Translator Registry
|
|
3
|
+
*
|
|
4
|
+
* Exports all source translators (path-based and harness-native) and lookup
|
|
5
|
+
* maps from format identifiers to their translator implementations.
|
|
6
|
+
*
|
|
7
|
+
* CONSTRAINTS:
|
|
8
|
+
* - NO filesystem, process, clock, random, Git, or network imports
|
|
9
|
+
* - Pure re-exports only
|
|
10
|
+
*
|
|
11
|
+
* Requirements: 2.9, 4.1, 4.2, 4.3, 4.5, 4.6
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { FormatIdentifier } from "../../../schemas";
|
|
15
|
+
import type { SourceTranslator } from "../../registry";
|
|
16
|
+
import { translateClaudeCodeNative } from "./claude-code-native";
|
|
17
|
+
import { translateClineNative } from "./cline-native";
|
|
18
|
+
import { translateCodexNative } from "./codex-native";
|
|
19
|
+
import { translateCopilotNative } from "./copilot-native";
|
|
20
|
+
import { translateCursorNative } from "./cursor-native";
|
|
21
|
+
import { translateKiroNative } from "./kiro-native";
|
|
22
|
+
import { translateKiroPower } from "./kiro-power";
|
|
23
|
+
import { translateKiroSkill } from "./kiro-skill";
|
|
24
|
+
import { translateQDeveloperNative } from "./qdeveloper-native";
|
|
25
|
+
import { translateSuperpowers } from "./superpowers";
|
|
26
|
+
import { translateWindsurfNative } from "./windsurf-native";
|
|
27
|
+
|
|
28
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
29
|
+
// Re-exports — Path-based source translators (task 6.2)
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
31
|
+
|
|
32
|
+
export { translateKiroPower } from "./kiro-power";
|
|
33
|
+
export { translateKiroSkill } from "./kiro-skill";
|
|
34
|
+
export { translateSuperpowers } from "./superpowers";
|
|
35
|
+
|
|
36
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
37
|
+
// Re-exports — Harness-native source translators (task 6.3)
|
|
38
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
39
|
+
|
|
40
|
+
export { translateClaudeCodeNative } from "./claude-code-native";
|
|
41
|
+
export { translateCodexNative } from "./codex-native";
|
|
42
|
+
export { translateKiroNative } from "./kiro-native";
|
|
43
|
+
|
|
44
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
45
|
+
// Re-exports — Harness-native source translators (task 6.4)
|
|
46
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
47
|
+
|
|
48
|
+
export { translateClineNative } from "./cline-native";
|
|
49
|
+
export { translateCopilotNative } from "./copilot-native";
|
|
50
|
+
export { translateCursorNative } from "./cursor-native";
|
|
51
|
+
export { translateQDeveloperNative } from "./qdeveloper-native";
|
|
52
|
+
export { translateWindsurfNative } from "./windsurf-native";
|
|
53
|
+
|
|
54
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
55
|
+
// Path-Based Source Translator Map
|
|
56
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Maps format identifiers to their path-based source translators.
|
|
60
|
+
*
|
|
61
|
+
* These handle known directory structures (e.g., kiro-power format expects
|
|
62
|
+
* POWER.md + steering/ files).
|
|
63
|
+
*/
|
|
64
|
+
export const PATH_BASED_SOURCE_TRANSLATORS: ReadonlyMap<
|
|
65
|
+
FormatIdentifier,
|
|
66
|
+
SourceTranslator
|
|
67
|
+
> = new Map<FormatIdentifier, SourceTranslator>([
|
|
68
|
+
["kiro-power" as FormatIdentifier, translateKiroPower],
|
|
69
|
+
["kiro-skill" as FormatIdentifier, translateKiroSkill],
|
|
70
|
+
["superpowers" as FormatIdentifier, translateSuperpowers],
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
74
|
+
// Harness-Native Source Translator Map
|
|
75
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Maps format identifiers to their harness-native source translators.
|
|
79
|
+
*
|
|
80
|
+
* These handle individual harness-native files and convert them to canonical
|
|
81
|
+
* artifacts (e.g., CLAUDE.md, AGENTS.md, .kiro/steering/*.md).
|
|
82
|
+
*/
|
|
83
|
+
export const HARNESS_NATIVE_SOURCE_TRANSLATORS: ReadonlyMap<
|
|
84
|
+
FormatIdentifier,
|
|
85
|
+
SourceTranslator
|
|
86
|
+
> = new Map<FormatIdentifier, SourceTranslator>([
|
|
87
|
+
["kiro" as FormatIdentifier, translateKiroNative],
|
|
88
|
+
["claude-code" as FormatIdentifier, translateClaudeCodeNative],
|
|
89
|
+
["codex" as FormatIdentifier, translateCodexNative],
|
|
90
|
+
["copilot" as FormatIdentifier, translateCopilotNative],
|
|
91
|
+
["cursor" as FormatIdentifier, translateCursorNative],
|
|
92
|
+
["windsurf" as FormatIdentifier, translateWindsurfNative],
|
|
93
|
+
["cline" as FormatIdentifier, translateClineNative],
|
|
94
|
+
["qdeveloper" as FormatIdentifier, translateQDeveloperNative],
|
|
95
|
+
]);
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rosetta Stone — Kiro Harness-Native Source Translator
|
|
3
|
+
*
|
|
4
|
+
* Translates Kiro's native format (.kiro/steering/*.md, hooks, MCP servers)
|
|
5
|
+
* into a canonical KnowledgeArtifact candidate. Handles both "steering" and
|
|
6
|
+
* "power" variants.
|
|
7
|
+
*
|
|
8
|
+
* CONSTRAINTS:
|
|
9
|
+
* - NO filesystem, process, clock, random, Git, or network imports
|
|
10
|
+
* - Pure function only
|
|
11
|
+
*
|
|
12
|
+
* Requirements: 2.9, 4.1, 4.2, 4.3, 4.5, 4.6
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import matter from "gray-matter";
|
|
16
|
+
import type {
|
|
17
|
+
CanonicalEvent,
|
|
18
|
+
CanonicalHook,
|
|
19
|
+
McpServerDefinition,
|
|
20
|
+
SourceDocument,
|
|
21
|
+
TranslationDiagnostic,
|
|
22
|
+
} from "../../../schemas";
|
|
23
|
+
import { createDiagnostic } from "../../diagnostics";
|
|
24
|
+
import type {
|
|
25
|
+
SourceTranslationOutput,
|
|
26
|
+
SourceTranslatorContext,
|
|
27
|
+
} from "../../registry";
|
|
28
|
+
import {
|
|
29
|
+
namespacedExtraField,
|
|
30
|
+
normalizeDocumentOrder,
|
|
31
|
+
SourceAccountant,
|
|
32
|
+
} from "../../source-accounting";
|
|
33
|
+
|
|
34
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
35
|
+
// Internal helpers
|
|
36
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
37
|
+
|
|
38
|
+
const KIRO_EVENT_MAP: Record<string, CanonicalEvent> = {
|
|
39
|
+
fileEdited: "file_edited",
|
|
40
|
+
fileCreated: "file_created",
|
|
41
|
+
fileDeleted: "file_deleted",
|
|
42
|
+
agentStop: "agent_stop",
|
|
43
|
+
promptSubmit: "prompt_submit",
|
|
44
|
+
preToolUse: "pre_tool_use",
|
|
45
|
+
postToolUse: "post_tool_use",
|
|
46
|
+
preTaskExecution: "pre_task",
|
|
47
|
+
postTaskExecution: "post_task",
|
|
48
|
+
userTriggered: "user_triggered",
|
|
49
|
+
file_edited: "file_edited",
|
|
50
|
+
file_created: "file_created",
|
|
51
|
+
file_deleted: "file_deleted",
|
|
52
|
+
agent_stop: "agent_stop",
|
|
53
|
+
prompt_submit: "prompt_submit",
|
|
54
|
+
pre_tool_use: "pre_tool_use",
|
|
55
|
+
post_tool_use: "post_tool_use",
|
|
56
|
+
pre_task: "pre_task",
|
|
57
|
+
post_task: "post_task",
|
|
58
|
+
user_triggered: "user_triggered",
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
function mapKiroEvent(rawEvent: string): CanonicalEvent {
|
|
62
|
+
return (KIRO_EVENT_MAP[rawEvent] ?? rawEvent) as CanonicalEvent;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Derives a kebab-case artifact name from a document path.
|
|
67
|
+
*/
|
|
68
|
+
function deriveArtifactName(path: string): string {
|
|
69
|
+
const segments = path.split("/");
|
|
70
|
+
const base = segments[segments.length - 1] ?? "";
|
|
71
|
+
const name =
|
|
72
|
+
base.replace(/\.kiro\.hook$/, "") || base.replace(/\.[^.]+$/, "");
|
|
73
|
+
return name
|
|
74
|
+
.toLowerCase()
|
|
75
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
76
|
+
.replace(/^-|-$/g, "");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Checks if a document is a primary steering/power markdown file.
|
|
81
|
+
*/
|
|
82
|
+
function isPrimaryMd(path: string): boolean {
|
|
83
|
+
return (
|
|
84
|
+
path.endsWith(".md") &&
|
|
85
|
+
!path.endsWith(".kiro.hook") &&
|
|
86
|
+
!path.includes("hooks/") &&
|
|
87
|
+
!path.includes("mcp-servers")
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Checks if a document is a hook file.
|
|
93
|
+
*/
|
|
94
|
+
function isHookFile(path: string): boolean {
|
|
95
|
+
return path.endsWith(".kiro.hook") || path.includes("hooks/");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Checks if a document is an MCP server definition file.
|
|
100
|
+
*/
|
|
101
|
+
function isMcpFile(path: string): boolean {
|
|
102
|
+
return path.includes("mcp-servers") || path.endsWith("mcp.json");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Parse a hook from a JSON document.
|
|
107
|
+
*/
|
|
108
|
+
function parseHookDocument(
|
|
109
|
+
doc: SourceDocument,
|
|
110
|
+
diagnostics: TranslationDiagnostic[],
|
|
111
|
+
): CanonicalHook[] {
|
|
112
|
+
const content =
|
|
113
|
+
typeof doc.content === "string"
|
|
114
|
+
? doc.content
|
|
115
|
+
: new TextDecoder().decode(doc.content);
|
|
116
|
+
|
|
117
|
+
let data: Record<string, unknown>;
|
|
118
|
+
try {
|
|
119
|
+
data = JSON.parse(content);
|
|
120
|
+
} catch {
|
|
121
|
+
diagnostics.push(
|
|
122
|
+
createDiagnostic("RS_CANONICAL_INVALID_YAML", {
|
|
123
|
+
message: `Hook file "${doc.path}" contains invalid JSON.`,
|
|
124
|
+
source: { path: doc.path },
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!data || typeof data !== "object") return [];
|
|
131
|
+
|
|
132
|
+
const hooks: CanonicalHook[] = [];
|
|
133
|
+
const hook: Partial<CanonicalHook> = {};
|
|
134
|
+
|
|
135
|
+
// Map event
|
|
136
|
+
const rawEvent =
|
|
137
|
+
(data.event as string) ??
|
|
138
|
+
((data.when as Record<string, unknown>)?.event as string);
|
|
139
|
+
if (rawEvent) {
|
|
140
|
+
hook.event = mapKiroEvent(rawEvent);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Map action
|
|
144
|
+
const rawAction =
|
|
145
|
+
(data.action as Record<string, unknown>) ??
|
|
146
|
+
(data.then as Record<string, unknown>);
|
|
147
|
+
if (rawAction) {
|
|
148
|
+
if (
|
|
149
|
+
rawAction.type === "run_command" ||
|
|
150
|
+
(rawAction.command && !rawAction.prompt)
|
|
151
|
+
) {
|
|
152
|
+
hook.action = {
|
|
153
|
+
type: "run_command",
|
|
154
|
+
command:
|
|
155
|
+
(rawAction.command as string) ?? (rawAction.prompt as string) ?? "",
|
|
156
|
+
};
|
|
157
|
+
} else if (rawAction.type === "ask_agent" || rawAction.prompt) {
|
|
158
|
+
hook.action = {
|
|
159
|
+
type: "ask_agent",
|
|
160
|
+
prompt:
|
|
161
|
+
(rawAction.prompt as string) ?? (rawAction.command as string) ?? "",
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Map name and description
|
|
167
|
+
hook.name =
|
|
168
|
+
(data.name as string) ??
|
|
169
|
+
(data.id as string) ??
|
|
170
|
+
deriveArtifactName(doc.path);
|
|
171
|
+
if (data.description) hook.description = data.description as string;
|
|
172
|
+
|
|
173
|
+
// Map condition
|
|
174
|
+
const whenObj = data.when as Record<string, unknown> | undefined;
|
|
175
|
+
const conditionObj = data.condition as Record<string, unknown> | undefined;
|
|
176
|
+
const filePatterns =
|
|
177
|
+
(conditionObj?.file_patterns as string[]) ??
|
|
178
|
+
(whenObj?.filePatterns as string[]);
|
|
179
|
+
const toolTypes =
|
|
180
|
+
(conditionObj?.tool_types as string[]) ?? (whenObj?.toolTypes as string[]);
|
|
181
|
+
|
|
182
|
+
if (filePatterns || toolTypes) {
|
|
183
|
+
hook.condition = {};
|
|
184
|
+
if (filePatterns) {
|
|
185
|
+
hook.condition.file_patterns = Array.isArray(filePatterns)
|
|
186
|
+
? filePatterns
|
|
187
|
+
: [filePatterns as unknown as string];
|
|
188
|
+
}
|
|
189
|
+
if (toolTypes) {
|
|
190
|
+
hook.condition.tool_types = Array.isArray(toolTypes)
|
|
191
|
+
? toolTypes
|
|
192
|
+
: [toolTypes as unknown as string];
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (hook.event && hook.action && hook.name) {
|
|
197
|
+
hooks.push(hook as CanonicalHook);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return hooks;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Parse MCP server definitions from a JSON document.
|
|
205
|
+
*/
|
|
206
|
+
function parseMcpDocument(
|
|
207
|
+
doc: SourceDocument,
|
|
208
|
+
diagnostics: TranslationDiagnostic[],
|
|
209
|
+
): McpServerDefinition[] {
|
|
210
|
+
const content =
|
|
211
|
+
typeof doc.content === "string"
|
|
212
|
+
? doc.content
|
|
213
|
+
: new TextDecoder().decode(doc.content);
|
|
214
|
+
|
|
215
|
+
let data: unknown;
|
|
216
|
+
try {
|
|
217
|
+
data = JSON.parse(content);
|
|
218
|
+
} catch {
|
|
219
|
+
diagnostics.push(
|
|
220
|
+
createDiagnostic("RS_CANONICAL_INVALID_YAML", {
|
|
221
|
+
message: `MCP file "${doc.path}" contains invalid JSON.`,
|
|
222
|
+
source: { path: doc.path },
|
|
223
|
+
}),
|
|
224
|
+
);
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const servers: McpServerDefinition[] = [];
|
|
229
|
+
|
|
230
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
231
|
+
const obj = data as Record<string, unknown>;
|
|
232
|
+
// Handle { mcpServers: { name: config } } shape
|
|
233
|
+
const mcpServers = (obj.mcpServers ?? obj) as Record<string, unknown>;
|
|
234
|
+
for (const [name, config] of Object.entries(mcpServers)) {
|
|
235
|
+
if (!config || typeof config !== "object") continue;
|
|
236
|
+
const cfg = config as Record<string, unknown>;
|
|
237
|
+
if (cfg.url) {
|
|
238
|
+
const transport =
|
|
239
|
+
cfg.type === "http" ? ("http" as const) : ("sse" as const);
|
|
240
|
+
servers.push({
|
|
241
|
+
name,
|
|
242
|
+
transport,
|
|
243
|
+
url: cfg.url as string,
|
|
244
|
+
env: (cfg.env as Record<string, string>) ?? {},
|
|
245
|
+
...(cfg.timeout ? { timeout: cfg.timeout as number } : {}),
|
|
246
|
+
});
|
|
247
|
+
} else if (cfg.command) {
|
|
248
|
+
servers.push({
|
|
249
|
+
name,
|
|
250
|
+
transport: "stdio" as const,
|
|
251
|
+
command: cfg.command as string,
|
|
252
|
+
args: (cfg.args as string[]) ?? [],
|
|
253
|
+
env: (cfg.env as Record<string, string>) ?? {},
|
|
254
|
+
...(cfg.timeout ? { timeout: cfg.timeout as number } : {}),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
} else if (Array.isArray(data)) {
|
|
259
|
+
for (const entry of data) {
|
|
260
|
+
if (!entry || typeof entry !== "object") continue;
|
|
261
|
+
const cfg = entry as Record<string, unknown>;
|
|
262
|
+
if (!cfg.name) continue;
|
|
263
|
+
if (cfg.url) {
|
|
264
|
+
servers.push({
|
|
265
|
+
name: cfg.name as string,
|
|
266
|
+
transport:
|
|
267
|
+
cfg.transport === "http" ? ("http" as const) : ("sse" as const),
|
|
268
|
+
url: cfg.url as string,
|
|
269
|
+
env: (cfg.env as Record<string, string>) ?? {},
|
|
270
|
+
});
|
|
271
|
+
} else if (cfg.command) {
|
|
272
|
+
servers.push({
|
|
273
|
+
name: cfg.name as string,
|
|
274
|
+
transport: "stdio" as const,
|
|
275
|
+
command: cfg.command as string,
|
|
276
|
+
args: (cfg.args as string[]) ?? [],
|
|
277
|
+
env: (cfg.env as Record<string, string>) ?? {},
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return servers;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
287
|
+
// Exported Translator
|
|
288
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Kiro harness-native source translator.
|
|
292
|
+
*
|
|
293
|
+
* Consumes:
|
|
294
|
+
* - Primary steering/power markdown file (frontmatter + body)
|
|
295
|
+
* - Hook files (.kiro.hook JSON → CanonicalHook[])
|
|
296
|
+
* - MCP server definitions (JSON → McpServerDefinition[])
|
|
297
|
+
*
|
|
298
|
+
* Handles both "steering" and "power" variants:
|
|
299
|
+
* - Power: looks for POWER.md/SKILL.md frontmatter structure
|
|
300
|
+
* - Steering: standard steering markdown format
|
|
301
|
+
*/
|
|
302
|
+
export function translateKiroNative(
|
|
303
|
+
documents: readonly SourceDocument[],
|
|
304
|
+
context: SourceTranslatorContext,
|
|
305
|
+
): SourceTranslationOutput {
|
|
306
|
+
const accountant = new SourceAccountant();
|
|
307
|
+
const diagnostics: TranslationDiagnostic[] = [];
|
|
308
|
+
const sorted = normalizeDocumentOrder(documents);
|
|
309
|
+
|
|
310
|
+
// Determine artifact name from caller context
|
|
311
|
+
const artifactNameHint = context.callerContext.artifactNameHint as
|
|
312
|
+
| string
|
|
313
|
+
| undefined;
|
|
314
|
+
|
|
315
|
+
// Classify documents
|
|
316
|
+
const primaryDocs = sorted.filter((d) => isPrimaryMd(d.path));
|
|
317
|
+
const hookDocs = sorted.filter((d) => isHookFile(d.path));
|
|
318
|
+
const mcpDocs = sorted.filter((d) => isMcpFile(d.path));
|
|
319
|
+
|
|
320
|
+
// Handle missing primary file
|
|
321
|
+
if (primaryDocs.length === 0) {
|
|
322
|
+
diagnostics.push(
|
|
323
|
+
createDiagnostic("RS_CANONICAL_MISSING_KNOWLEDGE_MD", {
|
|
324
|
+
formatId: context.format.id,
|
|
325
|
+
message:
|
|
326
|
+
"No primary Kiro steering or power markdown file found in the document set.",
|
|
327
|
+
}),
|
|
328
|
+
);
|
|
329
|
+
return {
|
|
330
|
+
diagnostics,
|
|
331
|
+
consumedPaths: accountant.getConsumedPaths(),
|
|
332
|
+
preservedPaths: accountant.getPreservedPaths(),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Parse primary markdown (use first matching file)
|
|
337
|
+
const primaryDoc = primaryDocs[0];
|
|
338
|
+
const content =
|
|
339
|
+
typeof primaryDoc.content === "string"
|
|
340
|
+
? primaryDoc.content
|
|
341
|
+
: new TextDecoder().decode(primaryDoc.content);
|
|
342
|
+
|
|
343
|
+
let frontmatterData: Record<string, unknown> = {};
|
|
344
|
+
let body = "";
|
|
345
|
+
|
|
346
|
+
try {
|
|
347
|
+
const parsed = matter(content);
|
|
348
|
+
frontmatterData = { ...parsed.data };
|
|
349
|
+
body = parsed.content.trim();
|
|
350
|
+
} catch {
|
|
351
|
+
diagnostics.push(
|
|
352
|
+
createDiagnostic("RS_CANONICAL_INVALID_FRONTMATTER", {
|
|
353
|
+
formatId: context.format.id,
|
|
354
|
+
message: `Failed to parse frontmatter in "${primaryDoc.path}".`,
|
|
355
|
+
source: { path: primaryDoc.path },
|
|
356
|
+
}),
|
|
357
|
+
);
|
|
358
|
+
// Continue with empty data — body still available as raw content
|
|
359
|
+
body = content.trim();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
accountant.consume(primaryDoc.path);
|
|
363
|
+
accountant.mapField(primaryDoc.path, "frontmatter", "frontmatter");
|
|
364
|
+
accountant.mapField(primaryDoc.path, "content", "body");
|
|
365
|
+
|
|
366
|
+
// Mark additional primary docs as preserved (multi-file sets)
|
|
367
|
+
for (let i = 1; i < primaryDocs.length; i++) {
|
|
368
|
+
accountant.preserve(primaryDocs[i].path);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Parse hooks
|
|
372
|
+
const allHooks: CanonicalHook[] = [];
|
|
373
|
+
for (const hookDoc of hookDocs) {
|
|
374
|
+
const hooks = parseHookDocument(hookDoc, diagnostics);
|
|
375
|
+
allHooks.push(...hooks);
|
|
376
|
+
accountant.consume(hookDoc.path);
|
|
377
|
+
accountant.mapField(hookDoc.path, "hooks", "hooks");
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Parse MCP servers
|
|
381
|
+
const allMcpServers: McpServerDefinition[] = [];
|
|
382
|
+
for (const mcpDoc of mcpDocs) {
|
|
383
|
+
const servers = parseMcpDocument(mcpDoc, diagnostics);
|
|
384
|
+
allMcpServers.push(...servers);
|
|
385
|
+
accountant.consume(mcpDoc.path);
|
|
386
|
+
accountant.mapField(mcpDoc.path, "mcpServers", "mcpServers");
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Derive artifact name
|
|
390
|
+
const name =
|
|
391
|
+
artifactNameHint ??
|
|
392
|
+
(frontmatterData.name as string) ??
|
|
393
|
+
deriveArtifactName(primaryDoc.path);
|
|
394
|
+
|
|
395
|
+
// Build extra fields from source-specific frontmatter keys
|
|
396
|
+
const extraFields: Record<string, unknown> = {};
|
|
397
|
+
const knownFrontmatterKeys = new Set([
|
|
398
|
+
"name",
|
|
399
|
+
"displayName",
|
|
400
|
+
"description",
|
|
401
|
+
"keywords",
|
|
402
|
+
"author",
|
|
403
|
+
"version",
|
|
404
|
+
"harnesses",
|
|
405
|
+
"type",
|
|
406
|
+
"inclusion",
|
|
407
|
+
"file_patterns",
|
|
408
|
+
"categories",
|
|
409
|
+
"ecosystem",
|
|
410
|
+
"depends",
|
|
411
|
+
"enhances",
|
|
412
|
+
"id",
|
|
413
|
+
"license",
|
|
414
|
+
"maturity",
|
|
415
|
+
"trust",
|
|
416
|
+
"risk-level",
|
|
417
|
+
"audience",
|
|
418
|
+
"model-assumptions",
|
|
419
|
+
"successor",
|
|
420
|
+
"replaces",
|
|
421
|
+
"collections",
|
|
422
|
+
"inherit-hooks",
|
|
423
|
+
"visibility",
|
|
424
|
+
"priority",
|
|
425
|
+
"outcomes",
|
|
426
|
+
"harness-config",
|
|
427
|
+
"migrations",
|
|
428
|
+
]);
|
|
429
|
+
|
|
430
|
+
for (const [key, value] of Object.entries(frontmatterData)) {
|
|
431
|
+
if (!knownFrontmatterKeys.has(key)) {
|
|
432
|
+
extraFields[
|
|
433
|
+
namespacedExtraField(context.format.id, primaryDoc.path, key)
|
|
434
|
+
] = value;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Build the canonical candidate
|
|
439
|
+
const candidate: Record<string, unknown> = {
|
|
440
|
+
name,
|
|
441
|
+
frontmatter: {
|
|
442
|
+
name,
|
|
443
|
+
...frontmatterData,
|
|
444
|
+
type: (frontmatterData.type as string) ?? "skill",
|
|
445
|
+
harnesses: (frontmatterData.harnesses as string[]) ?? ["kiro"],
|
|
446
|
+
},
|
|
447
|
+
body,
|
|
448
|
+
hooks: allHooks,
|
|
449
|
+
mcpServers: allMcpServers,
|
|
450
|
+
workflows: [],
|
|
451
|
+
sourcePath: primaryDoc.path,
|
|
452
|
+
extraFields,
|
|
453
|
+
bodyOverrides: {},
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
return {
|
|
457
|
+
candidate,
|
|
458
|
+
diagnostics,
|
|
459
|
+
consumedPaths: accountant.getConsumedPaths(),
|
|
460
|
+
preservedPaths: accountant.getPreservedPaths(),
|
|
461
|
+
};
|
|
462
|
+
}
|