199 lines
6.5 KiB
TypeScript
199 lines
6.5 KiB
TypeScript
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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.",
|
|
)
|
|
}
|