feat: fix skill transformation pipeline across all targets (#334)

This commit is contained in:
Trevin Chow
2026-03-21 19:45:20 -07:00
committed by GitHub
parent 0f6448d81c
commit 4087e1df82
33 changed files with 624 additions and 86 deletions

View File

@@ -1,6 +1,5 @@
import { promises as fs } from "fs"
import path from "path"
import { backupFile, ensureDir, readText, writeText } from "../utils/files"
import { backupFile, copySkillDir, ensureDir, writeText } from "../utils/files"
import type { CodexBundle } from "../types/codex"
import type { ClaudeMcpServer } from "../types/claude"
import { transformContentForCodex } from "../utils/codex-content"
@@ -19,10 +18,12 @@ export async function writeCodexBundle(outputRoot: string, bundle: CodexBundle):
if (bundle.skillDirs.length > 0) {
const skillsRoot = path.join(codexRoot, "skills")
for (const skill of bundle.skillDirs) {
await copyCodexSkillDir(
await copySkillDir(
skill.sourceDir,
path.join(skillsRoot, skill.name),
bundle.invocationTargets,
(content) => transformContentForCodex(content, bundle.invocationTargets, {
unknownSlashBehavior: "preserve",
}),
)
}
}
@@ -45,41 +46,6 @@ export async function writeCodexBundle(outputRoot: string, bundle: CodexBundle):
}
}
async function copyCodexSkillDir(
sourceDir: string,
targetDir: string,
invocationTargets?: CodexBundle["invocationTargets"],
): Promise<void> {
await ensureDir(targetDir)
const entries = await fs.readdir(sourceDir, { withFileTypes: true })
for (const entry of entries) {
const sourcePath = path.join(sourceDir, entry.name)
const targetPath = path.join(targetDir, entry.name)
if (entry.isDirectory()) {
await copyCodexSkillDir(sourcePath, targetPath, invocationTargets)
continue
}
if (!entry.isFile()) continue
if (entry.name === "SKILL.md") {
const content = await readText(sourcePath)
await writeText(
targetPath,
transformContentForCodex(content, invocationTargets, {
unknownSlashBehavior: "preserve",
}),
)
continue
}
await ensureDir(path.dirname(targetPath))
await fs.copyFile(sourcePath, targetPath)
}
}
function resolveCodexRoot(outputRoot: string): string {
return path.basename(outputRoot) === ".codex" ? outputRoot : path.join(outputRoot, ".codex")
}

View File

