UNPKG

@brianlovin/notion-skills

Version:

Sync agent skills from a Notion database to Claude Code, Codex, OpenCode, Cursor, Gemini CLI.

92 lines 2.75 kB
import { existsSync, lstatSync } from "node:fs"; import { mkdir, readlink, rm, symlink } from "node:fs/promises"; import { dirname, join } from "node:path"; import { KNOWN_TARGETS, findTargetByKey } from "./known-targets.js"; export function detectTargets() { return KNOWN_TARGETS.map((t) => ({ key: t.key, label: t.label, dir: t.dir, installed: existsSync(dirname(t.dir)), // parent (~/.claude, etc.) exists })); } export function targetsForKeys(keys) { const out = []; for (const key of keys) { const t = findTargetByKey(key); if (!t) { // Skip unknown keys silently — could be left over from an older // scope.json after a target was renamed/removed. continue; } out.push({ key: t.key, label: t.label, dir: t.dir, installed: existsSync(dirname(t.dir)), }); } return out; } /** Re-export so callers can pick up the registry without juggling imports. */ export { KNOWN_TARGETS, findTargetByKey }; /** * Ensure a symlink at `linkPath` points to `realPath`. * * Returns: * - "created" if a new symlink was made * - "ok" if the symlink already existed and was correct * - "skipped" if a non-symlink (manually authored skill) is in the way * - "fixed" if a stale symlink was replaced */ export async function ensureSymlink(realPath, linkPath) { await mkdir(dirname(linkPath), { recursive: true }); if (!existsSync(linkPath)) { try { await symlink(realPath, linkPath); return "created"; } catch (err) { if (err.code === "EEXIST") { return ensureSymlink(realPath, linkPath); } throw err; } } const stat = lstatSync(linkPath); if (!stat.isSymbolicLink()) { return "skipped"; } const current = await readlink(linkPath); if (current === realPath) return "ok"; await rm(linkPath, { force: true }); await symlink(realPath, linkPath); return "fixed"; } export async function removeSymlink(linkPath) { if (!existsSync(linkPath)) { // Could be a dangling symlink — lstat through manual check try { lstatSync(linkPath); } catch { return "absent"; } } let stat; try { stat = lstatSync(linkPath); } catch { return "absent"; } if (!stat.isSymbolicLink()) return "skipped"; await rm(linkPath, { force: true }); return "removed"; } export function targetSkillPath(target, skillName) { return join(target.dir, skillName); } //# sourceMappingURL=targets.js.map