import path from "path" import type { ClaudeHomeConfig } from "../parsers/claude-home" import type { ClaudePlugin } from "../types/claude" import { backupFile, writeText } from "../utils/files" import { convertClaudeToCodex } from "../converters/claude-to-codex" import { convertClaudeToCopilot } from "../converters/claude-to-copilot" import { convertClaudeToDroid } from "../converters/claude-to-droid" import { convertClaudeToGemini } from "../converters/claude-to-gemini" import { convertClaudeToKiro } from "../converters/claude-to-kiro" import { convertClaudeToOpenCode, type ClaudeToOpenCodeOptions } from "../converters/claude-to-opencode" import { convertClaudeToPi } from "../converters/claude-to-pi" import { convertClaudeToQwen, type ClaudeToQwenOptions } from "../converters/claude-to-qwen" import { convertClaudeToWindsurf } from "../converters/claude-to-windsurf" import { writeWindsurfBundle } from "../targets/windsurf" type WindsurfSyncScope = "global" | "workspace" const HOME_SYNC_PLUGIN_ROOT = path.join(process.cwd(), ".compound-sync-home") const DEFAULT_SYNC_OPTIONS: ClaudeToOpenCodeOptions = { agentMode: "subagent", inferTemperature: false, permissions: "none", } const DEFAULT_QWEN_SYNC_OPTIONS: ClaudeToQwenOptions = { agentMode: "subagent", inferTemperature: false, } function hasCommands(config: ClaudeHomeConfig): boolean { return (config.commands?.length ?? 0) > 0 } function buildClaudeHomePlugin(config: ClaudeHomeConfig): ClaudePlugin { return { root: HOME_SYNC_PLUGIN_ROOT, manifest: { name: "claude-home", version: "1.0.0", description: "Personal Claude Code home config", }, agents: [], commands: config.commands ?? [], skills: config.skills, mcpServers: undefined, } } export async function syncOpenCodeCommands( config: ClaudeHomeConfig, outputRoot: string, ): Promise { if (!hasCommands(config)) return const plugin = buildClaudeHomePlugin(config) const bundle = convertClaudeToOpenCode(plugin, DEFAULT_SYNC_OPTIONS) for (const commandFile of bundle.commandFiles) { const commandPath = path.join(outputRoot, "commands", `${commandFile.name}.md`) const backupPath = await backupFile(commandPath) if (backupPath) { console.log(`Backed up existing command file to ${backupPath}`) } await writeText(commandPath, commandFile.content + "\n") } } export async function syncCodexCommands( config: ClaudeHomeConfig, outputRoot: string, ): Promise { if (!hasCommands(config)) return const plugin = buildClaudeHomePlugin(config) const bundle = convertClaudeToCodex(plugin, DEFAULT_SYNC_OPTIONS) for (const prompt of bundle.prompts) { await writeText(path.join(outputRoot, "prompts", `${prompt.name}.md`), prompt.content + "\n") } for (const skill of bundle.generatedSkills) { await writeText(path.join(outputRoot, "skills", skill.name, "SKILL.md"), skill.content + "\n") } } export async function syncPiCommands( config: ClaudeHomeConfig, outputRoot: string, ): Promise { if (!hasCommands(config)) return const plugin = buildClaudeHomePlugin(config) const bundle = convertClaudeToPi(plugin, DEFAULT_SYNC_OPTIONS) for (const prompt of bundle.prompts) { await writeText(path.join(outputRoot, "prompts", `${prompt.name}.md`), prompt.content + "\n") } for (const extension of bundle.extensions) { await writeText(path.join(outputRoot, "extensions", extension.name), extension.content + "\n") } } export async function syncDroidCommands( config: ClaudeHomeConfig, outputRoot: string, ): Promise { if (!hasCommands(config)) return const plugin = buildClaudeHomePlugin(config) const bundle = convertClaudeToDroid(plugin, DEFAULT_SYNC_OPTIONS) for (const command of bundle.commands) { await writeText(path.join(outputRoot, "commands", `${command.name}.md`), command.content + "\n") } } export async function syncCopilotCommands( config: ClaudeHomeConfig, outputRoot: string, ): Promise { if (!hasCommands(config)) return const plugin = buildClaudeHomePlugin(config) const bundle = convertClaudeToCopilot(plugin, DEFAULT_SYNC_OPTIONS) for (const skill of bundle.generatedSkills) { await writeText(path.join(outputRoot, "skills", skill.name, "SKILL.md"), skill.content + "\n") } } export async function syncGeminiCommands( config: ClaudeHomeConfig, outputRoot: string, ): Promise { if (!hasCommands(config)) return const plugin = buildClaudeHomePlugin(config) const bundle = convertClaudeToGemini(plugin, DEFAULT_SYNC_OPTIONS) for (const command of bundle.commands) { await writeText(path.join(outputRoot, "commands", `${command.name}.toml`), command.content + "\n") } } export async function syncKiroCommands( config: ClaudeHomeConfig, outputRoot: string, ): Promise { if (!hasCommands(config)) return const plugin = buildClaudeHomePlugin(config) const bundle = convertClaudeToKiro(plugin, DEFAULT_SYNC_OPTIONS) for (const skill of bundle.generatedSkills) { await writeText(path.join(outputRoot, "skills", skill.name, "SKILL.md"), skill.content + "\n") } } export async function syncWindsurfCommands( config: ClaudeHomeConfig, outputRoot: string, scope: WindsurfSyncScope = "global", ): Promise { if (!hasCommands(config)) return const plugin = buildClaudeHomePlugin(config) const bundle = convertClaudeToWindsurf(plugin, DEFAULT_SYNC_OPTIONS) await writeWindsurfBundle(outputRoot, { agentSkills: [], commandWorkflows: bundle.commandWorkflows, skillDirs: [], mcpConfig: null, }, scope) } export async function syncQwenCommands( config: ClaudeHomeConfig, outputRoot: string, ): Promise { if (!hasCommands(config)) return const plugin = buildClaudeHomePlugin(config) const bundle = convertClaudeToQwen(plugin, DEFAULT_QWEN_SYNC_OPTIONS) for (const commandFile of bundle.commandFiles) { const parts = commandFile.name.split(":") if (parts.length > 1) { const nestedDir = path.join(outputRoot, "commands", ...parts.slice(0, -1)) await writeText(path.join(nestedDir, `${parts[parts.length - 1]}.md`), commandFile.content + "\n") continue } await writeText(path.join(outputRoot, "commands", `${commandFile.name}.md`), commandFile.content + "\n") } } export function warnUnsupportedOpenClawCommands(config: ClaudeHomeConfig): void { if (!hasCommands(config)) return console.warn( "Warning: OpenClaw personal command sync is skipped because this sync target currently has no documented user-level command surface.", ) }