@cleocode/caamp 2026.9.8 → 2026.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/instructions/injector.ts","../src/core/fs/atomic.ts","../src/core/registry/providers.ts","../src/core/instructions/markers.ts","../src/core/instructions/templates.ts"],"sourcesContent":["/**\n * Marker-based instruction file injection\n *\n * Injects content blocks between CAAMP markers in instruction files\n * (CLAUDE.md, AGENTS.md, GEMINI.md) and agent-definition files\n * (cleo-subagent.md, seed agent profiles) per provider's native folder.\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { CaampInjectionAction } from '@cleocode/contracts/caamp-markers';\nimport { writeFileAtomic } from '@cleocode/core/tools/fs.js';\nimport type { InjectionCheckResult, InjectionStatus, Provider } from '../../types.js';\nimport { assertNotTornRead, withFileLock } from '../fs/atomic.js';\nimport { getAgentsHome } from '../paths/standard.js';\nimport { getProvider, getProviderInstructionReferences } from '../registry/providers.js';\nimport {\n blockPattern,\n buildBlock,\n type CaampBlock,\n normalizeMarkers,\n parseBlocks,\n reconcile,\n repairContent,\n} from './markers.js';\nimport { buildInjectionContent, type InjectionTemplate } from './templates.js';\n\nexport type { CaampBlock } from './markers.js';\n\n// ── Block parsing ──────────────────────────────────────────────────────────\n\n/**\n * Parse all CAAMP blocks from a file's content string.\n *\n * Returns an array of {@link CaampBlock} objects in order of appearance.\n * Blocks with malformed markers (START without matching END) are silently\n * skipped to avoid crashing on corrupted files.\n *\n * @param fileContent - Raw text content of the file\n * @returns Array of parsed CAAMP blocks\n *\n * @remarks\n * Strict: a block whose marker has been damaged (for example a lost `<`) is\n * not seen. Run {@link normalizeMarkers} first when the input may be corrupt.\n *\n * @example\n * ```typescript\n * const blocks = parseCaampBlocks(await readFile(agentsMd, \"utf-8\"));\n * ```\n *\n * @public\n */\nexport function parseCaampBlocks(fileContent: string): CaampBlock[] {\n return parseBlocks(fileContent);\n}\n\n/**\n * Result of deduplicating CAAMP blocks in a single file.\n *\n * @public\n */\nexport interface DedupeResult {\n /** Absolute path to the file that was processed. */\n filePath: string;\n /** Number of duplicate blocks removed. */\n removed: number;\n /** Number of unique blocks kept. */\n kept: number;\n /** `true` if the file was modified on disk; `false` if it was already clean. */\n modified: boolean;\n /**\n * Number of damaged marker lines healed back to canonical form.\n *\n * Non-zero means the file had corruption that the strict block pattern could\n * not see — the condition that used to make duplicates accumulate invisibly.\n */\n repaired: number;\n}\n\n/**\n * Deduplicate CAAMP blocks in a file by content.\n *\n * Groups all `<!-- CAAMP:START -->...<!-- CAAMP:END -->` blocks by their\n * trimmed inner content. For each group that has more than one block, keeps\n * only the **last** occurrence (most recently written) and removes the earlier\n * duplicates. Blocks with distinct contents are preserved in their original\n * relative order.\n *\n * Idempotent: calling this on an already-clean file returns `modified: false`\n * and makes no filesystem writes.\n *\n * @param filePath - Absolute path to the file to deduplicate\n * @returns Dedup summary\n *\n * @remarks\n * \"Last occurrence wins\" matches the behaviour of CLEO's injection chain,\n * which writes the canonical `@~/.local/share/cleo/…` path on every session.\n * Stale temp-path blocks from earlier sessions therefore have earlier indices\n * and are removed, leaving the canonical block.\n *\n * @example\n * ```typescript\n * const result = await dedupeFile(\"/home/user/.agents/AGENTS.md\");\n * console.log(`Removed ${result.removed} duplicate(s)`);\n * ```\n *\n * @public\n */\nexport async function dedupeFile(filePath: string): Promise<DedupeResult> {\n if (!existsSync(filePath)) {\n return { filePath, removed: 0, kept: 0, modified: false, repaired: 0 };\n }\n\n return withFileLock(filePath, async () => {\n const original = await readFile(filePath, 'utf-8');\n\n // Heal damaged markers FIRST. A block whose marker lost a character is\n // invisible to the strict pattern, so without this step the duplicates it\n // caused would be reported as \"already clean\" (T12051).\n const { content: healed, repaired } = normalizeMarkers(original);\n const blocks = parseBlocks(healed);\n\n if (blocks.length === 0) {\n if (repaired > 0 && healed !== original) {\n await writeFileAtomic({ path: filePath, content: healed });\n return { filePath, removed: 0, kept: 0, modified: true, repaired };\n }\n return { filePath, removed: 0, kept: 0, modified: false, repaired };\n }\n\n // Group by trimmed content — last occurrence wins\n const lastByContent = new Map<string, CaampBlock>();\n for (const block of blocks) {\n lastByContent.set(block.content, block);\n }\n\n const keepSet = new Set<CaampBlock>(lastByContent.values());\n const removed = blocks.length - keepSet.size;\n\n // Rebuild file content: walk through the healed text, emit blocks that are\n // in keepSet and skip duplicates. Non-block text between blocks is preserved.\n let result = '';\n let cursor = 0;\n\n for (const block of blocks) {\n // Emit any non-block text before this block\n result += healed.slice(cursor, block.startIndex);\n cursor = block.endIndex;\n\n if (keepSet.has(block)) {\n result += block.raw;\n }\n // Removed duplicates contribute nothing — surrounding whitespace is\n // normalized by the final collapse step below.\n }\n\n // Emit any trailing text after the last block\n result += healed.slice(cursor);\n\n // Normalize: collapse 3+ consecutive newlines → 2, trim trailing whitespace\n result = `${result.replace(/\\n{3,}/g, '\\n\\n').trimEnd()}\\n`;\n\n // Only rewrite when there is real work to do. Cosmetic differences alone\n // (a missing trailing newline, say) must not cause a write — callers batch\n // this across files they do not own.\n if (removed === 0 && repaired === 0) {\n return { filePath, removed: 0, kept: blocks.length, modified: false, repaired };\n }\n\n if (result === original) {\n return { filePath, removed: 0, kept: blocks.length, modified: false, repaired };\n }\n\n await writeFileAtomic({ path: filePath, content: result });\n return { filePath, removed, kept: keepSet.size, modified: true, repaired };\n });\n}\n\n/**\n * Deduplicate CAAMP blocks across multiple files.\n *\n * Runs {@link dedupeFile} on each path in order and collects results.\n * Files that do not exist are skipped silently (their result has `removed: 0`).\n *\n * @param filePaths - Array of absolute file paths to process\n * @returns Array of results, one per input path\n *\n * @example\n * ```typescript\n * const results = await dedupeFiles([\n * \"/home/user/.agents/AGENTS.md\",\n * \"/project/AGENTS.md\",\n * ]);\n * const totalRemoved = results.reduce((n, r) => n + r.removed, 0);\n * console.log(`Removed ${totalRemoved} duplicate(s) across ${results.length} files`);\n * ```\n *\n * @public\n */\nexport async function dedupeFiles(filePaths: string[]): Promise<DedupeResult[]> {\n const results: DedupeResult[] = [];\n for (const filePath of filePaths) {\n results.push(await dedupeFile(filePath));\n }\n return results;\n}\n\n/**\n * Every instruction file CAAMP may have written to, for a given project.\n *\n * Covers three tiers, because corruption in any one of them affects every\n * agent session:\n *\n * 1. The **global hub** `~/.agents/AGENTS.md` — the highest-risk file in the\n * system. Every `cleo init`, `cleo upgrade` and `cleo doctor` run rewrites\n * it regardless of which project invoked them, and until T12051 no health\n * check looked at it at all.\n * 2. The project's own `AGENTS.md`, `CLAUDE.md` and `GEMINI.md`.\n * 3. Each detected provider's global instruction file.\n *\n * Paths are de-duplicated and returned whether or not they exist; callers skip\n * missing ones.\n *\n * @param projectDir - Absolute path to the project directory\n * @param providers - Detected providers whose global files should be included\n * @returns De-duplicated absolute paths, global hub first\n *\n * @example\n * ```typescript\n * const paths = instructionFileCascade(\"/project\", getInstalledProviders());\n * const results = await dedupeFiles(paths);\n * ```\n *\n * @public\n */\nexport function instructionFileCascade(projectDir: string, providers: Provider[]): string[] {\n const paths: string[] = [\n join(getAgentsHome(), 'AGENTS.md'),\n join(projectDir, 'AGENTS.md'),\n join(projectDir, 'CLAUDE.md'),\n join(projectDir, 'GEMINI.md'),\n ];\n\n for (const provider of providers) {\n paths.push(join(provider.pathGlobal, provider.instructFile));\n }\n\n return [...new Set(paths)];\n}\n\n/**\n * Summary of a repair sweep across instruction files.\n *\n * @public\n */\nexport interface RepairResult {\n /** Per-file outcomes, in cascade order. Files that do not exist are omitted. */\n files: DedupeResult[];\n /** Total damaged marker lines healed across all files. */\n repaired: number;\n /** Total duplicate blocks removed across all files. */\n removed: number;\n /** How many files were actually rewritten. */\n filesModified: number;\n}\n\n/**\n * Heal damaged CAAMP markers and collapse duplicate blocks across a project's\n * whole instruction-file cascade.\n *\n * This is the repair that `cleo doctor` prescribes. It is deliberately\n * content-agnostic — it does not need to know what *should* be inside the\n * block, so it can restore a file to a well-formed single-block state without\n * a provider registry lookup or a template refresh.\n *\n * Safe to run repeatedly: a healthy cascade reports `repaired: 0`,\n * `removed: 0`, `filesModified: 0` and performs no writes.\n *\n * @param projectDir - Absolute path to the project directory\n * @param providers - Detected providers whose global files should be included\n * @returns Aggregate repair summary\n *\n * @example\n * ```typescript\n * const result = await repairInstructionFiles(\"/project\", getInstalledProviders());\n * console.log(`healed ${result.repaired} marker(s), removed ${result.removed} duplicate(s)`);\n * ```\n *\n * @public\n */\nexport async function repairInstructionFiles(\n projectDir: string,\n providers: Provider[],\n): Promise<RepairResult> {\n const paths = instructionFileCascade(projectDir, providers).filter((p) => existsSync(p));\n const files: DedupeResult[] = [];\n\n for (const filePath of paths) {\n files.push(\n await withFileLock(filePath, async (): Promise<DedupeResult> => {\n const original = await readFile(filePath, 'utf-8');\n const { content, blocksBefore, repaired } = repairContent(original);\n const removed = Math.max(0, blocksBefore - 1);\n\n if (removed === 0 && repaired === 0) {\n return { filePath, removed: 0, kept: blocksBefore, modified: false, repaired: 0 };\n }\n if (content === original) {\n return { filePath, removed: 0, kept: blocksBefore, modified: false, repaired };\n }\n\n await writeFileAtomic({ path: filePath, content: content });\n return {\n filePath,\n removed,\n kept: blocksBefore > 0 ? 1 : 0,\n modified: true,\n repaired,\n };\n }),\n );\n }\n\n return {\n files,\n repaired: files.reduce((n, r) => n + r.repaired, 0),\n removed: files.reduce((n, r) => n + r.removed, 0),\n filesModified: files.filter((r) => r.modified).length,\n };\n}\n\n/**\n * Check the status of a CAAMP injection block in an instruction file.\n *\n * Returns the injection status:\n * - `\"missing\"` - File does not exist\n * - `\"none\"` - File exists but has no CAAMP markers\n * - `\"current\"` - CAAMP block exists and matches expected content (or no expected content given)\n * - `\"outdated\"` - CAAMP block exists but differs from expected content\n *\n * @param filePath - Absolute path to the instruction file\n * @param expectedContent - Optional expected content to compare against\n * @returns The injection status\n *\n * @remarks\n * Does not modify the file. Safe to call repeatedly for status checks.\n *\n * @example\n * ```typescript\n * const status = await checkInjection(\"/project/CLAUDE.md\", expectedContent);\n * if (status === \"outdated\") {\n * console.log(\"CAAMP injection needs updating\");\n * }\n * ```\n *\n * @public\n */\nexport async function checkInjection(\n filePath: string,\n expectedContent?: string,\n): Promise<InjectionStatus> {\n if (!existsSync(filePath)) return 'missing';\n\n const raw = await readFile(filePath, 'utf-8');\n\n // Damaged markers are healed in memory before the check so a corrupted file\n // reports `outdated` (which the caller repairs) instead of `none` (which\n // used to make the caller prepend a second block). No write happens here.\n const { content, repaired } = normalizeMarkers(raw);\n const blocks = parseBlocks(content);\n\n if (blocks.length === 0) return 'none';\n\n // More than one block, or a marker that had to be healed, means the file is\n // not in its canonical state regardless of what the block body says.\n if (blocks.length > 1 || repaired > 0) return 'outdated';\n\n if (expectedContent) {\n return blocks[0]?.content === expectedContent.trim() ? 'current' : 'outdated';\n }\n\n return 'current';\n}\n\n/**\n * Inject content into an instruction file between CAAMP markers.\n *\n * Behavior depends on the file state:\n * - File does not exist: creates the file with the injection block → `\"created\"`\n * - File exists without markers: prepends the injection block → `\"added\"`\n * - File exists with a damaged marker: heals it and replaces in place → `\"repaired\"`\n * - File exists with multiple markers (duplicates): consolidates into a single block → `\"consolidated\"`\n * - File exists with markers, content differs: replaces the block → `\"updated\"`\n * - File exists with markers, content matches: no-op → `\"intact\"`\n *\n * This function is **idempotent** — calling it multiple times with the same\n * content will not modify the file after the first write.\n *\n * @param filePath - Absolute path to the instruction file\n * @param content - Content to inject between CAAMP markers\n * @returns The {@link CaampInjectionAction} describing what was done\n *\n * @remarks\n * Damaged markers are healed *before* the file is classified. This is what\n * stops a single lost character from ratcheting into duplicate blocks: prior\n * to T12051 a marker that lost its leading `<` was invisible to the block\n * pattern, so this function concluded the file had no block and prepended a\n * second one — permanently doubling the injected protocol text, and doubling\n * again on the next mishap.\n *\n * The whole read-modify-write cycle runs under a cross-process lock and the\n * write itself is atomic, because the busiest target — `~/.agents/AGENTS.md` —\n * is rewritten by every project on the machine.\n *\n * All text outside the CAAMP markers is preserved verbatim.\n *\n * @example\n * ```typescript\n * const action = await inject(\"/project/CLAUDE.md\", \"## My Config\\nSome content\");\n * console.log(`File ${action}`); // \"created\" on first call, \"intact\" on subsequent\n * ```\n *\n * @public\n */\nexport async function inject(filePath: string, content: string): Promise<CaampInjectionAction> {\n // Canonicalise the body once, at the entry, so the create path and the\n // reconcile path agree on what \"the same content\" means. Without this a\n // whitespace-only difference reported `updated` forever.\n const body = content.trim();\n\n if (!existsSync(filePath)) {\n // Create new file with injection block. Still atomic + locked so a\n // concurrent creator cannot interleave with us.\n return withFileLock<CaampInjectionAction>(filePath, async () => {\n await writeFileAtomic({ path: filePath, content: `${buildBlock(body)}\\n` });\n return 'created';\n });\n }\n\n return withFileLock<CaampInjectionAction>(filePath, async () => {\n const existing = await readFile(filePath, 'utf-8');\n\n // Fail closed on a torn read. Our own writes are atomic, but callers\n // outside this package still rewrite instruction files with a plain\n // `writeFile` (truncate-then-write), and a read landing inside that window\n // returns empty for a file that is not empty on disk. Reconciling from\n // that would replace every byte of the user's content with a lone block.\n if (existing.length === 0) {\n assertNotTornRead(filePath, existing, (await stat(filePath)).size);\n }\n\n const { content: next, blocksBefore, repaired } = reconcile(existing, body);\n\n if (next === existing) return 'intact';\n\n await writeFileAtomic({ path: filePath, content: next });\n\n // Report the most significant thing that happened, most invasive first.\n if (blocksBefore === 0) return 'added';\n if (repaired > 0) return 'repaired';\n if (blocksBefore > 1) return 'consolidated';\n return 'updated';\n });\n}\n\n/**\n * Remove the CAAMP injection block from an instruction file.\n *\n * If removing the block would leave the file empty, the file is deleted entirely.\n *\n * @param filePath - Absolute path to the instruction file\n * @returns `true` if a CAAMP block was found and removed, `false` otherwise\n *\n * @remarks\n * Cleans up any leftover blank lines after removing the block. If the file\n * would be entirely empty after removal, the file itself is deleted.\n *\n * Blocks whose markers are damaged are healed first, so uninstall removes them\n * too rather than leaving orphaned fragments behind.\n *\n * @example\n * ```typescript\n * const removed = await removeInjection(\"/project/CLAUDE.md\");\n * ```\n *\n * @public\n */\nexport async function removeInjection(filePath: string): Promise<boolean> {\n if (!existsSync(filePath)) return false;\n\n return withFileLock(filePath, async () => {\n const original = await readFile(filePath, 'utf-8');\n const { content } = normalizeMarkers(original);\n\n // A fresh pattern per call: a shared /g RegExp carries `lastIndex`, so the\n // previous `MARKER_PATTERN.test()` here skipped matches on alternate calls.\n if (parseBlocks(content).length === 0) return false;\n\n const cleaned = content\n .replace(blockPattern(), '')\n .replace(/^\\n{2,}/, '\\n')\n .trim();\n\n if (!cleaned) {\n // File would be empty - remove it entirely\n const { rm } = await import('node:fs/promises');\n await rm(filePath);\n } else {\n await writeFileAtomic({ path: filePath, content: `${cleaned}\\n` });\n }\n\n return true;\n });\n}\n\n/**\n * Check injection status across all providers' instruction files.\n *\n * Deduplicates by file path since multiple providers may share the same\n * instruction file (e.g. many providers use `AGENTS.md`).\n *\n * @param providers - Array of providers to check\n * @param projectDir - Absolute path to the project directory\n * @param scope - Whether to check project or global instruction files\n * @param expectedContent - Optional expected content to compare against\n * @returns Array of injection check results, one per unique instruction file\n *\n * @remarks\n * Multiple providers may share the same instruction file (e.g. many use\n * `AGENTS.md`). This function deduplicates to avoid redundant file reads.\n *\n * @example\n * ```typescript\n * const results = await checkAllInjections(providers, \"/project\", \"project\", expected);\n * const outdated = results.filter(r => r.status === \"outdated\");\n * ```\n *\n * @public\n */\nexport async function checkAllInjections(\n providers: Provider[],\n projectDir: string,\n scope: 'project' | 'global',\n expectedContent?: string,\n): Promise<InjectionCheckResult[]> {\n const results: InjectionCheckResult[] = [];\n const checked = new Set<string>();\n\n for (const provider of providers) {\n const filePath =\n scope === 'global'\n ? join(provider.pathGlobal, provider.instructFile)\n : join(projectDir, provider.instructFile);\n\n // Skip duplicates (multiple providers share same instruction file)\n if (checked.has(filePath)) continue;\n checked.add(filePath);\n\n const status = await checkInjection(filePath, expectedContent);\n\n results.push({\n file: filePath,\n provider: provider.id,\n status,\n fileExists: existsSync(filePath),\n });\n }\n\n return results;\n}\n\n/**\n * Inject content into all providers' instruction files.\n *\n * Deduplicates by file path to avoid injecting the same file multiple times.\n *\n * @param providers - Array of providers to inject into\n * @param projectDir - Absolute path to the project directory\n * @param scope - Whether to target project or global instruction files\n * @param content - Content to inject between CAAMP markers\n * @returns Map of file path to action taken (`\"created\"`, `\"added\"`, `\"consolidated\"`, `\"updated\"`, or `\"intact\"`)\n *\n * @remarks\n * Providers sharing the same instruction file are only written once to avoid\n * conflicting concurrent writes.\n *\n * @example\n * ```typescript\n * const results = await injectAll(providers, \"/project\", \"project\", content);\n * for (const [file, action] of results) {\n * console.log(`${file}: ${action}`);\n * }\n * ```\n *\n * @public\n */\nexport async function injectAll(\n providers: Provider[],\n projectDir: string,\n scope: 'project' | 'global',\n content: string,\n): Promise<Map<string, CaampInjectionAction>> {\n const results = new Map<string, CaampInjectionAction>();\n const injected = new Set<string>();\n\n for (const provider of providers) {\n const filePath =\n scope === 'global'\n ? join(provider.pathGlobal, provider.instructFile)\n : join(projectDir, provider.instructFile);\n\n // Skip duplicates\n if (injected.has(filePath)) continue;\n injected.add(filePath);\n\n const action = await inject(filePath, content);\n results.set(filePath, action);\n }\n\n return results;\n}\n\n// ── Provider Instruction File API ─────────────────────────────────\n\n/**\n * Options for ensuring a provider instruction file.\n *\n * @public\n */\nexport interface EnsureProviderInstructionFileOptions {\n /**\n * `@` references to inject (e.g. `[\"@AGENTS.md\"]`).\n *\n * When omitted or `undefined`, the references declared in the CAAMP provider\n * registry (`provider.instructionReferences`) are used as the default. Callers\n * that supply an explicit array always take precedence over the registry default.\n *\n * @defaultValue Registry `instructionReferences` for the provider\n */\n references?: string[];\n /** Optional inline content blocks. @defaultValue `undefined` */\n content?: string[];\n /** Whether this is a global or project-level file. @defaultValue `\"project\"` */\n scope?: 'project' | 'global';\n}\n\n/**\n * Result of ensuring a provider instruction file.\n *\n * @public\n */\nexport interface EnsureProviderInstructionFileResult {\n /** Absolute path to the instruction file. */\n filePath: string;\n /** Instruction file name from the provider registry. */\n instructFile: string;\n /** Action taken. */\n action: CaampInjectionAction;\n /** Provider ID. */\n providerId: string;\n}\n\n/**\n * Ensure a provider's instruction file exists with the correct CAAMP block.\n *\n * This is the canonical API for adapters and external packages to manage\n * provider instruction files. Instead of directly creating/modifying\n * CLAUDE.md, GEMINI.md, etc., callers should use this function to\n * delegate instruction file management to CAAMP.\n *\n * The instruction file name is resolved from CAAMP's provider registry\n * (single source of truth), not hardcoded by the caller.\n *\n * @remarks\n * The instruction file name is resolved from CAAMP's provider registry\n * (single source of truth), not hardcoded by the caller.\n *\n * @param providerId - Provider ID from the registry (e.g. `\"claude-code\"`, `\"gemini-cli\"`)\n * @param projectDir - Absolute path to the project directory\n * @param options - References, content, and scope configuration\n * @returns Result with file path, action taken, and provider metadata\n * @throws Error if the provider ID is not found in the registry\n *\n * @example\n * ```typescript\n * const result = await ensureProviderInstructionFile(\"claude-code\", \"/project\", {\n * references: [\"\\@AGENTS.md\"],\n * });\n * ```\n *\n * @public\n */\nexport async function ensureProviderInstructionFile(\n providerId: string,\n projectDir: string,\n options: EnsureProviderInstructionFileOptions,\n): Promise<EnsureProviderInstructionFileResult> {\n const provider = getProvider(providerId);\n if (!provider) {\n throw new Error(`Unknown provider: \"${providerId}\". Check CAAMP provider registry.`);\n }\n\n const scope = options.scope ?? 'project';\n const filePath =\n scope === 'global'\n ? join(provider.pathGlobal, provider.instructFile)\n : join(projectDir, provider.instructFile);\n\n // Fall back to the registry default when the caller omits references.\n const references = options.references ?? getProviderInstructionReferences(providerId);\n\n const template: InjectionTemplate = {\n references,\n content: options.content,\n };\n\n const injectionContent = buildInjectionContent(template);\n const action = await inject(filePath, injectionContent);\n\n return {\n filePath,\n instructFile: provider.instructFile,\n action,\n providerId: provider.id,\n };\n}\n\n/**\n * Ensure instruction files for multiple providers at once.\n *\n * Deduplicates by file path — providers sharing the same instruction file\n * (e.g. many providers use AGENTS.md) are only written once.\n *\n * @remarks\n * Providers sharing the same instruction file (e.g. many use `AGENTS.md`)\n * are only written once, avoiding duplicate blocks.\n *\n * @param providerIds - Array of provider IDs from the registry\n * @param projectDir - Absolute path to the project directory\n * @param options - References, content, and scope configuration\n * @returns Array of results, one per unique instruction file\n * @throws Error if any provider ID is not found in the registry\n *\n * @example\n * ```typescript\n * const results = await ensureAllProviderInstructionFiles(\n * [\"claude-code\", \"cursor\", \"gemini-cli\"],\n * \"/project\",\n * { references: [\"\\@AGENTS.md\"] },\n * );\n * ```\n *\n * @public\n */\nexport async function ensureAllProviderInstructionFiles(\n providerIds: string[],\n projectDir: string,\n options: EnsureProviderInstructionFileOptions,\n): Promise<EnsureProviderInstructionFileResult[]> {\n const results: EnsureProviderInstructionFileResult[] = [];\n const processed = new Set<string>();\n\n for (const providerId of providerIds) {\n const provider = getProvider(providerId);\n if (!provider) {\n throw new Error(`Unknown provider: \"${providerId}\". Check CAAMP provider registry.`);\n }\n\n const scope = options.scope ?? 'project';\n const filePath =\n scope === 'global'\n ? join(provider.pathGlobal, provider.instructFile)\n : join(projectDir, provider.instructFile);\n\n // Skip duplicates (multiple providers may share the same instruction file)\n if (processed.has(filePath)) continue;\n processed.add(filePath);\n\n // Fall back to the registry default when the caller omits references.\n const references = options.references ?? getProviderInstructionReferences(providerId);\n\n const template: InjectionTemplate = {\n references,\n content: options.content,\n };\n\n const injectionContent = buildInjectionContent(template);\n const action = await inject(filePath, injectionContent);\n\n results.push({\n filePath,\n instructFile: provider.instructFile,\n action,\n providerId: provider.id,\n });\n }\n\n return results;\n}\n\n// ── Per-Provider Agent Folder API ─────────────────────────────────\n\n/**\n * Known provider IDs that have a defined agent folder path.\n *\n * @public\n */\nexport type KnownProviderAgentFolderId =\n | 'claude-code'\n | 'claude-sdk'\n | 'opencode'\n | 'codex'\n | 'cursor'\n | 'pi'\n | 'kimi'\n | 'gemini-cli'\n | 'openai-sdk';\n\n/**\n * Resolve the native agent-definition folder path for a given provider.\n *\n * Each AI provider reads agent-definition files (e.g. `cleo-subagent.md`,\n * seed agent profiles) from its own platform-specific directory. This\n * function returns the correct path per provider so the CAAMP injector can\n * write agent files to the right location for every enabled provider.\n *\n * Follows XDG conventions (`~/.config/<provider>/agents/`) for providers\n * that do not have a pre-existing dotfolder convention. Claude Code and\n * Claude SDK both share `~/.claude/agents/` to match the Claude Code\n * native agent-loading path.\n *\n * Returns `null` for unknown provider IDs so callers can handle the gap\n * without throwing.\n *\n * @param providerId - Provider ID from the CAAMP registry (e.g. `\"claude-code\"`, `\"opencode\"`)\n * @returns Absolute path to the provider's agent folder, or `null` if the provider is unknown\n *\n * @example\n * ```typescript\n * const folder = getProviderAgentFolder(\"claude-code\");\n * // => \"/home/user/.claude/agents\"\n *\n * const folder2 = getProviderAgentFolder(\"opencode\");\n * // => \"/home/user/.config/opencode/agents\"\n *\n * const folder3 = getProviderAgentFolder(\"unknown-provider\");\n * // => null\n * ```\n *\n * @public\n */\nexport function getProviderAgentFolder(providerId: string): string | null {\n const home = homedir();\n\n switch (providerId as KnownProviderAgentFolderId) {\n case 'claude-code':\n case 'claude-sdk':\n return join(home, '.claude', 'agents');\n case 'opencode':\n return join(home, '.config', 'opencode', 'agents');\n case 'codex':\n return join(home, '.config', 'codex', 'agents');\n case 'cursor':\n return join(home, '.cursor', 'agents');\n case 'pi':\n return join(home, '.config', 'pi', 'agents');\n case 'kimi':\n return join(home, '.config', 'kimi', 'agents');\n case 'gemini-cli':\n return join(home, '.config', 'gemini', 'agents');\n case 'openai-sdk':\n return join(home, '.config', 'openai', 'agents');\n default:\n return null;\n }\n}\n\n/**\n * Result of writing an agent-definition file to a single provider's agent folder.\n *\n * @public\n */\nexport interface WriteAgentFileResult {\n /** Provider ID the file was written for. */\n providerId: string;\n /** Absolute path to the written agent-definition file. */\n filePath: string;\n /** Action taken. */\n action: CaampInjectionAction;\n}\n\n/**\n * Options for writing agent-definition files to provider agent folders.\n *\n * @public\n */\nexport interface WriteAgentFileOptions {\n /**\n * File name for the agent-definition file (e.g. `\"cleo-subagent.md\"`).\n * This name is used as-is inside each provider's agent folder.\n */\n fileName: string;\n /** Content to inject between CAAMP markers in the agent-definition file. */\n content: string;\n /**\n * If `true`, skip writing to providers whose agent folder does not yet exist.\n * If `false` (default), the folder is created automatically.\n *\n * @defaultValue false\n */\n skipMissingFolders?: boolean;\n}\n\n/**\n * Write an agent-definition file to every enabled provider's native agent folder.\n *\n * For each provider ID supplied, the file is written to the provider's native\n * agent-definition directory (resolved via {@link getProviderAgentFolder}).\n * Writing is idempotent — if the file already exists with matching content the\n * action is `\"intact\"` and the file is not modified. This ensures that existing\n * `~/.claude/agents/cleo-subagent.md` installs from prior versions are preserved\n * without clobbering.\n *\n * Providers whose folder cannot be resolved (unknown provider IDs) are silently\n * skipped. Providers whose folder does not yet exist on disk are created\n * automatically unless `skipMissingFolders` is set to `true`.\n *\n * @param providerIds - Array of provider IDs to write agent files for\n * @param options - File name, content, and folder-creation behaviour\n * @returns Array of write results, one per provider that was successfully processed\n *\n * @example\n * ```typescript\n * const results = await writeAgentFileToAllProviders(\n * [\"claude-code\", \"opencode\", \"cursor\"],\n * {\n * fileName: \"cleo-subagent.md\",\n * content: \"## CLEO Subagent\\nYou are a CLEO subagent...\",\n * },\n * );\n * for (const r of results) {\n * console.log(`${r.providerId}: ${r.action} → ${r.filePath}`);\n * }\n * ```\n *\n * @public\n */\nexport async function writeAgentFileToAllProviders(\n providerIds: string[],\n options: WriteAgentFileOptions,\n): Promise<WriteAgentFileResult[]> {\n const results: WriteAgentFileResult[] = [];\n const processed = new Set<string>();\n\n for (const providerId of providerIds) {\n const folder = getProviderAgentFolder(providerId);\n if (folder === null) {\n // Unknown provider — skip silently; caller can detect by comparing\n // providerIds.length to results.length.\n continue;\n }\n\n const filePath = join(folder, options.fileName);\n\n // Deduplicate by resolved file path — claude-code and claude-sdk share\n // the same folder so we only write once.\n if (processed.has(filePath)) {\n // Still push a result so the caller sees all providers reflected.\n const existingResult = results.find((r) => r.filePath === filePath);\n if (existingResult) {\n results.push({ providerId, filePath, action: existingResult.action });\n }\n continue;\n }\n processed.add(filePath);\n\n if (options.skipMissingFolders === true && !existsSync(folder)) {\n // Folder does not exist and caller requested we skip rather than create.\n continue;\n }\n\n const action = await inject(filePath, options.content);\n results.push({ providerId, filePath, action });\n }\n\n return results;\n}\n","/**\n * Atomic file writes and cross-process file locking.\n *\n * CAAMP mutates files that are shared by *every* project on the machine — most\n * critically `~/.agents/AGENTS.md`, which every `cleo init`, `cleo upgrade` and\n * `cleo doctor` run rewrites regardless of which project it was invoked from.\n * A plain `writeFile` on such a path is `open(O_TRUNC)` followed by one or more\n * `write(2)` calls: two processes interleaving there can leave a caller reading\n * a half-written file, and a reader racing a writer can observe a truncated\n * one.\n *\n * Two primitives remove that class of failure:\n *\n * - `writeFileAtomic` — the canonical tmp-then-rename primitive from\n * `@cleocode/core/tools/fs.js`. `rename(2)` within a filesystem is atomic, so\n * a concurrent reader sees either the whole old file or the whole new one,\n * never a mixture. It is re-exported here for callers already importing this\n * module; the definition lives in core, per the tools-vs-skills boundary.\n * - {@link withFileLock} — serialise a whole read-modify-write cycle across\n * processes via an `O_EXCL` guard file, so two writers cannot both read the\n * pre-state and then clobber each other's result. Generalised from the\n * bespoke copy that lived inline in `lock-utils.ts` (now a caller).\n *\n * @task T12051\n */\n\nimport { mkdir, open, readFile, rm, stat, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\n/**\n * A guard file older than this is assumed to belong to a crashed process.\n *\n * Generous on purpose. The critical sections here are a read, a string\n * transform and an atomic write — milliseconds. Reclaiming after a short\n * interval does not speed anything up, it just makes it likelier that a\n * *live but descheduled* holder gets its guard stolen, which is a correctness\n * failure rather than a performance one.\n */\nconst DEFAULT_STALE_LOCK_MS = 30_000;\n\n/**\n * How many times {@link withFileLock} retries before giving up.\n *\n * 400 × 25 ms ≈ 10 s. The previous 40 × 25 ms ≈ 1 s budget expired under\n * exactly the multi-session contention the lock exists to handle.\n */\nconst DEFAULT_LOCK_RETRIES = 400;\n\n/** Delay between lock acquisition attempts, in milliseconds. */\nconst DEFAULT_LOCK_DELAY_MS = 25;\n\n/** Resolve after `ms` milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Options controlling {@link withFileLock}.\n *\n * @public\n */\nexport interface FileLockOptions {\n /**\n * Number of acquisition attempts before throwing.\n *\n * @defaultValue 400\n */\n retries?: number;\n /**\n * Delay between attempts, in milliseconds.\n *\n * @defaultValue 25\n */\n delayMs?: number;\n /**\n * Age at which an existing guard file is treated as abandoned and removed.\n *\n * @defaultValue 30000\n */\n staleMs?: number;\n}\n\n/**\n * Remove a guard file that is older than `staleMs`.\n *\n * @param guardPath - Path of the guard file\n * @param staleMs - Age beyond which the guard is considered abandoned\n * @param expectedToken - Token observed in the guard before waiting. The guard\n * is only reclaimed if it still carries this token, so a guard that was\n * released and re-acquired by someone else in the meantime is never removed.\n * @returns `true` if a stale guard was removed\n */\nasync function removeStaleGuard(\n guardPath: string,\n staleMs: number,\n expectedToken: string | null,\n): Promise<boolean> {\n try {\n const info = await stat(guardPath);\n if (Date.now() - info.mtimeMs <= staleMs) return false;\n\n // Re-read: only reclaim the *same* guard we decided was stale.\n const current = await readFile(guardPath, 'utf-8').catch(() => null);\n if (expectedToken !== null && current !== null && current !== expectedToken) return false;\n\n await rm(guardPath, { force: true });\n return true;\n } catch {\n // Missing or unreadable — nothing to reclaim.\n }\n return false;\n}\n\n/**\n * Run `fn` while holding an exclusive cross-process lock on `targetPath`.\n *\n * The lock is a `<targetPath>.lock` guard file created with `O_EXCL`, which is\n * atomic on POSIX and on Windows. A guard left behind by a crashed process is\n * reclaimed once it exceeds `staleMs`.\n *\n * The guard is always released, including when `fn` throws.\n *\n * @param targetPath - Path being protected (the guard is a sibling of it)\n * @param fn - Work to perform while holding the lock\n * @param options - Retry, delay and staleness tuning\n * @returns Whatever `fn` returns\n * @throws Error if the lock cannot be acquired within `retries` attempts\n *\n * @example\n * ```typescript\n * const action = await withFileLock(agentsMd, async () => {\n * const before = await readFile(agentsMd, \"utf-8\");\n * await writeFileAtomic(agentsMd, transform(before));\n * return \"updated\";\n * });\n * ```\n *\n * @public\n */\nexport async function withFileLock<T>(\n targetPath: string,\n fn: () => Promise<T>,\n options: FileLockOptions = {},\n): Promise<T> {\n const retries = options.retries ?? DEFAULT_LOCK_RETRIES;\n const delayMs = options.delayMs ?? DEFAULT_LOCK_DELAY_MS;\n const staleMs = options.staleMs ?? DEFAULT_STALE_LOCK_MS;\n const guardPath = `${targetPath}.lock`;\n\n // Fencing token. Written into the guard so release can verify the guard it\n // is about to remove is still OURS. Without this, a holder whose guard was\n // reclaimed as stale would delete the *next* holder's guard on the way out,\n // letting two callers run their critical sections concurrently.\n const token = `${process.pid}:${Date.now()}:${Math.random().toString(36).slice(2, 12)}`;\n\n await mkdir(dirname(targetPath), { recursive: true });\n\n let acquired = false;\n for (let attempt = 0; attempt < retries && !acquired; attempt += 1) {\n try {\n // O_EXCL creation is what establishes exclusivity; the token is written\n // afterwards purely so release can prove the guard is still ours. We are\n // already the sole owner at this point, so the two-step is safe.\n const handle = await open(guardPath, 'wx');\n await handle.close();\n await writeFile(guardPath, token, 'utf-8');\n acquired = true;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== 'EEXIST') throw error;\n\n // A guard may be orphaned by a crashed process. Snapshot whose it is,\n // then only reclaim it if the very same one is still there and stale.\n const observed = await readFile(guardPath, 'utf-8').catch(() => null);\n if (await removeStaleGuard(guardPath, staleMs, observed)) continue;\n await sleep(delayMs);\n }\n }\n\n if (!acquired) {\n throw new Error(\n `Timed out acquiring lock for ${targetPath} after ${retries} attempts ` +\n `(~${Math.round((retries * delayMs) / 1000)}s)`,\n );\n }\n\n try {\n return await fn();\n } finally {\n // Only release a guard that is still ours. If it was reclaimed as stale\n // and re-acquired by another caller, removing it here would revoke THEIR\n // lock.\n const current = await readFile(guardPath, 'utf-8').catch(() => null);\n if (current === null || current === token) {\n await rm(guardPath, { force: true }).catch(() => {\n // Best-effort release — a stale guard is reclaimed by the next caller.\n });\n }\n }\n}\n\n/**\n * Throw if a read looks like it landed inside another process's\n * truncate-then-write window.\n *\n * `writeFileAtomic` makes *our* writes indivisible, but callers outside this\n * package still rewrite instruction files with a plain `writeFile`, which is\n * `open(O_TRUNC)` followed by `write(2)`. A read landing between those two\n * returns an empty string for a file that is not empty on disk. Reconciling\n * from that observation would replace every byte of the user's content with a\n * lone CAAMP block.\n *\n * Failing closed is the right trade: the caller retries or reports, and the\n * file is left exactly as it was.\n *\n * @param filePath - Path that was read, for the error message\n * @param content - What the read returned\n * @param sizeOnDisk - `stat().size` for the same path\n * @throws Error when `content` is empty but `sizeOnDisk` is greater than zero\n *\n * @example\n * ```typescript\n * const text = await readFile(p, \"utf-8\");\n * if (text.length === 0) assertNotTornRead(p, text, (await stat(p)).size);\n * ```\n *\n * @public\n */\nexport function assertNotTornRead(filePath: string, content: string, sizeOnDisk: number): void {\n if (content.length === 0 && sizeOnDisk > 0) {\n throw new Error(\n `Refusing to rewrite ${filePath}: read 0 bytes but the file is ${sizeOnDisk} bytes on ` +\n 'disk (torn read from a concurrent non-atomic writer).',\n );\n }\n}\n","/**\n * Provider registry loader\n *\n * Loads providers from providers/registry.json and resolves\n * platform-specific paths at runtime.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type {\n DetectionMethod,\n Provider,\n ProviderCapabilities,\n ProviderHarnessCapability,\n ProviderHooksCapability,\n ProviderMcpCapability,\n ProviderSkillsCapability,\n ProviderSpawnCapability,\n} from '../../types.js';\nimport {\n type PathScope,\n resolveProviderSkillsDir,\n resolveProvidersRegistryPath,\n resolveRegistryTemplatePath,\n} from '../paths/standard.js';\nimport type {\n HookEvent,\n ProviderPriority,\n ProviderRegistry,\n ProviderStatus,\n RegistryCapabilities,\n RegistryHarnessCapability,\n RegistryHooksCapability,\n RegistryMcpIntegration,\n RegistryProvider,\n RegistrySpawnCapability,\n SkillsPrecedence,\n} from './types.js';\n\n// ── Capability Defaults ──────────────────────────────────────────────\n\nconst DEFAULT_SKILLS_CAPABILITY: ProviderSkillsCapability = {\n agentsGlobalPath: null,\n agentsProjectPath: null,\n precedence: 'vendor-only',\n};\n\nconst DEFAULT_HOOKS_CAPABILITY: ProviderHooksCapability = {\n supported: [],\n hookConfigPath: null,\n hookConfigPathProject: null,\n hookFormat: null,\n nativeEventCatalog: 'canonical',\n canInjectSystemPrompt: false,\n canBlockTools: false,\n};\n\nconst DEFAULT_SPAWN_CAPABILITY: ProviderSpawnCapability = {\n supportsSubagents: false,\n supportsProgrammaticSpawn: false,\n supportsInterAgentComms: false,\n supportsParallelSpawn: false,\n spawnMechanism: null,\n spawnCommand: null,\n};\n\nfunction resolveMcpCapability(raw: RegistryMcpIntegration): ProviderMcpCapability {\n return {\n configKey: raw.configKey,\n configFormat: raw.configFormat,\n configPathGlobal: resolveRegistryTemplatePath(raw.configPathGlobal),\n configPathProject: raw.configPathProject,\n supportedTransports: [...raw.supportedTransports],\n supportsHeaders: raw.supportsHeaders,\n };\n}\n\nfunction resolveHarnessCapability(raw: RegistryHarnessCapability): ProviderHarnessCapability {\n return {\n kind: raw.kind,\n spawnTargets: [...raw.spawnTargets],\n supportsConductorLoop: raw.supportsConductorLoop,\n supportsStageGuidance: raw.supportsStageGuidance,\n supportsCantBridge: raw.supportsCantBridge,\n extensionsPath: resolveRegistryTemplatePath(raw.extensionsPath),\n globalExtensionsHub: raw.globalExtensionsHub\n ? resolveRegistryTemplatePath(raw.globalExtensionsHub)\n : null,\n };\n}\n\nfunction resolveHooksCapability(raw: RegistryHooksCapability): ProviderHooksCapability {\n return {\n supported: [...raw.supported],\n hookConfigPath: raw.hookConfigPath ? resolveRegistryTemplatePath(raw.hookConfigPath) : null,\n hookConfigPathProject: raw.hookConfigPathProject ?? null,\n hookFormat: raw.hookFormat,\n nativeEventCatalog: raw.nativeEventCatalog ?? 'canonical',\n canInjectSystemPrompt: raw.canInjectSystemPrompt ?? false,\n canBlockTools: raw.canBlockTools ?? false,\n };\n}\n\nfunction resolveSpawnCapability(raw: RegistrySpawnCapability): ProviderSpawnCapability {\n return {\n supportsSubagents: raw.supportsSubagents,\n supportsProgrammaticSpawn: raw.supportsProgrammaticSpawn,\n supportsInterAgentComms: raw.supportsInterAgentComms,\n supportsParallelSpawn: raw.supportsParallelSpawn,\n spawnMechanism: raw.spawnMechanism,\n spawnCommand: raw.spawnCommand ? [...raw.spawnCommand] : null,\n };\n}\n\nfunction resolveCapabilities(raw?: RegistryCapabilities): ProviderCapabilities {\n const skills: ProviderSkillsCapability = raw?.skills\n ? {\n agentsGlobalPath: raw.skills.agentsGlobalPath\n ? resolveRegistryTemplatePath(raw.skills.agentsGlobalPath)\n : null,\n agentsProjectPath: raw.skills.agentsProjectPath,\n precedence: raw.skills.precedence,\n }\n : { ...DEFAULT_SKILLS_CAPABILITY };\n\n const hooks: ProviderHooksCapability = raw?.hooks\n ? resolveHooksCapability(raw.hooks)\n : { ...DEFAULT_HOOKS_CAPABILITY, supported: [] };\n\n const spawn: ProviderSpawnCapability = raw?.spawn\n ? resolveSpawnCapability(raw.spawn)\n : { ...DEFAULT_SPAWN_CAPABILITY };\n\n const mcp: ProviderMcpCapability | null = raw?.mcp ? resolveMcpCapability(raw.mcp) : null;\n\n const harness: ProviderHarnessCapability | null = raw?.harness\n ? resolveHarnessCapability(raw.harness)\n : null;\n\n return { mcp, harness, skills, hooks, spawn };\n}\n\nfunction findRegistryPath(): string {\n const thisDir = dirname(fileURLToPath(import.meta.url));\n return resolveProvidersRegistryPath(thisDir);\n}\n\nlet _registry: ProviderRegistry | null = null;\nlet _providers: Map<string, Provider> | null = null;\nlet _aliasMap: Map<string, string> | null = null;\n\nfunction resolveProvider(raw: RegistryProvider): Provider {\n return {\n id: raw.id,\n toolName: raw.toolName,\n vendor: raw.vendor,\n agentFlag: raw.agentFlag,\n aliases: raw.aliases,\n pathGlobal: resolveRegistryTemplatePath(raw.pathGlobal),\n pathProject: raw.pathProject,\n instructFile: raw.instructFile,\n instructionReferences: raw.instructionReferences ? [...raw.instructionReferences] : [],\n pathSkills: resolveRegistryTemplatePath(raw.pathSkills),\n pathProjectSkills: raw.pathProjectSkills,\n detection: {\n methods: raw.detection.methods as DetectionMethod[],\n binary: raw.detection.binary,\n directories: raw.detection.directories?.map(resolveRegistryTemplatePath),\n appBundle: raw.detection.appBundle,\n flatpakId: raw.detection.flatpakId,\n },\n priority: raw.priority,\n status: raw.status,\n agentSkillsCompatible: raw.agentSkillsCompatible,\n capabilities: resolveCapabilities(raw.capabilities),\n };\n}\n\nfunction loadRegistry(): ProviderRegistry {\n if (_registry) return _registry;\n\n const registryPath = findRegistryPath();\n const raw = readFileSync(registryPath, 'utf-8');\n _registry = JSON.parse(raw) as ProviderRegistry;\n return _registry;\n}\n\nfunction ensureProviders(): void {\n if (_providers) return;\n\n const registry = loadRegistry();\n _providers = new Map<string, Provider>();\n _aliasMap = new Map<string, string>();\n\n for (const [id, raw] of Object.entries(registry.providers)) {\n const provider = resolveProvider(raw);\n _providers.set(id, provider);\n\n // Build alias map\n for (const alias of provider.aliases) {\n _aliasMap.set(alias, id);\n }\n }\n}\n\n/**\n * Retrieve all registered providers with resolved platform paths.\n *\n * Providers are lazily loaded from `providers/registry.json` on first call\n * and cached for subsequent calls.\n *\n * @remarks\n * The registry is parsed once and cached in-module state. Platform-specific\n * template paths (e.g. `~/.config/...`) are resolved at load time via\n * {@link resolveRegistryTemplatePath}. Call {@link resetRegistry} to force\n * a reload.\n *\n * @returns Array of all provider definitions\n *\n * @example\n * ```typescript\n * const providers = getAllProviders();\n * console.log(`${providers.length} providers registered`);\n * ```\n *\n * @public\n */\nexport function getAllProviders(): Provider[] {\n ensureProviders();\n if (!_providers) return [];\n return Array.from(_providers.values());\n}\n\n/**\n * Look up a provider by its ID or any of its aliases.\n *\n * @remarks\n * Alias resolution is performed via an internal map built during registry loading.\n * If the input matches an alias, it is resolved to the canonical provider ID before\n * lookup. If it matches neither an alias nor a canonical ID, `undefined` is returned.\n *\n * @param idOrAlias - Provider ID (e.g. `\"claude-code\"`) or alias (e.g. `\"claude\"`)\n * @returns The matching provider, or `undefined` if not found\n *\n * @example\n * ```typescript\n * const provider = getProvider(\"claude\");\n * // Returns the claude-code provider via alias resolution\n * ```\n *\n * @public\n */\nexport function getProvider(idOrAlias: string): Provider | undefined {\n ensureProviders();\n const resolved = _aliasMap?.get(idOrAlias) ?? idOrAlias;\n return _providers?.get(resolved);\n}\n\n/**\n * Resolve an alias to its canonical provider ID.\n *\n * If the input is already a canonical ID (or unrecognized), it is returned as-is.\n *\n * @remarks\n * Alias mappings are built from the `aliases` array in each provider's registry\n * entry. This function is safe to call with canonical IDs -- they pass through unchanged.\n *\n * @param idOrAlias - Provider ID or alias to resolve\n * @returns The canonical provider ID\n *\n * @example\n * ```typescript\n * resolveAlias(\"claude\"); // \"claude-code\"\n * resolveAlias(\"claude-code\"); // \"claude-code\"\n * resolveAlias(\"unknown\"); // \"unknown\"\n * ```\n *\n * @public\n */\nexport function resolveAlias(idOrAlias: string): string {\n ensureProviders();\n return _aliasMap?.get(idOrAlias) ?? idOrAlias;\n}\n\n/**\n * Filter providers by their priority tier.\n *\n * @remarks\n * Provider priority is assigned in `providers/registry.json` and indicates the\n * relative importance of a provider for detection ordering and display.\n * Callers filtering by `\"primary\"` should expect zero or one result; the\n * registry loader does not enforce the single-primary invariant.\n *\n * @param priority - Priority level to filter by (`\"primary\"`, `\"high\"`, `\"medium\"`, or `\"low\"`)\n * @returns Array of providers matching the given priority\n *\n * @example\n * ```typescript\n * const highPriority = getProvidersByPriority(\"high\");\n * console.log(highPriority.map(p => p.toolName));\n * ```\n *\n * @public\n */\nexport function getProvidersByPriority(priority: ProviderPriority): Provider[] {\n return getAllProviders().filter((p) => p.priority === priority);\n}\n\n/**\n * Get the single primary harness provider, if any is registered.\n *\n * @remarks\n * Returns the provider with `priority === \"primary\"`. By convention a\n * registry defines at most one primary harness; this function returns\n * the first match if the invariant is violated and logs no warning. Use\n * {@link getProvidersByPriority} instead if you need to diagnose\n * duplicates.\n *\n * @returns The primary provider, or `undefined` if none is registered\n *\n * @example\n * ```typescript\n * const primary = getPrimaryProvider();\n * if (primary) {\n * console.log(`Primary harness: ${primary.toolName}`);\n * }\n * ```\n *\n * @public\n */\nexport function getPrimaryProvider(): Provider | undefined {\n return getAllProviders().find((p) => p.priority === 'primary');\n}\n\n/**\n * Filter providers by their lifecycle status.\n *\n * @remarks\n * Lifecycle status is maintained per-provider in the registry and reflects\n * the provider's stability and support level within CAAMP.\n *\n * @param status - Status to filter by (`\"active\"`, `\"beta\"`, `\"deprecated\"`, or `\"planned\"`)\n * @returns Array of providers matching the given status\n *\n * @example\n * ```typescript\n * const active = getProvidersByStatus(\"active\");\n * console.log(`${active.length} active providers`);\n * ```\n *\n * @public\n */\nexport function getProvidersByStatus(status: ProviderStatus): Provider[] {\n return getAllProviders().filter((p) => p.status === status);\n}\n\n/**\n * Filter providers that use a specific instruction file.\n *\n * Multiple providers often share the same instruction file (e.g. many use `\"AGENTS.md\"`).\n *\n * @remarks\n * CAAMP supports three instruction file types: `CLAUDE.md`, `AGENTS.md`, and `GEMINI.md`.\n * Most providers read from `AGENTS.md` as the universal standard, while a few\n * have vendor-specific files.\n *\n * @param file - Instruction file name (e.g. `\"CLAUDE.md\"`, `\"AGENTS.md\"`)\n * @returns Array of providers that use the given instruction file\n *\n * @example\n * ```typescript\n * const claudeProviders = getProvidersByInstructFile(\"CLAUDE.md\");\n * console.log(claudeProviders.map(p => p.id));\n * ```\n *\n * @public\n */\nexport function getProvidersByInstructFile(file: string): Provider[] {\n return getAllProviders().filter((p) => p.instructFile === file);\n}\n\n/**\n * Get the set of all unique instruction file names across all providers.\n *\n * @remarks\n * Iterates over all registered providers and collects the distinct\n * `instructFile` values. The result is deduplicated via a `Set`.\n *\n * @returns Array of unique instruction file names (e.g. `[\"CLAUDE.md\", \"AGENTS.md\", \"GEMINI.md\"]`)\n *\n * @example\n * ```typescript\n * const files = getInstructionFiles();\n * // [\"CLAUDE.md\", \"AGENTS.md\", \"GEMINI.md\"]\n * ```\n *\n * @public\n */\nexport function getInstructionFiles(): string[] {\n const files = new Set<string>();\n for (const p of getAllProviders()) {\n files.add(p.instructFile);\n }\n return Array.from(files);\n}\n\n/**\n * Get the total number of registered providers.\n *\n * @remarks\n * Triggers lazy loading of the registry if not already loaded.\n * The count reflects the number of entries in `providers/registry.json`.\n *\n * @returns Count of providers in the registry\n *\n * @example\n * ```typescript\n * console.log(`Registry has ${getProviderCount()} providers`);\n * ```\n *\n * @public\n */\nexport function getProviderCount(): number {\n ensureProviders();\n return _providers?.size ?? 0;\n}\n\n/**\n * Get the semantic version string of the provider registry.\n *\n * @remarks\n * The version is read from the top-level `version` field in `providers/registry.json`\n * and follows semver conventions. It is bumped when provider definitions change.\n *\n * @returns Version string from `providers/registry.json` (e.g. `\"2.0.0\"`)\n *\n * @example\n * ```typescript\n * console.log(`Registry version: ${getRegistryVersion()}`);\n * ```\n *\n * @public\n */\nexport function getRegistryVersion(): string {\n return loadRegistry().version;\n}\n\n/**\n * Filter providers that support a specific hook event.\n *\n * @remarks\n * Hook events are declared per-provider in the `capabilities.hooks.supported`\n * array within the registry. Only providers that explicitly list the event\n * are returned.\n *\n * @param event - Hook event to filter by (e.g. `\"onToolComplete\"`)\n * @returns Array of providers whose hooks capability includes the given event\n *\n * @example\n * ```typescript\n * const providers = getProvidersByHookEvent(\"onToolComplete\");\n * console.log(providers.map(p => p.id));\n * ```\n *\n * @public\n */\nexport function getProvidersByHookEvent(event: HookEvent): Provider[] {\n return getAllProviders().filter((p) => p.capabilities.hooks.supported.includes(event));\n}\n\n/**\n * Get hook events common to all specified providers.\n *\n * If providerIds is provided, returns the intersection of their supported events.\n * If providerIds is undefined or empty, uses all providers.\n *\n * @remarks\n * Computes the set intersection of `capabilities.hooks.supported` across the\n * target providers. Useful for determining which hook events can be reliably\n * used across a multi-agent installation.\n *\n * @param providerIds - Optional array of provider IDs to intersect\n * @returns Array of hook events supported by ALL specified providers\n *\n * @example\n * ```typescript\n * const common = getCommonHookEvents([\"claude-code\", \"gemini-cli\"]);\n * console.log(`${common.length} common hook events`);\n * ```\n *\n * @public\n */\nexport function getCommonHookEvents(providerIds?: string[]): HookEvent[] {\n const providers =\n providerIds && providerIds.length > 0\n ? providerIds.map((id) => getProvider(id)).filter((p): p is Provider => p !== undefined)\n : getAllProviders();\n\n if (providers.length === 0) return [];\n\n const first = providers[0]!.capabilities.hooks.supported as HookEvent[];\n return first.filter((event) =>\n providers.every((p) => p.capabilities.hooks.supported.includes(event)),\n );\n}\n\n/**\n * Check whether a provider supports a specific capability via dot-path query.\n *\n * The dot-path addresses a value inside `provider.capabilities`. For boolean\n * fields the provider \"supports\" the capability when the value is `true`.\n * For non-boolean fields the provider \"supports\" it when the value is neither\n * `null` nor `undefined` (and, for arrays, non-empty).\n *\n * @remarks\n * This function traverses the capabilities object using dot-delimited path\n * segments. It handles three value types: booleans (must be `true`), arrays\n * (must be non-empty), and all other values (must be non-null/undefined).\n * Invalid paths return `false`.\n *\n * @param provider - Provider to inspect\n * @param dotPath - Dot-delimited capability path (e.g. `\"spawn.supportsSubagents\"`, `\"hooks.supported\"`)\n * @returns `true` when the provider has the specified capability\n *\n * @example\n * ```typescript\n * const claude = getProvider(\"claude-code\");\n * providerSupports(claude!, \"spawn.supportsSubagents\"); // true\n * providerSupports(claude!, \"hooks.supported\"); // true (non-empty array)\n * ```\n *\n * @public\n */\nexport function providerSupports(provider: Provider, dotPath: string): boolean {\n const parts = dotPath.split('.');\n let current: unknown = provider.capabilities;\n for (const part of parts) {\n if (current == null || typeof current !== 'object') return false;\n current = (current as Record<string, unknown>)[part];\n }\n if (typeof current === 'boolean') return current;\n if (Array.isArray(current)) return current.length > 0;\n return current != null;\n}\n\n/**\n * Filter providers that support spawning subagents.\n *\n * @remarks\n * This is a convenience wrapper that checks the `capabilities.spawn.supportsSubagents`\n * boolean flag. For more granular spawn capability filtering, use\n * {@link getProvidersBySpawnCapability}.\n *\n * @returns Array of providers where `capabilities.spawn.supportsSubagents === true`\n *\n * @example\n * ```typescript\n * const spawnCapable = getSpawnCapableProviders();\n * console.log(spawnCapable.map(p => p.id));\n * ```\n *\n * @public\n */\nexport function getSpawnCapableProviders(): Provider[] {\n return getAllProviders().filter((p) => p.capabilities.spawn.supportsSubagents);\n}\n\n/**\n * Filter providers by a specific boolean spawn capability flag.\n *\n * @remarks\n * The spawn capability has four boolean flags that can be queried independently.\n * The `spawnMechanism` and `spawnCommand` fields are excluded from the flag\n * type since they are not boolean checks.\n *\n * @param flag - One of the four boolean flags on {@link ProviderSpawnCapability}\n * (`\"supportsSubagents\"`, `\"supportsProgrammaticSpawn\"`,\n * `\"supportsInterAgentComms\"`, `\"supportsParallelSpawn\"`)\n * @returns Array of providers where the specified flag is `true`\n *\n * @example\n * ```typescript\n * const parallel = getProvidersBySpawnCapability(\"supportsParallelSpawn\");\n * console.log(parallel.map(p => p.id));\n * ```\n *\n * @see {@link getSpawnCapableProviders}\n *\n * @public\n */\nexport function getProvidersBySpawnCapability(\n flag: keyof Omit<ProviderSpawnCapability, 'spawnMechanism' | 'spawnCommand'>,\n): Provider[] {\n return getAllProviders().filter((p) => p.capabilities.spawn[flag] === true);\n}\n\n/**\n * Reset cached registry data, forcing a reload on next access.\n *\n * @remarks\n * Clears the in-memory provider map, alias map, and raw registry cache.\n * Primarily used in test suites to ensure a clean state between test cases.\n *\n * @example\n * ```typescript\n * resetRegistry();\n * // Next call to getAllProviders() will re-read registry.json\n * ```\n *\n * @public\n */\nexport function resetRegistry(): void {\n _registry = null;\n _providers = null;\n _aliasMap = null;\n}\n\n/**\n * Get the default `@` instruction references for a provider from the registry.\n *\n * Returns the `instructionReferences` array declared in `providers/registry.json`\n * for the given provider ID or alias. These references are the canonical defaults\n * used by {@link ensureProviderInstructionFile} when no explicit `references`\n * argument is supplied by the caller.\n *\n * @remarks\n * The return value is a fresh copy of the registry array — mutating it has no\n * effect on the cached registry state. If the provider is not found or it has\n * no `instructionReferences` entry, an empty array is returned so callers\n * never receive `undefined`.\n *\n * @param idOrAlias - Provider ID (e.g. `\"claude-code\"`) or alias (e.g. `\"claude\"`)\n * @returns Array of `@`-prefixed instruction reference strings, or `[]` if none\n *\n * @example\n * ```typescript\n * const refs = getProviderInstructionReferences(\"claude-code\");\n * // [\"@~/.cleo/templates/CLEO-INJECTION.md\", \"@.cleo/memory-bridge.md\"]\n *\n * const unknown = getProviderInstructionReferences(\"no-such-provider\");\n * // []\n * ```\n *\n * @public\n */\nexport function getProviderInstructionReferences(idOrAlias: string): string[] {\n const provider = getProvider(idOrAlias);\n return provider?.instructionReferences ? [...provider.instructionReferences] : [];\n}\n\n// ── Skills Query Functions ──────────────────────────────────────────\n\n/**\n * Filter providers by their skills precedence value.\n *\n * @remarks\n * Skills precedence controls how a provider resolves skill files when both\n * vendor-specific and `.agents/` standard paths exist. Values include\n * `\"vendor-only\"`, `\"agents-canonical\"`, `\"agents-first\"`, `\"agents-supported\"`,\n * and `\"vendor-global-agents-project\"`.\n *\n * @param precedence - Skills precedence to filter by\n * @returns Array of providers matching the given precedence\n *\n * @example\n * ```typescript\n * const vendorOnly = getProvidersBySkillsPrecedence(\"vendor-only\");\n * console.log(vendorOnly.map(p => p.id));\n * ```\n *\n * @public\n */\nexport function getProvidersBySkillsPrecedence(precedence: SkillsPrecedence): Provider[] {\n return getAllProviders().filter((p) => p.capabilities.skills.precedence === precedence);\n}\n\n/**\n * Get the effective skills paths for a provider, ordered by precedence.\n *\n * @remarks\n * The returned array is ordered by precedence priority. For example, with\n * `\"agents-first\"` precedence the `.agents/` path appears before the vendor\n * path. The `source` field indicates whether the path comes from the vendor\n * directory or the `.agents/` standard directory.\n *\n * @param provider - Provider to resolve paths for\n * @param scope - Whether to resolve global or project paths\n * @param projectDir - Project directory for project-scope resolution\n * @returns Ordered array of paths with source and scope metadata\n *\n * @example\n * ```typescript\n * const provider = getProvider(\"claude-code\")!;\n * const paths = getEffectiveSkillsPaths(provider, \"global\");\n * for (const p of paths) {\n * console.log(`${p.source} (${p.scope}): ${p.path}`);\n * }\n * ```\n *\n * @public\n */\nexport function getEffectiveSkillsPaths(\n provider: Provider,\n scope: PathScope,\n projectDir?: string,\n): Array<{ path: string; source: string; scope: string }> {\n const vendorPath = resolveProviderSkillsDir(provider, scope, projectDir);\n const { precedence, agentsGlobalPath, agentsProjectPath } = provider.capabilities.skills;\n\n const resolveAgentsPath = (): string | null => {\n if (scope === 'global' && agentsGlobalPath) return agentsGlobalPath;\n if (scope === 'project' && agentsProjectPath && projectDir) {\n return join(projectDir, agentsProjectPath);\n }\n return null;\n };\n\n const agentsPath = resolveAgentsPath();\n const scopeLabel = scope === 'global' ? 'global' : 'project';\n\n switch (precedence) {\n case 'vendor-only':\n return [{ path: vendorPath, source: 'vendor', scope: scopeLabel }];\n case 'agents-canonical':\n return agentsPath ? [{ path: agentsPath, source: 'agents', scope: scopeLabel }] : [];\n case 'agents-first':\n return [\n ...(agentsPath ? [{ path: agentsPath, source: 'agents', scope: scopeLabel }] : []),\n { path: vendorPath, source: 'vendor', scope: scopeLabel },\n ];\n case 'agents-supported':\n return [\n { path: vendorPath, source: 'vendor', scope: scopeLabel },\n ...(agentsPath ? [{ path: agentsPath, source: 'agents', scope: scopeLabel }] : []),\n ];\n case 'vendor-global-agents-project':\n if (scope === 'global') {\n return [{ path: vendorPath, source: 'vendor', scope: 'global' }];\n }\n return [\n ...(agentsPath ? [{ path: agentsPath, source: 'agents', scope: 'project' }] : []),\n { path: vendorPath, source: 'vendor', scope: 'project' },\n ];\n default:\n return [{ path: vendorPath, source: 'vendor', scope: scopeLabel }];\n }\n}\n\n/**\n * Build a full skills map for all providers.\n *\n * @remarks\n * Produces a summary of each provider's skills configuration including\n * the precedence mode and resolved global/project paths. For `\"vendor-only\"`\n * providers the paths point to the vendor skills directory; for others they\n * point to the `.agents/` standard paths.\n *\n * @returns Array of skills map entries with provider ID, tool name, precedence, and paths\n *\n * @example\n * ```typescript\n * const skillsMap = buildSkillsMap();\n * for (const entry of skillsMap) {\n * console.log(`${entry.providerId}: ${entry.precedence}`);\n * }\n * ```\n *\n * @public\n */\nexport function buildSkillsMap(): Array<{\n providerId: string;\n toolName: string;\n precedence: SkillsPrecedence;\n paths: { global: string | null; project: string | null };\n}> {\n return getAllProviders().map((p) => {\n const { precedence, agentsGlobalPath, agentsProjectPath } = p.capabilities.skills;\n const isVendorOnly = precedence === 'vendor-only';\n return {\n providerId: p.id,\n toolName: p.toolName,\n precedence,\n paths: {\n global: isVendorOnly ? p.pathSkills : (agentsGlobalPath ?? null),\n project: isVendorOnly ? p.pathProjectSkills : (agentsProjectPath ?? null),\n },\n };\n });\n}\n\n/**\n * Get capabilities for a provider by ID or alias.\n *\n * @remarks\n * Shorthand for `getProvider(idOrAlias)?.capabilities`. Returns the full\n * capabilities object containing mcp, harness, skills, hooks, and spawn\n * sub-objects.\n *\n * @param idOrAlias - Provider ID or alias\n * @returns The provider's capabilities, or undefined if not found\n *\n * @example\n * ```typescript\n * const caps = getProviderCapabilities(\"claude-code\");\n * if (caps?.spawn.supportsSubagents) {\n * console.log(\"Supports subagent spawning\");\n * }\n * ```\n *\n * @public\n */\nexport function getProviderCapabilities(idOrAlias: string): ProviderCapabilities | undefined {\n return getProvider(idOrAlias)?.capabilities;\n}\n\n/**\n * Check if a provider supports a capability using ID/alias lookup.\n *\n * Convenience wrapper that resolves the provider first, then delegates\n * to the provider-level {@link providerSupports}.\n *\n * @remarks\n * Returns `false` both when the provider is not found and when the capability\n * is not supported. Use {@link getProvider} first if you need to distinguish\n * between these cases.\n *\n * @param idOrAlias - Provider ID or alias\n * @param capabilityPath - Dot-path into capabilities (e.g. \"spawn.supportsSubagents\")\n * @returns true if the provider supports the capability, false otherwise\n *\n * @example\n * ```typescript\n * if (providerSupportsById(\"claude-code\", \"spawn.supportsSubagents\")) {\n * console.log(\"Claude Code supports subagent spawning\");\n * }\n * ```\n *\n * @see {@link providerSupports}\n *\n * @public\n */\nexport function providerSupportsById(idOrAlias: string, capabilityPath: string): boolean {\n const provider = getProvider(idOrAlias);\n if (!provider) return false;\n return providerSupports(provider, capabilityPath);\n}\n","/**\n * CAAMP marker engine — parsing, damage repair, and canonical block rendering.\n *\n * The grammar itself lives in `@cleocode/contracts/caamp-markers` (const data\n * in the leaf package, because `@cleocode/core` and `@cleocode/caamp` depend on\n * each other and cannot share a module directly). This file is the only place\n * that turns that grammar into behaviour.\n *\n * ## Why damage repair exists\n *\n * A CAAMP block is delimited by two HTML comments. Losing a single character\n * from an opening marker — `<!-- CAAMP:START -->` becoming `!-- CAAMP:START -->`\n * — used to be unrecoverable *and* self-amplifying:\n *\n * 1. The strict pattern no longer matched the block.\n * 2. `inject()` concluded the file had no CAAMP block at all and **prepended a\n * fresh one**, rather than replacing the damaged one.\n * 3. The file now contained two blocks. The protocol text they reference was\n * loaded into every agent's context twice.\n * 4. `cleo doctor` reported \"markers unbalanced\" and prescribed `cleo upgrade`\n * — which ran `inject()` again and could only make it worse.\n *\n * That ratchet was observed in the wild on `~/.agents/AGENTS.md`, which had\n * accumulated three blocks from two separate single-byte losses.\n *\n * {@link normalizeMarkers} breaks the loop by healing near-miss markers back to\n * canonical form *before* any decision is made about the file, so a damaged\n * block is recognised and replaced instead of duplicated.\n *\n * @task T12051\n */\n\nimport {\n CAAMP_BLOCK_PATTERN_SOURCE,\n CAAMP_DAMAGED_END_PATTERN_SOURCE,\n CAAMP_DAMAGED_START_PATTERN_SOURCE,\n CAAMP_MARKER_END,\n CAAMP_MARKER_START,\n} from '@cleocode/contracts/caamp-markers';\n\n/**\n * A single parsed CAAMP block extracted from a file.\n *\n * @public\n */\nexport interface CaampBlock {\n /** Raw text of the entire block including markers. */\n raw: string;\n /** Trimmed content between the markers. */\n content: string;\n /** Zero-based character offset of the start of the block in the file. */\n startIndex: number;\n /** Zero-based character offset immediately after the block in the file. */\n endIndex: number;\n}\n\n/**\n * Result of healing damaged markers in a string.\n *\n * @public\n */\nexport interface NormalizeResult {\n /** Content with every recognised marker rewritten to canonical form. */\n content: string;\n /** How many marker lines were rewritten. `0` means the input was already canonical. */\n repaired: number;\n}\n\n/**\n * Build a fresh global pattern matching a complete canonical CAAMP block.\n *\n * A new `RegExp` is returned on every call deliberately. A shared module-level\n * `RegExp` carrying the `g` flag holds a mutable `lastIndex`, so reusing one\n * across `.test()` or `.exec()` calls silently skips matches — a defect that\n * previously existed in `removeInjection`.\n *\n * @returns A new `RegExp` with the `g` flag; capture group 1 is the block body\n *\n * @example\n * ```typescript\n * for (const m of content.matchAll(blockPattern())) {\n * console.log(m[1]);\n * }\n * ```\n *\n * @public\n */\nexport function blockPattern(): RegExp {\n return new RegExp(CAAMP_BLOCK_PATTERN_SOURCE, 'g');\n}\n\n/**\n * Rewrite every damaged CAAMP marker line back to its canonical form.\n *\n * Only whole lines are considered, so prose that merely mentions a marker is\n * left alone. Lines that are already canonical are matched but rewritten to an\n * identical string, and therefore are not counted as repairs.\n *\n * @param content - Raw file contents\n * @returns The healed content and the number of marker lines actually changed\n *\n * @example\n * ```typescript\n * const { content, repaired } = normalizeMarkers(await readFile(p, \"utf-8\"));\n * if (repaired > 0) console.log(`healed ${repaired} damaged marker(s)`);\n * ```\n *\n * @public\n */\nexport function normalizeMarkers(content: string): NormalizeResult {\n let repaired = 0;\n\n const heal = (input: string, source: string, canonical: string): string =>\n input.replace(new RegExp(source, 'gmi'), (match) => {\n if (match === canonical) return match;\n repaired += 1;\n return canonical;\n });\n\n let out = heal(content, CAAMP_DAMAGED_START_PATTERN_SOURCE, CAAMP_MARKER_START);\n out = heal(out, CAAMP_DAMAGED_END_PATTERN_SOURCE, CAAMP_MARKER_END);\n\n return { content: out, repaired };\n}\n\n/**\n * Parse every canonical CAAMP block out of a file's contents.\n *\n * Blocks are returned in order of appearance. An opening marker with no\n * matching closing marker is skipped rather than throwing, so a corrupted file\n * can still be inspected.\n *\n * Call {@link normalizeMarkers} first if the input may contain damaged markers\n * — this function is deliberately strict.\n *\n * @param fileContent - Raw text content of the file\n * @returns Array of parsed CAAMP blocks\n *\n * @example\n * ```typescript\n * const blocks = parseBlocks(await readFile(agentsMd, \"utf-8\"));\n * console.log(`${blocks.length} block(s)`);\n * ```\n *\n * @public\n */\nexport function parseBlocks(fileContent: string): CaampBlock[] {\n const blocks: CaampBlock[] = [];\n const pattern = blockPattern();\n\n for (let match = pattern.exec(fileContent); match !== null; match = pattern.exec(fileContent)) {\n const raw = match[0];\n blocks.push({\n raw,\n content: (match[1] ?? '').trim(),\n startIndex: match.index,\n endIndex: match.index + raw.length,\n });\n }\n\n return blocks;\n}\n\n/**\n * Wrap content in canonical CAAMP markers.\n *\n * @param content - Body of the block\n * @returns The full block, markers included\n *\n * @example\n * ```typescript\n * buildBlock(\"@AGENTS.md\");\n * // \"<!-- CAAMP:START -->\\n@AGENTS.md\\n<!-- CAAMP:END -->\"\n * ```\n *\n * @public\n */\nexport function buildBlock(content: string): string {\n return `${CAAMP_MARKER_START}\\n${content}\\n${CAAMP_MARKER_END}`;\n}\n\n/**\n * Tidy whitespace produced by removing blocks, and guarantee a trailing newline.\n *\n * @param content - Content to normalise\n * @returns Content with runs of blank lines collapsed and exactly one trailing newline\n */\nfunction tidy(content: string): string {\n const collapsed = content.replace(/\\n{3,}/g, '\\n\\n').trimEnd();\n return collapsed.length > 0 ? `${collapsed}\\n` : '';\n}\n\n/**\n * Where {@link reconcile} places the block when the file has none yet.\n *\n * Only applies to a file that has no CAAMP block at all — an existing block is\n * always replaced where it already is, never moved.\n *\n * @public\n */\nexport type BlockInsertPosition = 'prepend' | 'append';\n\n/**\n * Outcome of reconciling a file's contents against the desired CAAMP block.\n *\n * @public\n */\nexport interface ReconcileResult {\n /** The file contents that should be on disk. */\n content: string;\n /** Number of blocks found before reconciliation. */\n blocksBefore: number;\n /** Number of damaged marker lines healed. */\n repaired: number;\n}\n\n/**\n * Reconcile a file's contents so it contains exactly one canonical CAAMP block\n * carrying `desiredContent`.\n *\n * The rules, in order:\n *\n * 1. Damaged markers are healed first, so a corrupted block is recognised as a\n * block rather than treated as absent.\n * 2. If the file has no block, one is inserted — at the top by default, or at\n * the bottom when `insert` is `'append'` (which is what the Pi harness has\n * always done for its own `AGENTS.md`).\n * 3. If the file has one or more blocks, the **first** is replaced in place and\n * any others are removed. Replacing in place matters: prepending instead\n * would walk the block up the file on every run, and would separate it from\n * any heading a user wrote above it.\n * 4. All text outside CAAMP blocks is preserved. CAAMP owns the region between\n * its markers and nothing else in the file. The only change made outside\n * them is whitespace tidying — runs of three or more newlines collapse to\n * two, and the file ends with exactly one newline. No non-blank line is\n * ever removed, reordered or rewritten.\n *\n * This function is pure — it performs no I/O, which is what makes the\n * behaviour straightforward to test exhaustively.\n *\n * @param existing - Current file contents\n * @param desiredContent - Body the single surviving block should carry\n * @param insert - Placement when the file has no block yet\n * @returns The reconciled content plus what was found on the way\n *\n * @example\n * ```typescript\n * const { content, blocksBefore, repaired } = reconcile(onDisk, \"@AGENTS.md\");\n * if (content !== onDisk) await writeFileAtomic(path, content);\n * ```\n *\n * @public\n */\nexport function reconcile(\n existing: string,\n desiredContent: string,\n insert: BlockInsertPosition = 'prepend',\n): ReconcileResult {\n const { content: healed, repaired } = normalizeMarkers(existing);\n const blocks = parseBlocks(healed);\n // Trim the body so whitespace-only differences converge to one canonical\n // form instead of rewriting the file on every call.\n const desiredBlock = buildBlock(desiredContent.trim());\n\n if (blocks.length === 0) {\n const body = healed.trim();\n if (body.length === 0) return { content: tidy(desiredBlock), blocksBefore: 0, repaired };\n return {\n content:\n insert === 'append'\n ? tidy(`${body}\\n\\n${desiredBlock}`)\n : tidy(`${desiredBlock}\\n\\n${body}`),\n blocksBefore: 0,\n repaired,\n };\n }\n\n let out = '';\n let cursor = 0;\n\n for (const [index, block] of blocks.entries()) {\n out += healed.slice(cursor, block.startIndex);\n cursor = block.endIndex;\n // Keep the first block's position; every later block is dropped.\n if (index === 0) out += desiredBlock;\n }\n out += healed.slice(cursor);\n\n return { content: tidy(out), blocksBefore: blocks.length, repaired };\n}\n\n/**\n * Merge the bodies of several CAAMP blocks into one, preserving order and\n * dropping exact duplicate lines.\n *\n * Used by repair, which — unlike injection — does not know what the block\n * *should* contain. Keeping the union rather than picking a winner means no\n * reference is silently dropped when two blocks legitimately differ (a project\n * block carrying `@AGENTS.md` and a global one carrying\n * `@~/.agents/AGENTS.md`, for instance).\n *\n * @param blocks - Blocks to merge, in file order\n * @returns The merged body\n *\n * @example\n * ```typescript\n * mergeBlockBodies(parseBlocks(content));\n * // \"@AGENTS.md\\n@~/.agents/AGENTS.md\"\n * ```\n *\n * @public\n */\nexport function mergeBlockBodies(blocks: readonly CaampBlock[]): string {\n const seen = new Set<string>();\n const lines: string[] = [];\n\n for (const block of blocks) {\n for (const line of block.content.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed.length === 0 || seen.has(trimmed)) continue;\n seen.add(trimmed);\n lines.push(trimmed);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Restore a file to exactly one well-formed CAAMP block without needing to\n * know what that block should contain.\n *\n * This is what `cleo caamp repair` and `cleo doctor` use. Injection knows the\n * desired body and calls {@link reconcile}; repair does not, so it derives the\n * surviving body from what is already there via {@link mergeBlockBodies}.\n *\n * Deriving rather than deduplicating matters: the previous repair removed only\n * blocks with *identical* bodies, so a file with two blocks carrying different\n * references was reported as \"2 blocks (expected 1)\" by the health check and\n * then left untouched by the repair the health check prescribed — an\n * unfixable warning loop.\n *\n * @param existing - Current file contents\n * @returns The repaired content plus what was found on the way\n *\n * @example\n * ```typescript\n * const { content, blocksBefore, repaired } = repairContent(onDisk);\n * ```\n *\n * @public\n */\nexport function repairContent(existing: string): ReconcileResult {\n const { content: healed, repaired } = normalizeMarkers(existing);\n const blocks = parseBlocks(healed);\n\n if (blocks.length === 0) {\n return { content: healed, blocksBefore: 0, repaired };\n }\n\n const merged = reconcile(healed, mergeBlockBodies(blocks));\n return { content: merged.content, blocksBefore: blocks.length, repaired };\n}\n","/**\n * Instruction template management\n *\n * Generates injection content based on provider capabilities.\n * Includes structured InjectionTemplate API for project-level customization.\n */\n\nimport type { Provider } from '../../types.js';\n\n// ── InjectionTemplate API ───────────────────────────────────────────\n\n/**\n * Structured template for injection content.\n *\n * @remarks\n * Projects use this to define what goes between CAAMP markers in\n * instruction files, rather than passing ad-hoc strings.\n *\n * @public\n */\nexport interface InjectionTemplate {\n /** References to include (e.g. `\"\\@AGENTS.md\"`, `\"\\@.cleo/project-context.json\"`). */\n references: string[];\n /** Inline content blocks (raw markdown/text). @defaultValue `undefined` */\n content?: string[];\n}\n\n/**\n * Build injection content from a structured template.\n *\n * Produces a string suitable for injection between CAAMP markers.\n * References are output as `@` lines, content blocks are appended as-is.\n *\n * @param template - Template defining references and content\n * @returns Formatted injection content string\n *\n * @remarks\n * References are output one per line. Content blocks are appended after a\n * blank separator line when references are present.\n *\n * @example\n * ```typescript\n * const content = buildInjectionContent({\n * references: [\"\\@AGENTS.md\"],\n * });\n * ```\n *\n * @public\n */\nexport function buildInjectionContent(template: InjectionTemplate): string {\n const lines: string[] = [];\n\n for (const ref of template.references) {\n lines.push(ref);\n }\n\n if (template.content && template.content.length > 0) {\n if (lines.length > 0) {\n lines.push('');\n }\n lines.push(...template.content);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Parse injection content back into template form.\n *\n * Lines starting with `@` are treated as references.\n * All other non-empty lines are treated as content blocks.\n *\n * @param content - Raw injection content string\n * @returns Parsed InjectionTemplate\n *\n * @remarks\n * Inverse of {@link buildInjectionContent}. Empty lines are ignored.\n *\n * @example\n * ```typescript\n * const template = parseInjectionContent(\"\\@AGENTS.md\\n\\@.cleo/config.json\");\n * ```\n *\n * @public\n */\nexport function parseInjectionContent(content: string): InjectionTemplate {\n const references: string[] = [];\n const contentLines: string[] = [];\n\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n if (trimmed.startsWith('@')) {\n references.push(trimmed);\n } else {\n contentLines.push(line);\n }\n }\n\n return {\n references,\n content: contentLines.length > 0 ? contentLines : undefined,\n };\n}\n\n// ── Legacy API (preserved) ──────────────────────────────────────────\n\n/**\n * Generate a standard CAAMP injection block for instruction files.\n *\n * Produces markdown content suitable for injection between CAAMP markers.\n * Optionally includes MCP server and custom content sections.\n *\n * @remarks\n * This is the legacy API preserved for backward compatibility. New code\n * should prefer {@link buildInjectionContent} with an `InjectionTemplate`.\n *\n * @param options - Optional configuration for the generated content\n * @returns Generated markdown string\n *\n * @example\n * ```typescript\n * const content = generateInjectionContent({ mcpServerName: \"filesystem\" });\n * ```\n *\n * @public\n */\nexport function generateInjectionContent(options?: {\n mcpServerName?: string;\n customContent?: string;\n}): string {\n const lines: string[] = [];\n\n lines.push('## CAAMP Managed Configuration');\n lines.push('');\n lines.push('This section is managed by [CAAMP](https://github.com/caamp/caamp).');\n lines.push('Do not edit between the CAAMP markers manually.');\n\n if (options?.mcpServerName) {\n lines.push('');\n lines.push(`### MCP Server: ${options.mcpServerName}`);\n lines.push(`Configured via \\`caamp mcp install\\`.`);\n }\n\n if (options?.customContent) {\n lines.push('');\n lines.push(options.customContent);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generate a skills discovery section for instruction files.\n *\n * @remarks\n * Produces a markdown list of installed skill names. Returns an empty string\n * when no skills are provided.\n *\n * @param skillNames - Array of skill names to list\n * @returns Markdown string listing installed skills\n *\n * @example\n * ```typescript\n * const section = generateSkillsSection([\"code-review\", \"testing\"]);\n * ```\n *\n * @public\n */\nexport function generateSkillsSection(skillNames: string[]): string {\n if (skillNames.length === 0) return '';\n\n const lines: string[] = [];\n lines.push('### Installed Skills');\n lines.push('');\n\n for (const name of skillNames) {\n lines.push(`- \\`${name}\\` - Available via SKILL.md`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Get the correct instruction file name for a provider.\n *\n * @remarks\n * Simple accessor that returns the `instructFile` property from the provider\n * registry entry (e.g. `\"CLAUDE.md\"`, `\"AGENTS.md\"`, `\"GEMINI.md\"`).\n *\n * @param provider - Provider registry entry\n * @returns Instruction file name\n *\n * @example\n * ```typescript\n * const fileName = getInstructFile(provider);\n * // \"CLAUDE.md\"\n * ```\n *\n * @public\n */\nexport function getInstructFile(provider: Provider): string {\n return provider.instructFile;\n}\n\n/**\n * Group providers by their instruction file name.\n *\n * Useful for determining which providers share the same instruction file\n * (e.g. multiple providers using `AGENTS.md`).\n *\n * @param providers - Array of providers to group\n * @returns Map from instruction file name to array of providers using that file\n *\n * @remarks\n * Useful for determining which providers share the same instruction file\n * to avoid duplicate file operations.\n *\n * @example\n * ```typescript\n * const groups = groupByInstructFile(getAllProviders());\n * for (const [file, providers] of groups) {\n * console.log(`${file}: ${providers.map(p => p.id).join(\", \")}`);\n * }\n * ```\n *\n * @public\n */\nexport function groupByInstructFile(providers: Provider[]): Map<string, Provider[]> {\n const groups = new Map<string, Provider[]>();\n\n for (const provider of providers) {\n const existing = groups.get(provider.instructFile) ?? [];\n existing.push(provider);\n groups.set(provider.instructFile, existing);\n }\n\n return groups;\n}\n"],"mappings":";;;;;;;;AAQA,SAAS,kBAAkB;AAC3B,SAAS,YAAAA,WAAU,QAAAC,aAAY;AAC/B,SAAS,eAAe;AACxB,SAAS,QAAAC,aAAY;AAErB,SAAS,uBAAuB;;;ACahC,SAAS,OAAO,MAAM,UAAU,IAAI,MAAM,iBAAiB;AAC3D,SAAS,eAAe;AAWxB,IAAM,wBAAwB;AAQ9B,IAAM,uBAAuB;AAG7B,IAAM,wBAAwB;AAG9B,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAsCA,eAAe,iBACb,WACA,SACA,eACkB;AAClB,MAAI;AACF,UAAM,OAAO,MAAM,KAAK,SAAS;AACjC,QAAI,KAAK,IAAI,IAAI,KAAK,WAAW,QAAS,QAAO;AAGjD,UAAM,UAAU,MAAM,SAAS,WAAW,OAAO,EAAE,MAAM,MAAM,IAAI;AACnE,QAAI,kBAAkB,QAAQ,YAAY,QAAQ,YAAY,cAAe,QAAO;AAEpF,UAAM,GAAG,WAAW,EAAE,OAAO,KAAK,CAAC;AACnC,WAAO;AAAA,EACT,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AA4BA,eAAsB,aACpB,YACA,IACA,UAA2B,CAAC,GAChB;AACZ,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,YAAY,GAAG,UAAU;AAM/B,QAAM,QAAQ,GAAG,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AAErF,QAAM,MAAM,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAEpD,MAAI,WAAW;AACf,WAAS,UAAU,GAAG,UAAU,WAAW,CAAC,UAAU,WAAW,GAAG;AAClE,QAAI;AAIF,YAAM,SAAS,MAAM,KAAK,WAAW,IAAI;AACzC,YAAM,OAAO,MAAM;AACnB,YAAM,UAAU,WAAW,OAAO,OAAO;AACzC,iBAAW;AAAA,IACb,SAAS,OAAO;AACd,YAAM,OAAQ,MAAgC;AAC9C,UAAI,SAAS,SAAU,OAAM;AAI7B,YAAM,WAAW,MAAM,SAAS,WAAW,OAAO,EAAE,MAAM,MAAM,IAAI;AACpE,UAAI,MAAM,iBAAiB,WAAW,SAAS,QAAQ,EAAG;AAC1D,YAAM,MAAM,OAAO;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR,gCAAgC,UAAU,UAAU,OAAO,eACpD,KAAK,MAAO,UAAU,UAAW,GAAI,CAAC;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI;AACF,WAAO,MAAM,GAAG;AAAA,EAClB,UAAE;AAIA,UAAM,UAAU,MAAM,SAAS,WAAW,OAAO,EAAE,MAAM,MAAM,IAAI;AACnE,QAAI,YAAY,QAAQ,YAAY,OAAO;AACzC,YAAM,GAAG,WAAW,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,MAEjD,CAAC;AAAA,IACH;AAAA,EACF;AACF;AA6BO,SAAS,kBAAkB,UAAkB,SAAiB,YAA0B;AAC7F,MAAI,QAAQ,WAAW,KAAK,aAAa,GAAG;AAC1C,UAAM,IAAI;AAAA,MACR,uBAAuB,QAAQ,kCAAkC,UAAU;AAAA,IAE7E;AAAA,EACF;AACF;;;ACpOA,SAAS,oBAAoB;AAC7B,SAAS,WAAAC,UAAS,YAAY;AAC9B,SAAS,qBAAqB;AAiC9B,IAAM,4BAAsD;AAAA,EAC1D,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,YAAY;AACd;AAEA,IAAM,2BAAoD;AAAA,EACxD,WAAW,CAAC;AAAA,EACZ,gBAAgB;AAAA,EAChB,uBAAuB;AAAA,EACvB,YAAY;AAAA,EACZ,oBAAoB;AAAA,EACpB,uBAAuB;AAAA,EACvB,eAAe;AACjB;AAEA,IAAM,2BAAoD;AAAA,EACxD,mBAAmB;AAAA,EACnB,2BAA2B;AAAA,EAC3B,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,gBAAgB;AAAA,EAChB,cAAc;AAChB;AAEA,SAAS,qBAAqB,KAAoD;AAChF,SAAO;AAAA,IACL,WAAW,IAAI;AAAA,IACf,cAAc,IAAI;AAAA,IAClB,kBAAkB,4BAA4B,IAAI,gBAAgB;AAAA,IAClE,mBAAmB,IAAI;AAAA,IACvB,qBAAqB,CAAC,GAAG,IAAI,mBAAmB;AAAA,IAChD,iBAAiB,IAAI;AAAA,EACvB;AACF;AAEA,SAAS,yBAAyB,KAA2D;AAC3F,SAAO;AAAA,IACL,MAAM,IAAI;AAAA,IACV,cAAc,CAAC,GAAG,IAAI,YAAY;AAAA,IAClC,uBAAuB,IAAI;AAAA,IAC3B,uBAAuB,IAAI;AAAA,IAC3B,oBAAoB,IAAI;AAAA,IACxB,gBAAgB,4BAA4B,IAAI,cAAc;AAAA,IAC9D,qBAAqB,IAAI,sBACrB,4BAA4B,IAAI,mBAAmB,IACnD;AAAA,EACN;AACF;AAEA,SAAS,uBAAuB,KAAuD;AACrF,SAAO;AAAA,IACL,WAAW,CAAC,GAAG,IAAI,SAAS;AAAA,IAC5B,gBAAgB,IAAI,iBAAiB,4BAA4B,IAAI,cAAc,IAAI;AAAA,IACvF,uBAAuB,IAAI,yBAAyB;AAAA,IACpD,YAAY,IAAI;AAAA,IAChB,oBAAoB,IAAI,sBAAsB;AAAA,IAC9C,uBAAuB,IAAI,yBAAyB;AAAA,IACpD,eAAe,IAAI,iBAAiB;AAAA,EACtC;AACF;AAEA,SAAS,uBAAuB,KAAuD;AACrF,SAAO;AAAA,IACL,mBAAmB,IAAI;AAAA,IACvB,2BAA2B,IAAI;AAAA,IAC/B,yBAAyB,IAAI;AAAA,IAC7B,uBAAuB,IAAI;AAAA,IAC3B,gBAAgB,IAAI;AAAA,IACpB,cAAc,IAAI,eAAe,CAAC,GAAG,IAAI,YAAY,IAAI;AAAA,EAC3D;AACF;AAEA,SAAS,oBAAoB,KAAkD;AAC7E,QAAM,SAAmC,KAAK,SAC1C;AAAA,IACE,kBAAkB,IAAI,OAAO,mBACzB,4BAA4B,IAAI,OAAO,gBAAgB,IACvD;AAAA,IACJ,mBAAmB,IAAI,OAAO;AAAA,IAC9B,YAAY,IAAI,OAAO;AAAA,EACzB,IACA,EAAE,GAAG,0BAA0B;AAEnC,QAAM,QAAiC,KAAK,QACxC,uBAAuB,IAAI,KAAK,IAChC,EAAE,GAAG,0BAA0B,WAAW,CAAC,EAAE;AAEjD,QAAM,QAAiC,KAAK,QACxC,uBAAuB,IAAI,KAAK,IAChC,EAAE,GAAG,yBAAyB;AAElC,QAAM,MAAoC,KAAK,MAAM,qBAAqB,IAAI,GAAG,IAAI;AAErF,QAAM,UAA4C,KAAK,UACnD,yBAAyB,IAAI,OAAO,IACpC;AAEJ,SAAO,EAAE,KAAK,SAAS,QAAQ,OAAO,MAAM;AAC9C;AAEA,SAAS,mBAA2B;AAClC,QAAM,UAAUC,SAAQ,cAAc,YAAY,GAAG,CAAC;AACtD,SAAO,6BAA6B,OAAO;AAC7C;AAEA,IAAI,YAAqC;AACzC,IAAI,aAA2C;AAC/C,IAAI,YAAwC;AAE5C,SAAS,gBAAgB,KAAiC;AACxD,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,UAAU,IAAI;AAAA,IACd,QAAQ,IAAI;AAAA,IACZ,WAAW,IAAI;AAAA,IACf,SAAS,IAAI;AAAA,IACb,YAAY,4BAA4B,IAAI,UAAU;AAAA,IACtD,aAAa,IAAI;AAAA,IACjB,cAAc,IAAI;AAAA,IAClB,uBAAuB,IAAI,wBAAwB,CAAC,GAAG,IAAI,qBAAqB,IAAI,CAAC;AAAA,IACrF,YAAY,4BAA4B,IAAI,UAAU;AAAA,IACtD,mBAAmB,IAAI;AAAA,IACvB,WAAW;AAAA,MACT,SAAS,IAAI,UAAU;AAAA,MACvB,QAAQ,IAAI,UAAU;AAAA,MACtB,aAAa,IAAI,UAAU,aAAa,IAAI,2BAA2B;AAAA,MACvE,WAAW,IAAI,UAAU;AAAA,MACzB,WAAW,IAAI,UAAU;AAAA,IAC3B;AAAA,IACA,UAAU,IAAI;AAAA,IACd,QAAQ,IAAI;AAAA,IACZ,uBAAuB,IAAI;AAAA,IAC3B,cAAc,oBAAoB,IAAI,YAAY;AAAA,EACpD;AACF;AAEA,SAAS,eAAiC;AACxC,MAAI,UAAW,QAAO;AAEtB,QAAM,eAAe,iBAAiB;AACtC,QAAM,MAAM,aAAa,cAAc,OAAO;AAC9C,cAAY,KAAK,MAAM,GAAG;AAC1B,SAAO;AACT;AAEA,SAAS,kBAAwB;AAC/B,MAAI,WAAY;AAEhB,QAAM,WAAW,aAAa;AAC9B,eAAa,oBAAI,IAAsB;AACvC,cAAY,oBAAI,IAAoB;AAEpC,aAAW,CAAC,IAAI,GAAG,KAAK,OAAO,QAAQ,SAAS,SAAS,GAAG;AAC1D,UAAM,WAAW,gBAAgB,GAAG;AACpC,eAAW,IAAI,IAAI,QAAQ;AAG3B,eAAW,SAAS,SAAS,SAAS;AACpC,gBAAU,IAAI,OAAO,EAAE;AAAA,IACzB;AAAA,EACF;AACF;AAwBO,SAAS,kBAA8B;AAC5C,kBAAgB;AAChB,MAAI,CAAC,WAAY,QAAO,CAAC;AACzB,SAAO,MAAM,KAAK,WAAW,OAAO,CAAC;AACvC;AAqBO,SAAS,YAAY,WAAyC;AACnE,kBAAgB;AAChB,QAAM,WAAW,WAAW,IAAI,SAAS,KAAK;AAC9C,SAAO,YAAY,IAAI,QAAQ;AACjC;AAuBO,SAAS,aAAa,WAA2B;AACtD,kBAAgB;AAChB,SAAO,WAAW,IAAI,SAAS,KAAK;AACtC;AAsBO,SAAS,uBAAuB,UAAwC;AAC7E,SAAO,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ;AAChE;AAwBO,SAAS,qBAA2C;AACzD,SAAO,gBAAgB,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,SAAS;AAC/D;AAoBO,SAAS,qBAAqB,QAAoC;AACvE,SAAO,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM;AAC5D;AAuBO,SAAS,2BAA2B,MAA0B;AACnE,SAAO,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,iBAAiB,IAAI;AAChE;AAmBO,SAAS,sBAAgC;AAC9C,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,KAAK,gBAAgB,GAAG;AACjC,UAAM,IAAI,EAAE,YAAY;AAAA,EAC1B;AACA,SAAO,MAAM,KAAK,KAAK;AACzB;AAkBO,SAAS,mBAA2B;AACzC,kBAAgB;AAChB,SAAO,YAAY,QAAQ;AAC7B;AAkBO,SAAS,qBAA6B;AAC3C,SAAO,aAAa,EAAE;AACxB;AAqBO,SAAS,wBAAwB,OAA8B;AACpE,SAAO,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM,UAAU,SAAS,KAAK,CAAC;AACvF;AAwBO,SAAS,oBAAoB,aAAqC;AACvE,QAAM,YACJ,eAAe,YAAY,SAAS,IAChC,YAAY,IAAI,CAAC,OAAO,YAAY,EAAE,CAAC,EAAE,OAAO,CAAC,MAAqB,MAAM,MAAS,IACrF,gBAAgB;AAEtB,MAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AAEpC,QAAM,QAAQ,UAAU,CAAC,EAAG,aAAa,MAAM;AAC/C,SAAO,MAAM;AAAA,IAAO,CAAC,UACnB,UAAU,MAAM,CAAC,MAAM,EAAE,aAAa,MAAM,UAAU,SAAS,KAAK,CAAC;AAAA,EACvE;AACF;AA6BO,SAAS,iBAAiB,UAAoB,SAA0B;AAC7E,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,MAAI,UAAmB,SAAS;AAChC,aAAW,QAAQ,OAAO;AACxB,QAAI,WAAW,QAAQ,OAAO,YAAY,SAAU,QAAO;AAC3D,cAAW,QAAoC,IAAI;AAAA,EACrD;AACA,MAAI,OAAO,YAAY,UAAW,QAAO;AACzC,MAAI,MAAM,QAAQ,OAAO,EAAG,QAAO,QAAQ,SAAS;AACpD,SAAO,WAAW;AACpB;AAoBO,SAAS,2BAAuC;AACrD,SAAO,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM,iBAAiB;AAC/E;AAyBO,SAAS,8BACd,MACY;AACZ,SAAO,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM,IAAI,MAAM,IAAI;AAC5E;AAmDO,SAAS,iCAAiC,WAA6B;AAC5E,QAAM,WAAW,YAAY,SAAS;AACtC,SAAO,UAAU,wBAAwB,CAAC,GAAG,SAAS,qBAAqB,IAAI,CAAC;AAClF;AAwBO,SAAS,+BAA+B,YAA0C;AACvF,SAAO,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO,eAAe,UAAU;AACxF;AA2BO,SAAS,wBACd,UACA,OACA,YACwD;AACxD,QAAM,aAAa,yBAAyB,UAAU,OAAO,UAAU;AACvE,QAAM,EAAE,YAAY,kBAAkB,kBAAkB,IAAI,SAAS,aAAa;AAElF,QAAM,oBAAoB,MAAqB;AAC7C,QAAI,UAAU,YAAY,iBAAkB,QAAO;AACnD,QAAI,UAAU,aAAa,qBAAqB,YAAY;AAC1D,aAAO,KAAK,YAAY,iBAAiB;AAAA,IAC3C;AACA,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,kBAAkB;AACrC,QAAM,aAAa,UAAU,WAAW,WAAW;AAEnD,UAAQ,YAAY;AAAA,IAClB,KAAK;AACH,aAAO,CAAC,EAAE,MAAM,YAAY,QAAQ,UAAU,OAAO,WAAW,CAAC;AAAA,IACnE,KAAK;AACH,aAAO,aAAa,CAAC,EAAE,MAAM,YAAY,QAAQ,UAAU,OAAO,WAAW,CAAC,IAAI,CAAC;AAAA,IACrF,KAAK;AACH,aAAO;AAAA,QACL,GAAI,aAAa,CAAC,EAAE,MAAM,YAAY,QAAQ,UAAU,OAAO,WAAW,CAAC,IAAI,CAAC;AAAA,QAChF,EAAE,MAAM,YAAY,QAAQ,UAAU,OAAO,WAAW;AAAA,MAC1D;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,EAAE,MAAM,YAAY,QAAQ,UAAU,OAAO,WAAW;AAAA,QACxD,GAAI,aAAa,CAAC,EAAE,MAAM,YAAY,QAAQ,UAAU,OAAO,WAAW,CAAC,IAAI,CAAC;AAAA,MAClF;AAAA,IACF,KAAK;AACH,UAAI,UAAU,UAAU;AACtB,eAAO,CAAC,EAAE,MAAM,YAAY,QAAQ,UAAU,OAAO,SAAS,CAAC;AAAA,MACjE;AACA,aAAO;AAAA,QACL,GAAI,aAAa,CAAC,EAAE,MAAM,YAAY,QAAQ,UAAU,OAAO,UAAU,CAAC,IAAI,CAAC;AAAA,QAC/E,EAAE,MAAM,YAAY,QAAQ,UAAU,OAAO,UAAU;AAAA,MACzD;AAAA,IACF;AACE,aAAO,CAAC,EAAE,MAAM,YAAY,QAAQ,UAAU,OAAO,WAAW,CAAC;AAAA,EACrE;AACF;AAuBO,SAAS,iBAKb;AACD,SAAO,gBAAgB,EAAE,IAAI,CAAC,MAAM;AAClC,UAAM,EAAE,YAAY,kBAAkB,kBAAkB,IAAI,EAAE,aAAa;AAC3E,UAAM,eAAe,eAAe;AACpC,WAAO;AAAA,MACL,YAAY,EAAE;AAAA,MACd,UAAU,EAAE;AAAA,MACZ;AAAA,MACA,OAAO;AAAA,QACL,QAAQ,eAAe,EAAE,aAAc,oBAAoB;AAAA,QAC3D,SAAS,eAAe,EAAE,oBAAqB,qBAAqB;AAAA,MACtE;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAuBO,SAAS,wBAAwB,WAAqD;AAC3F,SAAO,YAAY,SAAS,GAAG;AACjC;AA4BO,SAAS,qBAAqB,WAAmB,gBAAiC;AACvF,QAAM,WAAW,YAAY,SAAS;AACtC,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,iBAAiB,UAAU,cAAc;AAClD;;;AC9yBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAiDA,SAAS,eAAuB;AACrC,SAAO,IAAI,OAAO,4BAA4B,GAAG;AACnD;AAoBO,SAAS,iBAAiB,SAAkC;AACjE,MAAI,WAAW;AAEf,QAAM,OAAO,CAAC,OAAe,QAAgB,cAC3C,MAAM,QAAQ,IAAI,OAAO,QAAQ,KAAK,GAAG,CAAC,UAAU;AAClD,QAAI,UAAU,UAAW,QAAO;AAChC,gBAAY;AACZ,WAAO;AAAA,EACT,CAAC;AAEH,MAAI,MAAM,KAAK,SAAS,oCAAoC,kBAAkB;AAC9E,QAAM,KAAK,KAAK,kCAAkC,gBAAgB;AAElE,SAAO,EAAE,SAAS,KAAK,SAAS;AAClC;AAuBO,SAAS,YAAY,aAAmC;AAC7D,QAAM,SAAuB,CAAC;AAC9B,QAAM,UAAU,aAAa;AAE7B,WAAS,QAAQ,QAAQ,KAAK,WAAW,GAAG,UAAU,MAAM,QAAQ,QAAQ,KAAK,WAAW,GAAG;AAC7F,UAAM,MAAM,MAAM,CAAC;AACnB,WAAO,KAAK;AAAA,MACV;AAAA,MACA,UAAU,MAAM,CAAC,KAAK,IAAI,KAAK;AAAA,MAC/B,YAAY,MAAM;AAAA,MAClB,UAAU,MAAM,QAAQ,IAAI;AAAA,IAC9B,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAgBO,SAAS,WAAW,SAAyB;AAClD,SAAO,GAAG,kBAAkB;AAAA,EAAK,OAAO;AAAA,EAAK,gBAAgB;AAC/D;AAQA,SAAS,KAAK,SAAyB;AACrC,QAAM,YAAY,QAAQ,QAAQ,WAAW,MAAM,EAAE,QAAQ;AAC7D,SAAO,UAAU,SAAS,IAAI,GAAG,SAAS;AAAA,IAAO;AACnD;AA+DO,SAAS,UACd,UACA,gBACA,SAA8B,WACb;AACjB,QAAM,EAAE,SAAS,QAAQ,SAAS,IAAI,iBAAiB,QAAQ;AAC/D,QAAM,SAAS,YAAY,MAAM;AAGjC,QAAM,eAAe,WAAW,eAAe,KAAK,CAAC;AAErD,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,OAAO,OAAO,KAAK;AACzB,QAAI,KAAK,WAAW,EAAG,QAAO,EAAE,SAAS,KAAK,YAAY,GAAG,cAAc,GAAG,SAAS;AACvF,WAAO;AAAA,MACL,SACE,WAAW,WACP,KAAK,GAAG,IAAI;AAAA;AAAA,EAAO,YAAY,EAAE,IACjC,KAAK,GAAG,YAAY;AAAA;AAAA,EAAO,IAAI,EAAE;AAAA,MACvC,cAAc;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM;AACV,MAAI,SAAS;AAEb,aAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,GAAG;AAC7C,WAAO,OAAO,MAAM,QAAQ,MAAM,UAAU;AAC5C,aAAS,MAAM;AAEf,QAAI,UAAU,EAAG,QAAO;AAAA,EAC1B;AACA,SAAO,OAAO,MAAM,MAAM;AAE1B,SAAO,EAAE,SAAS,KAAK,GAAG,GAAG,cAAc,OAAO,QAAQ,SAAS;AACrE;AAuBO,SAAS,iBAAiB,QAAuC;AACtE,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,QAAkB,CAAC;AAEzB,aAAW,SAAS,QAAQ;AAC1B,eAAW,QAAQ,MAAM,QAAQ,MAAM,IAAI,GAAG;AAC5C,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,QAAQ,WAAW,KAAK,KAAK,IAAI,OAAO,EAAG;AAC/C,WAAK,IAAI,OAAO;AAChB,YAAM,KAAK,OAAO;AAAA,IACpB;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AA0BO,SAAS,cAAc,UAAmC;AAC/D,QAAM,EAAE,SAAS,QAAQ,SAAS,IAAI,iBAAiB,QAAQ;AAC/D,QAAM,SAAS,YAAY,MAAM;AAEjC,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,EAAE,SAAS,QAAQ,cAAc,GAAG,SAAS;AAAA,EACtD;AAEA,QAAM,SAAS,UAAU,QAAQ,iBAAiB,MAAM,CAAC;AACzD,SAAO,EAAE,SAAS,OAAO,SAAS,cAAc,OAAO,QAAQ,SAAS;AAC1E;;;ACzTO,SAAS,sBAAsB,UAAqC;AACzE,QAAM,QAAkB,CAAC;AAEzB,aAAW,OAAO,SAAS,YAAY;AACrC,UAAM,KAAK,GAAG;AAAA,EAChB;AAEA,MAAI,SAAS,WAAW,SAAS,QAAQ,SAAS,GAAG;AACnD,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,KAAK,EAAE;AAAA,IACf;AACA,UAAM,KAAK,GAAG,SAAS,OAAO;AAAA,EAChC;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAqBO,SAAS,sBAAsB,SAAoC;AACxE,QAAM,aAAuB,CAAC;AAC9B,QAAM,eAAyB,CAAC;AAEhC,aAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,QAAS;AAEd,QAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,iBAAW,KAAK,OAAO;AAAA,IACzB,OAAO;AACL,mBAAa,KAAK,IAAI;AAAA,IACxB;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,SAAS,aAAa,SAAS,IAAI,eAAe;AAAA,EACpD;AACF;AAwBO,SAAS,yBAAyB,SAG9B;AACT,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,gCAAgC;AAC3C,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,qEAAqE;AAChF,QAAM,KAAK,iDAAiD;AAE5D,MAAI,SAAS,eAAe;AAC1B,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,mBAAmB,QAAQ,aAAa,EAAE;AACrD,UAAM,KAAK,uCAAuC;AAAA,EACpD;AAEA,MAAI,SAAS,eAAe;AAC1B,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,QAAQ,aAAa;AAAA,EAClC;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAmBO,SAAS,sBAAsB,YAA8B;AAClE,MAAI,WAAW,WAAW,EAAG,QAAO;AAEpC,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,sBAAsB;AACjC,QAAM,KAAK,EAAE;AAEb,aAAW,QAAQ,YAAY;AAC7B,UAAM,KAAK,OAAO,IAAI,6BAA6B;AAAA,EACrD;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AA+CO,SAAS,oBAAoB,WAAgD;AAClF,QAAM,SAAS,oBAAI,IAAwB;AAE3C,aAAW,YAAY,WAAW;AAChC,UAAM,WAAW,OAAO,IAAI,SAAS,YAAY,KAAK,CAAC;AACvD,aAAS,KAAK,QAAQ;AACtB,WAAO,IAAI,SAAS,cAAc,QAAQ;AAAA,EAC5C;AAEA,SAAO;AACT;;;AJzLO,SAAS,iBAAiB,aAAmC;AAClE,SAAO,YAAY,WAAW;AAChC;AAsDA,eAAsB,WAAW,UAAyC;AACxE,MAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,WAAO,EAAE,UAAU,SAAS,GAAG,MAAM,GAAG,UAAU,OAAO,UAAU,EAAE;AAAA,EACvE;AAEA,SAAO,aAAa,UAAU,YAAY;AACxC,UAAM,WAAW,MAAMC,UAAS,UAAU,OAAO;AAKjD,UAAM,EAAE,SAAS,QAAQ,SAAS,IAAI,iBAAiB,QAAQ;AAC/D,UAAM,SAAS,YAAY,MAAM;AAEjC,QAAI,OAAO,WAAW,GAAG;AACvB,UAAI,WAAW,KAAK,WAAW,UAAU;AACvC,cAAM,gBAAgB,EAAE,MAAM,UAAU,SAAS,OAAO,CAAC;AACzD,eAAO,EAAE,UAAU,SAAS,GAAG,MAAM,GAAG,UAAU,MAAM,SAAS;AAAA,MACnE;AACA,aAAO,EAAE,UAAU,SAAS,GAAG,MAAM,GAAG,UAAU,OAAO,SAAS;AAAA,IACpE;AAGA,UAAM,gBAAgB,oBAAI,IAAwB;AAClD,eAAW,SAAS,QAAQ;AAC1B,oBAAc,IAAI,MAAM,SAAS,KAAK;AAAA,IACxC;AAEA,UAAM,UAAU,IAAI,IAAgB,cAAc,OAAO,CAAC;AAC1D,UAAM,UAAU,OAAO,SAAS,QAAQ;AAIxC,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,eAAW,SAAS,QAAQ;AAE1B,gBAAU,OAAO,MAAM,QAAQ,MAAM,UAAU;AAC/C,eAAS,MAAM;AAEf,UAAI,QAAQ,IAAI,KAAK,GAAG;AACtB,kBAAU,MAAM;AAAA,MAClB;AAAA,IAGF;AAGA,cAAU,OAAO,MAAM,MAAM;AAG7B,aAAS,GAAG,OAAO,QAAQ,WAAW,MAAM,EAAE,QAAQ,CAAC;AAAA;AAKvD,QAAI,YAAY,KAAK,aAAa,GAAG;AACnC,aAAO,EAAE,UAAU,SAAS,GAAG,MAAM,OAAO,QAAQ,UAAU,OAAO,SAAS;AAAA,IAChF;AAEA,QAAI,WAAW,UAAU;AACvB,aAAO,EAAE,UAAU,SAAS,GAAG,MAAM,OAAO,QAAQ,UAAU,OAAO,SAAS;AAAA,IAChF;AAEA,UAAM,gBAAgB,EAAE,MAAM,UAAU,SAAS,OAAO,CAAC;AACzD,WAAO,EAAE,UAAU,SAAS,MAAM,QAAQ,MAAM,UAAU,MAAM,SAAS;AAAA,EAC3E,CAAC;AACH;AAuBA,eAAsB,YAAY,WAA8C;AAC9E,QAAM,UAA0B,CAAC;AACjC,aAAW,YAAY,WAAW;AAChC,YAAQ,KAAK,MAAM,WAAW,QAAQ,CAAC;AAAA,EACzC;AACA,SAAO;AACT;AA8BO,SAAS,uBAAuB,YAAoB,WAAiC;AAC1F,QAAM,QAAkB;AAAA,IACtBC,MAAK,cAAc,GAAG,WAAW;AAAA,IACjCA,MAAK,YAAY,WAAW;AAAA,IAC5BA,MAAK,YAAY,WAAW;AAAA,IAC5BA,MAAK,YAAY,WAAW;AAAA,EAC9B;AAEA,aAAW,YAAY,WAAW;AAChC,UAAM,KAAKA,MAAK,SAAS,YAAY,SAAS,YAAY,CAAC;AAAA,EAC7D;AAEA,SAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC;AAC3B;AA0CA,eAAsB,uBACpB,YACA,WACuB;AACvB,QAAM,QAAQ,uBAAuB,YAAY,SAAS,EAAE,OAAO,CAAC,MAAM,WAAW,CAAC,CAAC;AACvF,QAAM,QAAwB,CAAC;AAE/B,aAAW,YAAY,OAAO;AAC5B,UAAM;AAAA,MACJ,MAAM,aAAa,UAAU,YAAmC;AAC9D,cAAM,WAAW,MAAMD,UAAS,UAAU,OAAO;AACjD,cAAM,EAAE,SAAS,cAAc,SAAS,IAAI,cAAc,QAAQ;AAClE,cAAM,UAAU,KAAK,IAAI,GAAG,eAAe,CAAC;AAE5C,YAAI,YAAY,KAAK,aAAa,GAAG;AACnC,iBAAO,EAAE,UAAU,SAAS,GAAG,MAAM,cAAc,UAAU,OAAO,UAAU,EAAE;AAAA,QAClF;AACA,YAAI,YAAY,UAAU;AACxB,iBAAO,EAAE,UAAU,SAAS,GAAG,MAAM,cAAc,UAAU,OAAO,SAAS;AAAA,QAC/E;AAEA,cAAM,gBAAgB,EAAE,MAAM,UAAU,QAAiB,CAAC;AAC1D,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA,MAAM,eAAe,IAAI,IAAI;AAAA,UAC7B,UAAU;AAAA,UACV;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,UAAU,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,UAAU,CAAC;AAAA,IAClD,SAAS,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,SAAS,CAAC;AAAA,IAChD,eAAe,MAAM,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE;AAAA,EACjD;AACF;AA4BA,eAAsB,eACpB,UACA,iBAC0B;AAC1B,MAAI,CAAC,WAAW,QAAQ,EAAG,QAAO;AAElC,QAAM,MAAM,MAAMA,UAAS,UAAU,OAAO;AAK5C,QAAM,EAAE,SAAS,SAAS,IAAI,iBAAiB,GAAG;AAClD,QAAM,SAAS,YAAY,OAAO;AAElC,MAAI,OAAO,WAAW,EAAG,QAAO;AAIhC,MAAI,OAAO,SAAS,KAAK,WAAW,EAAG,QAAO;AAE9C,MAAI,iBAAiB;AACnB,WAAO,OAAO,CAAC,GAAG,YAAY,gBAAgB,KAAK,IAAI,YAAY;AAAA,EACrE;AAEA,SAAO;AACT;AA0CA,eAAsB,OAAO,UAAkB,SAAgD;AAI7F,QAAM,OAAO,QAAQ,KAAK;AAE1B,MAAI,CAAC,WAAW,QAAQ,GAAG;AAGzB,WAAO,aAAmC,UAAU,YAAY;AAC9D,YAAM,gBAAgB,EAAE,MAAM,UAAU,SAAS,GAAG,WAAW,IAAI,CAAC;AAAA,EAAK,CAAC;AAC1E,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,SAAO,aAAmC,UAAU,YAAY;AAC9D,UAAM,WAAW,MAAMA,UAAS,UAAU,OAAO;AAOjD,QAAI,SAAS,WAAW,GAAG;AACzB,wBAAkB,UAAU,WAAW,MAAME,MAAK,QAAQ,GAAG,IAAI;AAAA,IACnE;AAEA,UAAM,EAAE,SAAS,MAAM,cAAc,SAAS,IAAI,UAAU,UAAU,IAAI;AAE1E,QAAI,SAAS,SAAU,QAAO;AAE9B,UAAM,gBAAgB,EAAE,MAAM,UAAU,SAAS,KAAK,CAAC;AAGvD,QAAI,iBAAiB,EAAG,QAAO;AAC/B,QAAI,WAAW,EAAG,QAAO;AACzB,QAAI,eAAe,EAAG,QAAO;AAC7B,WAAO;AAAA,EACT,CAAC;AACH;AAwBA,eAAsB,gBAAgB,UAAoC;AACxE,MAAI,CAAC,WAAW,QAAQ,EAAG,QAAO;AAElC,SAAO,aAAa,UAAU,YAAY;AACxC,UAAM,WAAW,MAAMF,UAAS,UAAU,OAAO;AACjD,UAAM,EAAE,QAAQ,IAAI,iBAAiB,QAAQ;AAI7C,QAAI,YAAY,OAAO,EAAE,WAAW,EAAG,QAAO;AAE9C,UAAM,UAAU,QACb,QAAQ,aAAa,GAAG,EAAE,EAC1B,QAAQ,WAAW,IAAI,EACvB,KAAK;AAER,QAAI,CAAC,SAAS;AAEZ,YAAM,EAAE,IAAAG,IAAG,IAAI,MAAM,OAAO,aAAkB;AAC9C,YAAMA,IAAG,QAAQ;AAAA,IACnB,OAAO;AACL,YAAM,gBAAgB,EAAE,MAAM,UAAU,SAAS,GAAG,OAAO;AAAA,EAAK,CAAC;AAAA,IACnE;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AA0BA,eAAsB,mBACpB,WACA,YACA,OACA,iBACiC;AACjC,QAAM,UAAkC,CAAC;AACzC,QAAM,UAAU,oBAAI,IAAY;AAEhC,aAAW,YAAY,WAAW;AAChC,UAAM,WACJ,UAAU,WACNF,MAAK,SAAS,YAAY,SAAS,YAAY,IAC/CA,MAAK,YAAY,SAAS,YAAY;AAG5C,QAAI,QAAQ,IAAI,QAAQ,EAAG;AAC3B,YAAQ,IAAI,QAAQ;AAEpB,UAAM,SAAS,MAAM,eAAe,UAAU,eAAe;AAE7D,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,UAAU,SAAS;AAAA,MACnB;AAAA,MACA,YAAY,WAAW,QAAQ;AAAA,IACjC,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AA2BA,eAAsB,UACpB,WACA,YACA,OACA,SAC4C;AAC5C,QAAM,UAAU,oBAAI,IAAkC;AACtD,QAAM,WAAW,oBAAI,IAAY;AAEjC,aAAW,YAAY,WAAW;AAChC,UAAM,WACJ,UAAU,WACNA,MAAK,SAAS,YAAY,SAAS,YAAY,IAC/CA,MAAK,YAAY,SAAS,YAAY;AAG5C,QAAI,SAAS,IAAI,QAAQ,EAAG;AAC5B,aAAS,IAAI,QAAQ;AAErB,UAAM,SAAS,MAAM,OAAO,UAAU,OAAO;AAC7C,YAAQ,IAAI,UAAU,MAAM;AAAA,EAC9B;AAEA,SAAO;AACT;AAwEA,eAAsB,8BACpB,YACA,YACA,SAC8C;AAC9C,QAAM,WAAW,YAAY,UAAU;AACvC,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,sBAAsB,UAAU,mCAAmC;AAAA,EACrF;AAEA,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,WACJ,UAAU,WACNA,MAAK,SAAS,YAAY,SAAS,YAAY,IAC/CA,MAAK,YAAY,SAAS,YAAY;AAG5C,QAAM,aAAa,QAAQ,cAAc,iCAAiC,UAAU;AAEpF,QAAM,WAA8B;AAAA,IAClC;AAAA,IACA,SAAS,QAAQ;AAAA,EACnB;AAEA,QAAM,mBAAmB,sBAAsB,QAAQ;AACvD,QAAM,SAAS,MAAM,OAAO,UAAU,gBAAgB;AAEtD,SAAO;AAAA,IACL;AAAA,IACA,cAAc,SAAS;AAAA,IACvB;AAAA,IACA,YAAY,SAAS;AAAA,EACvB;AACF;AA6BA,eAAsB,kCACpB,aACA,YACA,SACgD;AAChD,QAAM,UAAiD,CAAC;AACxD,QAAM,YAAY,oBAAI,IAAY;AAElC,aAAW,cAAc,aAAa;AACpC,UAAM,WAAW,YAAY,UAAU;AACvC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,sBAAsB,UAAU,mCAAmC;AAAA,IACrF;AAEA,UAAM,QAAQ,QAAQ,SAAS;AAC/B,UAAM,WACJ,UAAU,WACNA,MAAK,SAAS,YAAY,SAAS,YAAY,IAC/CA,MAAK,YAAY,SAAS,YAAY;AAG5C,QAAI,UAAU,IAAI,QAAQ,EAAG;AAC7B,cAAU,IAAI,QAAQ;AAGtB,UAAM,aAAa,QAAQ,cAAc,iCAAiC,UAAU;AAEpF,UAAM,WAA8B;AAAA,MAClC;AAAA,MACA,SAAS,QAAQ;AAAA,IACnB;AAEA,UAAM,mBAAmB,sBAAsB,QAAQ;AACvD,UAAM,SAAS,MAAM,OAAO,UAAU,gBAAgB;AAEtD,YAAQ,KAAK;AAAA,MACX;AAAA,MACA,cAAc,SAAS;AAAA,MACvB;AAAA,MACA,YAAY,SAAS;AAAA,IACvB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAqDO,SAAS,uBAAuB,YAAmC;AACxE,QAAM,OAAO,QAAQ;AAErB,UAAQ,YAA0C;AAAA,IAChD,KAAK;AAAA,IACL,KAAK;AACH,aAAOA,MAAK,MAAM,WAAW,QAAQ;AAAA,IACvC,KAAK;AACH,aAAOA,MAAK,MAAM,WAAW,YAAY,QAAQ;AAAA,IACnD,KAAK;AACH,aAAOA,MAAK,MAAM,WAAW,SAAS,QAAQ;AAAA,IAChD,KAAK;AACH,aAAOA,MAAK,MAAM,WAAW,QAAQ;AAAA,IACvC,KAAK;AACH,aAAOA,MAAK,MAAM,WAAW,MAAM,QAAQ;AAAA,IAC7C,KAAK;AACH,aAAOA,MAAK,MAAM,WAAW,QAAQ,QAAQ;AAAA,IAC/C,KAAK;AACH,aAAOA,MAAK,MAAM,WAAW,UAAU,QAAQ;AAAA,IACjD,KAAK;AACH,aAAOA,MAAK,MAAM,WAAW,UAAU,QAAQ;AAAA,IACjD;AACE,aAAO;AAAA,EACX;AACF;AAwEA,eAAsB,6BACpB,aACA,SACiC;AACjC,QAAM,UAAkC,CAAC;AACzC,QAAM,YAAY,oBAAI,IAAY;AAElC,aAAW,cAAc,aAAa;AACpC,UAAM,SAAS,uBAAuB,UAAU;AAChD,QAAI,WAAW,MAAM;AAGnB;AAAA,IACF;AAEA,UAAM,WAAWA,MAAK,QAAQ,QAAQ,QAAQ;AAI9C,QAAI,UAAU,IAAI,QAAQ,GAAG;AAE3B,YAAM,iBAAiB,QAAQ,KAAK,CAAC,MAAM,EAAE,aAAa,QAAQ;AAClE,UAAI,gBAAgB;AAClB,gBAAQ,KAAK,EAAE,YAAY,UAAU,QAAQ,eAAe,OAAO,CAAC;AAAA,MACtE;AACA;AAAA,IACF;AACA,cAAU,IAAI,QAAQ;AAEtB,QAAI,QAAQ,uBAAuB,QAAQ,CAAC,WAAW,MAAM,GAAG;AAE9D;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,OAAO,UAAU,QAAQ,OAAO;AACrD,YAAQ,KAAK,EAAE,YAAY,UAAU,OAAO,CAAC;AAAA,EAC/C;AAEA,SAAO;AACT;","names":["readFile","stat","join","dirname","dirname","readFile","join","stat","rm"]}