/** * `skills sync` — the last mile: write skills from this machine's corpus * (~/.hasna/skills/installed//) into each coding agent's global skills directory, * per-tool adapted, so an agent auto-loads them. * * This is the deliberate reversal of the old "pins, not installs" stub: agent skill * folders used to be left entirely unmanaged and every write path returned success:false. * They are now written — but only the ones this tool owns. Ownership is tracked with a * marker file. An unmarked directory is left alone by default; only one that already has * SKILL.md can be explicitly adopted, while any other unmarked directory is never touched. */ import { renameSync } from "node:fs"; /** * The coding agents `skills sync` targets by default. These runtimes load a * `/./skills//SKILL.md` tree (OpenCode is the one path exception). * Gemini is retired; Windsurf/pi are addressable through installSkillForAgent but are * not in the default fan-out. */ export type SyncAgent = "claude" | "codewith" | "codex" | "opencode" | "cursor"; export declare const SYNC_AGENTS: readonly SyncAgent[]; /** * Ownership marker written beside every SKILL.md this tool syncs. Its presence is how a * re-sync tells "a skill I wrote, safe to update" from "a skill the user hand-authored, * do not touch". A hidden sidecar rather than a frontmatter field so the SKILL.md the * agent loads stays exactly the adapted document and nothing else. */ export declare const SYNC_MARKER_FILE = ".hasna-skills.json"; export declare const SYNC_MARKER_MANAGED_BY = "@hasna/skills"; export interface SyncMarker { managedBy: string; skill: string; source: string; syncedAt: string; } export declare function isSyncAgent(value: string): value is SyncAgent; export declare function resolveSyncAgents(arg?: string): SyncAgent[]; /** The global skills directory for an agent, honouring a test-supplied home. */ export declare function agentGlobalSkillsDir(agent: SyncAgent, homeDir?: string): string; /** * Adapt a SKILL.md for a target agent. * * `user_invocable` is a Claude-only frontmatter field (it controls Claude's slash menu). * The Claude copy carries it; every other agent's copy has it stripped, because the field * is meaningless there and skill-sync policy is that non-Claude copies must not carry * Claude-specific frontmatter. The body is left verbatim: automatically rewriting prose * would corrupt meaning, and corpus/instruction skills are authored tool-neutral. */ export declare function adaptSkillMdForAgent(skillMd: string, agent: string): string; /** A pointer SKILL.md for an executable skill: what it is and how to actually run it. */ export declare function pointerSkillMd(name: string, description: string): string; export type SyncActionKind = "create" | "update" | "skip"; export interface AgentSyncAction { skill: string; agent: SyncAgent; path: string; action: SyncActionKind; reason?: string; } export interface SyncSkillsOptions { /** Specific skills to sync. When empty, every corpus skill is synced. */ names?: string[]; /** Explicit; the default when no names are given is already "all". */ all?: boolean; /** Target agents. Defaults to SYNC_AGENTS. */ agents?: SyncAgent[]; /** Report intended actions without writing anything. */ dryRun?: boolean; /** Adopt an unmanaged agent skill only when its directory already contains SKILL.md. */ force?: boolean; /** Corpus root override. Tests only. */ rootDir?: string; /** Home directory override for agent skill dirs. Tests only. */ homeDir?: string; } export interface SyncSkillsResult { actions: AgentSyncAction[]; } export declare function syncSkillsToAgents(options?: SyncSkillsOptions): SyncSkillsResult; export interface WriteManagedAgentSkillParams { skill: string; agent: SyncAgent; skillMd: string; source?: "bundled" | "corpus"; resourceDir?: string; homeDir?: string; dryRun?: boolean; force?: boolean; } /** * Write one skill into one agent's global folder, non-clobbering. * * A directory this tool has written before carries the marker file and is replaced with * an exact mirror. A directory with a SKILL.md but no marker is the user's own skill and * is skipped unless `force` explicitly adopts it. Any other pre-existing unmarked * directory is always left untouched. A fresh directory is created. */ export declare function writeManagedAgentSkill(params: WriteManagedAgentSkillParams): AgentSyncAction; export interface ManagedDirWriteResult { action: SyncActionKind; path: string; reason?: string; } export interface ManagedDirWriteOptions { skill: string; source?: string; resourceDir?: string; dryRun?: boolean; force?: boolean; /** Test seam for exercising rollback after the original directory has moved. */ renameDirectory?: typeof renameSync; } /** * The primitive both the fan-out sync and the single-skill installer share: replace one * owned skill directory with an exact staged mirror, non-clobbering, and stamp it as ours. * `dir` is the skill's own directory (…/skills/), so callers control scope (global * vs project) by choosing the directory. */ export declare function writeManagedSkillDir(dir: string, skillMd: string, options: ManagedDirWriteOptions): ManagedDirWriteResult; /** * Remove a skill this tool synced from an agent folder. Refuses to delete a directory it * did not write (no marker), so it can never remove a user's hand-authored skill. */ export declare function removeManagedAgentSkill(skill: string, agent: SyncAgent, homeDir?: string): boolean;