@@ -1,5 +1,6 @@
import path from "path"
import { backupFile, copyDir, ensureDir, writeJson, writeText } from "../utils/files"
import { backupFile, copySkillDir, ensureDir, writeJson, writeText } from "../utils/files"
import { transformContentForCopilot } from "../converters/claude-to-copilot"
import type { CopilotBundle } from "../types/copilot"
export async function writeCopilotBundle(outputRoot: string, bundle: CopilotBundle): Promise<void> {
@@ -23,7 +24,7 @@ export async function writeCopilotBundle(outputRoot: string, bundle: CopilotBund
if (bundle.skillDirs.length > 0) {
const skillsDir = path.join(paths.githubDir, "skills")
for (const skill of bundle.skillDirs) {
await copyDir(skill.sourceDir, path.join(skillsDir, skill.name))
await copySkillDir(skill.sourceDir, path.join(skillsDir, skill.name), transformContentForCopilot)
}
}

View File

@@ -1,5 +1,6 @@
import path from "path"
import { copyDir, ensureDir, resolveCommandPath, writeText } from "../utils/files"
import { copySkillDir, ensureDir, resolveCommandPath, writeText } from "../utils/files"
import { transformContentForDroid } from "../converters/claude-to-droid"
import type { DroidBundle } from "../types/droid"
export async function writeDroidBundle(outputRoot: string, bundle: DroidBundle): Promise<void> {
@@ -24,7 +25,7 @@ export async function writeDroidBundle(outputRoot: string, bundle: DroidBundle):
if (bundle.skillDirs.length > 0) {
await ensureDir(paths.skillsDir)
for (const skill of bundle.skillDirs) {
await copyDir(skill.sourceDir, path.join(paths.skillsDir, skill.name))
await copySkillDir(skill.sourceDir, path.join(paths.skillsDir, skill.name), transformContentForDroid)
}
}
}

View File

@@ -1,5 +1,6 @@
import path from "path"
import { backupFile, copyDir, ensureDir, pathExists, readJson, resolveCommandPath, writeJson, writeText } from "../utils/files"
import { backupFile, copySkillDir, ensureDir, pathExists, readJson, resolveCommandPath, writeJson, writeText } from "../utils/files"
import { transformContentForGemini } from "../converters/claude-to-gemini"
import type { GeminiBundle } from "../types/gemini"
export async function writeGeminiBundle(outputRoot: string, bundle: GeminiBundle): Promise<void> {
@@ -14,7 +15,7 @@ export async function writeGeminiBundle(outputRoot: string, bundle: GeminiBundle
if (bundle.skillDirs.length > 0) {
for (const skill of bundle.skillDirs) {
await copyDir(skill.sourceDir, path.join(paths.skillsDir, skill.name))
await copySkillDir(skill.sourceDir, path.join(paths.skillsDir, skill.name), transformContentForGemini)
}
}

View File

@@ -1,5 +1,6 @@
import path from "path"
import { backupFile, copyDir, ensureDir, pathExists, readJson, writeJson, writeText } from "../utils/files"
import { backupFile, copySkillDir, ensureDir, pathExists, readJson, writeJson, writeText } from "../utils/files"
import { transformContentForKiro } from "../converters/claude-to-kiro"
import type { KiroBundle } from "../types/kiro"
export async function writeKiroBundle(outputRoot: string, bundle: KiroBundle): Promise<void> {
@@ -50,7 +51,10 @@ export async function writeKiroBundle(outputRoot: string, bundle: KiroBundle): P
continue
}
await copyDir(skill.sourceDir, destDir)
const knownAgentNames = bundle.agents.map((a) => a.name)
await copySkillDir(skill.sourceDir, destDir, (content) =>
transformContentForKiro(content, knownAgentNames),
)
}
}

View File

@@ -1,13 +1,14 @@
import path from "path"
import {
backupFile,
copyDir,
copySkillDir,
ensureDir,
pathExists,
readText,
writeJson,
writeText,
} from "../utils/files"
import { transformContentForPi } from "../converters/claude-to-pi"
import type { PiBundle } from "../types/pi"
const PI_AGENTS_BLOCK_START = "<!-- BEGIN COMPOUND PI TOOL MAP -->"
@@ -37,7 +38,7 @@ export async function writePiBundle(outputRoot: string, bundle: PiBundle): Promi
}
for (const skill of bundle.skillDirs) {
await copyDir(skill.sourceDir, path.join(paths.skillsDir, skill.name))
await copySkillDir(skill.sourceDir, path.join(paths.skillsDir, skill.name), transformContentForPi)
}
for (const skill of bundle.generatedSkills) {

View File

@@ -1,6 +1,7 @@
import path from "path"
import { backupFile, copyDir, ensureDir, pathExists, readJson, writeJsonSecure, writeText } from "../utils/files"
import { backupFile, copySkillDir, ensureDir, pathExists, readJson, writeJsonSecure, writeText } from "../utils/files"
import { formatFrontmatter } from "../utils/frontmatter"
import { transformContentForWindsurf } from "../converters/claude-to-windsurf"
import type { WindsurfBundle } from "../types/windsurf"
import type { TargetScope } from "./index"
@@ -58,7 +59,10 @@ export async function writeWindsurfBundle(outputRoot: string, bundle: WindsurfBu
continue
}
await copyDir(skill.sourceDir, destDir)
const knownAgentNames = bundle.agentSkills.map((s) => s.name)
await copySkillDir(skill.sourceDir, destDir, (content) =>
transformContentForWindsurf(content, knownAgentNames),
)
}
}