chore: Resolve conflicts with main, update to v0.12.0

- sync.ts: add gemini + all targets, keep copilot, remove cursor (native), use shared hasPotentialSecrets
- install.ts + convert.ts: import both detectInstalledTools and resolveTargetOutputRoot; update --to all block to use new object API; fix resolvedScope ordering (was referencing target before definition)
- CHANGELOG.md: add v0.12.0 entry (auto-detect + Gemini sync)
- README.md: merge all install targets, collapsible output format table, sync defaults to --target all
- package.json: bump to 0.12.0
- sync --target now defaults to "all" when omitted

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Kieran Klaassen
2026-03-01 15:12:21 -08:00
83 changed files with 9076 additions and 913 deletions

View File

@@ -6,32 +6,19 @@ import { syncToOpenCode } from "../sync/opencode"
import { syncToCodex } from "../sync/codex"
import { syncToPi } from "../sync/pi"
import { syncToDroid } from "../sync/droid"
import { syncToCursor } from "../sync/cursor"
import { syncToCopilot } from "../sync/copilot"
import { syncToGemini } from "../sync/gemini"
import { expandHome } from "../utils/resolve-home"
import { hasPotentialSecrets } from "../utils/secrets"
import { detectInstalledTools } from "../utils/detect-tools"
const validTargets = ["opencode", "codex", "pi", "droid", "cursor", "gemini", "all"] as const
const validTargets = ["opencode", "codex", "pi", "droid", "copilot", "gemini", "all"] as const
type SyncTarget = (typeof validTargets)[number]
function isValidTarget(value: string): value is SyncTarget {
return (validTargets as readonly string[]).includes(value)
}
/** Check if any MCP servers have env vars that might contain secrets */
function hasPotentialSecrets(mcpServers: Record<string, unknown>): boolean {
const sensitivePatterns = /key|token|secret|password|credential|api_key/i
for (const server of Object.values(mcpServers)) {
const env = (server as { env?: Record<string, string> }).env
if (env) {
for (const key of Object.keys(env)) {
if (sensitivePatterns.test(key)) return true
}
}
}
return false
}
function resolveOutputRoot(target: string): string {
switch (target) {
case "opencode":
@@ -42,8 +29,8 @@ function resolveOutputRoot(target: string): string {
return path.join(os.homedir(), ".pi", "agent")
case "droid":
return path.join(os.homedir(), ".factory")
case "cursor":
return path.join(process.cwd(), ".cursor")
case "copilot":
return path.join(process.cwd(), ".github")
case "gemini":
return path.join(process.cwd(), ".gemini")
default:
@@ -65,8 +52,8 @@ async function syncTarget(target: string, config: Awaited<ReturnType<typeof load
case "droid":
await syncToDroid(config, outputRoot)
break
case "cursor":
await syncToCursor(config, outputRoot)
case "copilot":
await syncToCopilot(config, outputRoot)
break
case "gemini":
await syncToGemini(config, outputRoot)
@@ -77,13 +64,13 @@ async function syncTarget(target: string, config: Awaited<ReturnType<typeof load
export default defineCommand({
meta: {
name: "sync",
description: "Sync Claude Code config (~/.claude/) to OpenCode, Codex, Pi, Droid, Cursor, or Gemini",
description: "Sync Claude Code config (~/.claude/) to OpenCode, Codex, Pi, Droid, Copilot, or Gemini",
},
args: {
target: {
type: "string",
required: true,
description: "Target: opencode | codex | pi | droid | cursor | gemini | all",
default: "all",
description: "Target: opencode | codex | pi | droid | copilot | gemini | all (default: all)",
},
claudeHome: {
type: "string",