Files
claude-engineering-plugin/src/commands/sync.ts
2026-03-02 21:02:21 -08:00

89 lines
2.9 KiB
TypeScript

import { defineCommand } from "citty"
import path from "path"
import { loadClaudeHome } from "../parsers/claude-home"
import {
getDefaultSyncRegistryContext,
getSyncTarget,
isSyncTargetName,
syncTargetNames,
type SyncTargetName,
} from "../sync/registry"
import { expandHome } from "../utils/resolve-home"
import { hasPotentialSecrets } from "../utils/secrets"
import { detectInstalledTools } from "../utils/detect-tools"
const validTargets = [...syncTargetNames, "all"] as const
type SyncTarget = SyncTargetName | "all"
function isValidTarget(value: string): value is SyncTarget {
return value === "all" || isSyncTargetName(value)
}
export default defineCommand({
meta: {
name: "sync",
description: "Sync Claude Code config (~/.claude/) to supported provider configs and skills",
},
args: {
target: {
type: "string",
default: "all",
description: `Target: ${syncTargetNames.join(" | ")} | all (default: all)`,
},
claudeHome: {
type: "string",
alias: "claude-home",
description: "Path to Claude home (default: ~/.claude)",
},
},
async run({ args }) {
if (!isValidTarget(args.target)) {
throw new Error(`Unknown target: ${args.target}. Use one of: ${validTargets.join(", ")}`)
}
const { home, cwd } = getDefaultSyncRegistryContext()
const claudeHome = expandHome(args.claudeHome ?? path.join(home, ".claude"))
const config = await loadClaudeHome(claudeHome)
// Warn about potential secrets in MCP env vars
if (hasPotentialSecrets(config.mcpServers)) {
console.warn(
"⚠️ Warning: MCP servers contain env vars that may include secrets (API keys, tokens).\n" +
" These will be copied to the target config. Review before sharing the config file.",
)
}
if (args.target === "all") {
const detected = await detectInstalledTools()
const activeTargets = detected.filter((t) => t.detected).map((t) => t.name)
if (activeTargets.length === 0) {
console.log("No AI coding tools detected.")
return
}
console.log(`Syncing to ${activeTargets.length} detected tool(s)...`)
for (const tool of detected) {
console.log(` ${tool.detected ? "✓" : "✗"} ${tool.name}${tool.reason}`)
}
for (const name of activeTargets) {
const target = getSyncTarget(name as SyncTargetName)
const outputRoot = target.resolveOutputRoot(home, cwd)
await target.sync(config, outputRoot)
console.log(`✓ Synced to ${name}: ${outputRoot}`)
}
return
}
console.log(
`Syncing ${config.skills.length} skills, ${config.commands?.length ?? 0} commands, ${Object.keys(config.mcpServers).length} MCP servers...`,
)
const target = getSyncTarget(args.target as SyncTargetName)
const outputRoot = target.resolveOutputRoot(home, cwd)
await target.sync(config, outputRoot)
console.log(`✓ Synced to ${args.target}: ${outputRoot}`)
},
})