- Add sync --target droid (skills to ~/.factory/skills/) - Add sync --target cursor (skills + MCP to .cursor/) - Extract expandHome/resolveTargetHome to src/utils/resolve-home.ts - Remove duplicated path helpers from convert.ts and install.ts - Bump version to 0.6.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
79 lines
2.3 KiB
TypeScript
79 lines
2.3 KiB
TypeScript
import fs from "fs/promises"
|
|
import path from "path"
|
|
import type { ClaudeHomeConfig } from "../parsers/claude-home"
|
|
import type { ClaudeMcpServer } from "../types/claude"
|
|
import { forceSymlink, isValidSkillName } from "../utils/symlink"
|
|
|
|
type CursorMcpServer = {
|
|
command?: string
|
|
args?: string[]
|
|
url?: string
|
|
env?: Record<string, string>
|
|
headers?: Record<string, string>
|
|
}
|
|
|
|
type CursorMcpConfig = {
|
|
mcpServers: Record<string, CursorMcpServer>
|
|
}
|
|
|
|
export async function syncToCursor(
|
|
config: ClaudeHomeConfig,
|
|
outputRoot: string,
|
|
): Promise<void> {
|
|
const skillsDir = path.join(outputRoot, "skills")
|
|
await fs.mkdir(skillsDir, { recursive: true })
|
|
|
|
for (const skill of config.skills) {
|
|
if (!isValidSkillName(skill.name)) {
|
|
console.warn(`Skipping skill with invalid name: ${skill.name}`)
|
|
continue
|
|
}
|
|
const target = path.join(skillsDir, skill.name)
|
|
await forceSymlink(skill.sourceDir, target)
|
|
}
|
|
|
|
if (Object.keys(config.mcpServers).length > 0) {
|
|
const mcpPath = path.join(outputRoot, "mcp.json")
|
|
const existing = await readJsonSafe(mcpPath)
|
|
const converted = convertMcpForCursor(config.mcpServers)
|
|
const merged: CursorMcpConfig = {
|
|
mcpServers: {
|
|
...(existing.mcpServers ?? {}),
|
|
...converted,
|
|
},
|
|
}
|
|
await fs.writeFile(mcpPath, JSON.stringify(merged, null, 2), { mode: 0o600 })
|
|
}
|
|
}
|
|
|
|
async function readJsonSafe(filePath: string): Promise<Partial<CursorMcpConfig>> {
|
|
try {
|
|
const content = await fs.readFile(filePath, "utf-8")
|
|
return JSON.parse(content) as Partial<CursorMcpConfig>
|
|
} catch (err) {
|
|
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
return {}
|
|
}
|
|
throw err
|
|
}
|
|
}
|
|
|
|
function convertMcpForCursor(
|
|
servers: Record<string, ClaudeMcpServer>,
|
|
): Record<string, CursorMcpServer> {
|
|
const result: Record<string, CursorMcpServer> = {}
|
|
for (const [name, server] of Object.entries(servers)) {
|
|
const entry: CursorMcpServer = {}
|
|
if (server.command) {
|
|
entry.command = server.command
|
|
if (server.args && server.args.length > 0) entry.args = server.args
|
|
if (server.env && Object.keys(server.env).length > 0) entry.env = server.env
|
|
} else if (server.url) {
|
|
entry.url = server.url
|
|
if (server.headers && Object.keys(server.headers).length > 0) entry.headers = server.headers
|
|
}
|
|
result[name] = entry
|
|
}
|
|
return result
|
|
}
|