@sofatutor/agent-bridge 0.13.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["pkg.version"],"sources":["../src/lib/config.ts","../src/lib/git.ts","../src/lib/fs.ts","../src/lib/sources.ts","../package.json","../src/lib/version.ts","../src/commands/init.ts","../src/lib/migrations/index.ts","../src/lib/manifest.ts","../src/lib/sync.ts","../src/commands/sync.ts","../src/commands/update.ts","../src/commands/opt-out.ts","../src/index.ts"],"sourcesContent":["import { readFile, writeFile, access, mkdir, rm } from 'node:fs/promises';\nimport { join, isAbsolute } from 'node:path';\nimport yaml from 'js-yaml';\nimport { z } from 'zod';\n\n// ---------------------------------------------------------------------------\n// Zod Schemas\n// ---------------------------------------------------------------------------\n\nconst SAFE_NAME_RE = /^[A-Za-z0-9._-]+$/;\n\nconst safeName = z\n .string()\n .min(1)\n .refine((v) => SAFE_NAME_RE.test(v) && v !== '.' && v !== '..', {\n message: 'Only [A-Za-z0-9._-] characters allowed, cannot be . or ..',\n });\n\nconst safeRelativeFolder = z\n .string()\n .min(1)\n .refine(\n (value) => {\n if (isAbsolute(value) || value.includes('\\0')) return false;\n const segments = value.split(/[\\\\/]/).filter((s) => s.length > 0);\n if (segments.length === 0) return false;\n return segments.every(\n (seg) => seg !== '..' && seg !== '.' && /^\\.?[A-Za-z0-9._-]+$/.test(seg)\n );\n },\n { message: 'Must be a relative path using only [A-Za-z0-9._-]' }\n );\n\nconst toolConfigSchema = z.object({\n name: safeName.refine((v) => !v.includes('--'), {\n message: \"Must not contain '--' (reserved for tool-prefix routing)\",\n }),\n folder: safeRelativeFolder,\n});\n\nconst sourceConfigSchema = z.object({\n name: safeName,\n source: z.string().min(1).refine((v) => !v.startsWith('-'), {\n message: \"Must not start with '-'\",\n }),\n branch: z\n .string()\n .refine((v) => /^[A-Za-z0-9._/-]+$/.test(v) && !v.startsWith('-'), {\n message: \"Must match [A-Za-z0-9._/-] and not start with '-'\",\n })\n .optional(),\n});\n\nconst bridgeConfigSchema = z\n .object({\n version: z.string().optional(),\n domains: z.array(safeName).min(1, \"'domains' must be a non-empty array\"),\n tools: z.array(toolConfigSchema).min(1, \"'tools' must be a non-empty array\"),\n sources: z.array(sourceConfigSchema).min(1, \"'sources' must be a non-empty array\"),\n })\n .superRefine((data, ctx) => {\n // Check unique tool names\n const toolNames = new Set<string>();\n const toolFolders = new Set<string>();\n data.tools.forEach((t, i) => {\n if (toolNames.has(t.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate tool name: '${t.name}'`,\n path: ['tools', i, 'name'],\n });\n }\n toolNames.add(t.name);\n if (toolFolders.has(t.folder)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate tool folder: '${t.folder}'`,\n path: ['tools', i, 'folder'],\n });\n }\n toolFolders.add(t.folder);\n });\n\n // Check unique source names and branch validity\n const sourceNames = new Set<string>();\n data.sources.forEach((s, i) => {\n if (sourceNames.has(s.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate source name: '${s.name}'`,\n path: ['sources', i, 'name'],\n });\n }\n sourceNames.add(s.name);\n\n const isRemote =\n s.source.startsWith('https://') ||\n s.source.startsWith('http://') ||\n s.source.startsWith('file://') ||\n /^[\\w.-]+@[\\w.-]+:/.test(s.source);\n\n if (s.branch && !isRemote) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"'branch' is only valid for remote sources\",\n path: ['sources', i, 'branch'],\n });\n }\n if (!isRemote && !isAbsolute(s.source)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'Local source paths must be absolute',\n path: ['sources', i, 'source'],\n });\n }\n });\n });\n\n// ---------------------------------------------------------------------------\n// Types (inferred from Zod schemas)\n// ---------------------------------------------------------------------------\n\nexport type SourceType = 'git-https' | 'git-ssh' | 'local';\nexport type ToolConfig = z.infer<typeof toolConfigSchema>;\nexport type SourceConfig = z.infer<typeof sourceConfigSchema>;\nexport type BridgeConfig = z.infer<typeof bridgeConfigSchema>;\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const BRIDGE_DIR = '.agent-bridge';\nexport const CONFIG_FILENAME = 'config.yml';\n\n/**\n * Tombstone written by `opt-out`. It lives inside `.agent-bridge/` so it's\n * gitignored by default (the directory's `.gitignore` ignores everything but\n * `config.yml`), keeping opt-out local to a machine. `init`/`sync` honor it so\n * a `postinstall` guard doesn't silently reinstall Agent Bridge on the next\n * `npm install`. Force-add it (`git add -f`) to commit a repo-wide opt-out.\n */\nexport const OPT_OUT_MARKER = join(BRIDGE_DIR, 'optout');\n\n// ---------------------------------------------------------------------------\n// Source type detection\n// ---------------------------------------------------------------------------\n\nexport function detectSourceType(source: string): SourceType {\n if (\n source.startsWith('https://') ||\n source.startsWith('http://') ||\n source.startsWith('file://')\n ) {\n return 'git-https';\n }\n if (/^[\\w.-]+@[\\w.-]+:/.test(source)) {\n return 'git-ssh';\n }\n return 'local';\n}\n\nexport function isRemoteSource(source: string): boolean {\n const type = detectSourceType(source);\n return type === 'git-https' || type === 'git-ssh';\n}\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nexport function bridgeDir(repoRoot: string): string {\n return join(repoRoot, BRIDGE_DIR);\n}\n\nexport function configPath(repoRoot: string): string {\n return join(repoRoot, BRIDGE_DIR, CONFIG_FILENAME);\n}\n\nexport function sourceDir(repoRoot: string, sourceName: string): string {\n return join(repoRoot, BRIDGE_DIR, sourceName);\n}\n\nexport function optOutMarkerPath(repoRoot: string): string {\n return join(repoRoot, OPT_OUT_MARKER);\n}\n\n/** Whether an opt-out tombstone is present at the repo root. */\nexport async function isOptedOut(repoRoot: string): Promise<boolean> {\n try {\n await access(optOutMarkerPath(repoRoot));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Write the opt-out tombstone inside `.agent-bridge/`. Recreates the directory\n * (opt-out deletes it) and its `.gitignore` so the marker is ignored by default.\n */\nexport async function writeOptOutMarker(repoRoot: string): Promise<void> {\n const dir = bridgeDir(repoRoot);\n await mkdir(dir, { recursive: true });\n // Same ignore rules init/sync write: ignore everything but the config.\n await writeFile(\n join(dir, '.gitignore'),\n ['# Ignore cloned sources', '*', '!config.yml', '!.gitignore'].join('\\n') + '\\n',\n 'utf-8'\n );\n await writeFile(\n optOutMarkerPath(repoRoot),\n '# Agent Bridge opt-out marker. Remove this file (or run `agent-bridge init --force`) to re-enable.\\n',\n 'utf-8'\n );\n}\n\n/** Remove the opt-out tombstone if present (idempotent). */\nexport async function removeOptOutMarker(repoRoot: string): Promise<void> {\n await rm(optOutMarkerPath(repoRoot), { force: true });\n}\n\n// ---------------------------------------------------------------------------\n// Config I/O\n// ---------------------------------------------------------------------------\n\nexport async function configExists(repoRoot: string): Promise<boolean> {\n try {\n await access(configPath(repoRoot));\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function loadConfig(repoRoot: string): Promise<BridgeConfig> {\n const raw = await readFile(configPath(repoRoot), 'utf-8');\n const data = yaml.load(raw);\n\n const result = bridgeConfigSchema.safeParse(data);\n if (!result.success) {\n const errors = result.error.issues.map(\n (i) => `${i.path.join('.')}: ${i.message}`\n );\n throw new Error(`Invalid config: ${errors.join('; ')}`);\n }\n\n return result.data;\n}\n\nexport async function saveConfig(\n repoRoot: string,\n config: BridgeConfig\n): Promise<void> {\n const dir = bridgeDir(repoRoot);\n await mkdir(dir, { recursive: true });\n const content = yaml.dump(config, { lineWidth: -1, noRefs: true });\n await writeFile(configPath(repoRoot), content, 'utf-8');\n}\n\n// ---------------------------------------------------------------------------\n// Validation (legacy interface for tests)\n// ---------------------------------------------------------------------------\n\nexport interface ConfigValidationResult {\n ok: boolean;\n errors: string[];\n}\n\nexport function validateConfig(config: unknown): ConfigValidationResult {\n const result = bridgeConfigSchema.safeParse(config);\n if (result.success) {\n return { ok: true, errors: [] };\n }\n const errors = result.error.issues.map(\n (i) => `${i.path.join('.')}: ${i.message}`\n );\n return { ok: false, errors };\n}\n","import { execSync } from 'node:child_process';\nimport { mkdir, writeFile, chmod, readFile, access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport function findRepoRoot(): string {\n try {\n return execSync('git rev-parse --show-toplevel', {\n encoding: 'utf-8',\n stdio: 'pipe',\n }).trim();\n } catch {\n return process.cwd();\n }\n}\n\n/**\n * Check if a directory is inside a Git repository.\n */\nexport function isInGitRepo(cwd?: string): boolean {\n try {\n execSync('git rev-parse --is-inside-work-tree', {\n encoding: 'utf-8',\n stdio: 'pipe',\n cwd,\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get the path to the .git/hooks directory.\n */\nexport function getGitHooksDir(repoRoot: string): string {\n return join(repoRoot, '.git', 'hooks');\n}\n\n/**\n * The hook names that Agent Bridge will install.\n */\nexport const AGENT_BRIDGE_HOOKS = ['post-checkout', 'post-merge'] as const;\nexport type AgentBridgeHook = (typeof AGENT_BRIDGE_HOOKS)[number];\n\n/**\n * Marker comment to identify Agent Bridge hooks.\n */\nconst HOOK_MARKER = '# agent-bridge-hook';\n\n/**\n * Generate the hook script content.\n * Runs update and sync in the background, logging to `.agent-bridge/hook.log`\n * (trimmed to the last ~200 lines) so failures are diagnosable.\n */\nexport function generateHookScript(): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# This hook was installed by Agent Bridge.\n# It runs 'agent-bridge update && agent-bridge sync' in the background\n# to keep your AI agent configurations up to date.\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel 2>/dev/null)\"\nLOG_DIR=\"\\${REPO_ROOT:-.}/.agent-bridge\"\nLOG_FILE=\"\\${LOG_DIR}/hook.log\"\n\nmkdir -p \"\\$LOG_DIR\" 2>/dev/null\n\n(\n # Wait a moment for git to finish\n sleep 1\n\n {\n echo \"--- $(date '+%Y-%m-%dT%H:%M:%S%z') agent-bridge hook ---\"\n if command -v agent-bridge >/dev/null 2>&1; then\n agent-bridge update && agent-bridge sync\n elif command -v npx >/dev/null 2>&1; then\n npx @sofatutor/agent-bridge update && npx @sofatutor/agent-bridge sync\n else\n echo \"agent-bridge not found (install globally or ensure npx is available)\"\n fi\n } >>\"\\$LOG_FILE\" 2>&1\n\n # Keep the log from growing without bound.\n if [ -f \"\\$LOG_FILE\" ]; then\n tail -n 200 \"\\$LOG_FILE\" >\"\\$LOG_FILE.tmp\" && mv \"\\$LOG_FILE.tmp\" \"\\$LOG_FILE\"\n fi\n) </dev/null >/dev/null 2>&1 &\n`;\n}\n\n/**\n * Check if a hook file contains the Agent Bridge marker.\n */\nexport async function hasAgentBridgeHook(hookPath: string): Promise<boolean> {\n try {\n const content = await readFile(hookPath, 'utf-8');\n return content.includes(HOOK_MARKER);\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a hook file exists.\n */\nasync function hookExists(hookPath: string): Promise<boolean> {\n try {\n await access(hookPath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport interface InstallHooksResult {\n installed: AgentBridgeHook[];\n skipped: AgentBridgeHook[];\n errors: Array<{ hook: AgentBridgeHook; error: string }>;\n}\n\n/**\n * Install Agent Bridge git hooks in the repository.\n * \n * @param repoRoot - The root of the git repository\n * @param force - If true, overwrite existing hooks that don't have the marker\n * @returns Result with installed, skipped, and errored hooks\n */\nexport async function installGitHooks(\n repoRoot: string,\n force = false\n): Promise<InstallHooksResult> {\n const result: InstallHooksResult = {\n installed: [],\n skipped: [],\n errors: [],\n };\n\n if (!isInGitRepo(repoRoot)) {\n for (const hook of AGENT_BRIDGE_HOOKS) {\n result.errors.push({ hook, error: 'Not a git repository' });\n }\n return result;\n }\n\n const hooksDir = getGitHooksDir(repoRoot);\n\n // Ensure hooks directory exists\n try {\n await mkdir(hooksDir, { recursive: true });\n } catch (err) {\n for (const hook of AGENT_BRIDGE_HOOKS) {\n result.errors.push({ hook, error: `Failed to create hooks directory: ${err}` });\n }\n return result;\n }\n\n const hookContent = generateHookScript();\n\n for (const hookName of AGENT_BRIDGE_HOOKS) {\n const hookPath = join(hooksDir, hookName);\n\n try {\n const exists = await hookExists(hookPath);\n \n if (exists) {\n const hasMarker = await hasAgentBridgeHook(hookPath);\n \n if (hasMarker) {\n // Already installed, update it\n await writeFile(hookPath, hookContent, 'utf-8');\n await chmod(hookPath, 0o755);\n result.installed.push(hookName);\n } else if (force) {\n // Force overwrite\n await writeFile(hookPath, hookContent, 'utf-8');\n await chmod(hookPath, 0o755);\n result.installed.push(hookName);\n } else {\n // Skip - existing hook without marker\n result.skipped.push(hookName);\n }\n } else {\n // Create new hook\n await writeFile(hookPath, hookContent, 'utf-8');\n await chmod(hookPath, 0o755);\n result.installed.push(hookName);\n }\n } catch (err) {\n result.errors.push({ hook: hookName, error: String(err) });\n }\n }\n\n return result;\n}\n\n/**\n * Remove Agent Bridge git hooks from the repository.\n * Only removes hooks that have the Agent Bridge marker.\n */\nexport async function removeGitHooks(repoRoot: string): Promise<AgentBridgeHook[]> {\n const removed: AgentBridgeHook[] = [];\n\n if (!isInGitRepo(repoRoot)) {\n return removed;\n }\n\n const hooksDir = getGitHooksDir(repoRoot);\n\n for (const hookName of AGENT_BRIDGE_HOOKS) {\n const hookPath = join(hooksDir, hookName);\n \n try {\n if (await hasAgentBridgeHook(hookPath)) {\n const { unlink } = await import('node:fs/promises');\n await unlink(hookPath);\n removed.push(hookName);\n }\n } catch {\n // Ignore errors\n }\n }\n\n return removed;\n}\n","import { readdir, copyFile, stat } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport fsExtra from 'fs-extra';\n\nconst { pathExists, remove, outputFile, readFile: fsReadFile, ensureDir } = fsExtra;\n\n/** Name of the marker file placed inside every synced feature folder. */\nexport const MARKER_FILENAME = '.agentbridge';\n\nexport async function dirExists(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function fileExists(p: string): Promise<boolean> {\n return pathExists(p);\n}\n\nexport async function listFilesRecursive(dir: string): Promise<string[]> {\n const files: string[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n // Skip symlinks entirely: never follow them out of the source tree,\n // and don't attempt to replicate them (keeps the destination simple\n // and avoids symlink-escape vulnerabilities).\n if (entry.isSymbolicLink()) continue;\n // Skip Agent Bridge marker files that may exist in source repos.\n if (entry.name === MARKER_FILENAME) continue;\n if (entry.isDirectory()) {\n const subFiles = await listFilesRecursive(join(dir, entry.name));\n files.push(...subFiles.map((f) => join(entry.name, f)));\n } else if (entry.isFile()) {\n files.push(entry.name);\n }\n }\n return files;\n}\n\n/**\n * Copy all files from `srcDir` into `destDir`, preserving nested structure.\n * Overwrites existing files. Creates directories as needed.\n */\nexport async function copyDirContents(srcDir: string, destDir: string): Promise<void> {\n const files = await listFilesRecursive(srcDir);\n for (const relFile of files) {\n const srcFile = join(srcDir, relFile);\n const destFile = join(destDir, relFile);\n await ensureDir(dirname(destFile));\n await copyFile(srcFile, destFile);\n }\n}\n\n/**\n * Write the `.agentbridge` marker file into a feature folder.\n */\nexport async function writeMarker(featureDir: string): Promise<void> {\n await outputFile(join(featureDir, MARKER_FILENAME), '');\n}\n\n/**\n * Check whether a directory contains the `.agentbridge` marker.\n */\nexport async function hasMarker(featureDir: string): Promise<boolean> {\n return pathExists(join(featureDir, MARKER_FILENAME));\n}\n\n/**\n * Remove a directory and all its contents.\n */\nexport async function removeDir(dir: string): Promise<void> {\n await remove(dir);\n}\n\n/**\n * Remove a single file.\n */\nexport async function removeFile(filePath: string): Promise<void> {\n await remove(filePath);\n}\n\n// ---------------------------------------------------------------------------\n// Manifest helpers (single .agentbridge per feature-type directory)\n// ---------------------------------------------------------------------------\n\n/**\n * Read the manifest file in a directory. Returns list of managed entries.\n * Entries ending with '/' are folders, others are files.\n */\nexport async function readManifest(dir: string): Promise<string[]> {\n const manifestPath = join(dir, MARKER_FILENAME);\n try {\n const content = await fsReadFile(manifestPath, 'utf-8');\n return content.split('\\n').filter((line) => line.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Check if an entry in the manifest is a folder (ends with /).\n */\nexport function isManifestFolder(entry: string): boolean {\n return entry.endsWith('/');\n}\n\n/**\n * Get the base name from a manifest entry (strips trailing / for folders).\n */\nexport function manifestEntryName(entry: string): string {\n return entry.endsWith('/') ? entry.slice(0, -1) : entry;\n}\n\n/**\n * Write a manifest file listing managed entries.\n */\nexport async function writeManifest(dir: string, entries: string[]): Promise<void> {\n const manifestPath = join(dir, MARKER_FILENAME);\n const content = entries.length > 0 ? entries.join('\\n') + '\\n' : '';\n await outputFile(manifestPath, content);\n}\n\n/**\n * Add an entry to the manifest. Creates manifest if it doesn't exist.\n * Use trailing '/' for folders.\n */\nexport async function addToManifest(dir: string, entry: string): Promise<void> {\n const existing = await readManifest(dir);\n if (!existing.includes(entry)) {\n existing.push(entry);\n await writeManifest(dir, existing);\n }\n}\n\n/**\n * Remove an entry from the manifest.\n * Deletes the manifest file entirely if it becomes empty.\n */\nexport async function removeFromManifest(dir: string, entry: string): Promise<void> {\n const existing = await readManifest(dir);\n const updated = existing.filter((e) => e !== entry);\n if (updated.length !== existing.length) {\n if (updated.length === 0) {\n // Remove manifest file when empty\n await remove(join(dir, MARKER_FILENAME));\n } else {\n await writeManifest(dir, updated);\n }\n }\n}\n\n/**\n * Check if an entry is tracked in the manifest.\n */\nexport async function isInManifest(dir: string, entry: string): Promise<boolean> {\n const entries = await readManifest(dir);\n return entries.includes(entry);\n}\n","import { execFileSync } from 'node:child_process';\nimport { mkdir, readdir, rm, writeFile, access } from 'node:fs/promises';\nimport { join, isAbsolute, resolve } from 'node:path';\nimport {\n type SourceConfig,\n type BridgeConfig,\n bridgeDir,\n sourceDir,\n isRemoteSource,\n} from './config.js';\nimport { dirExists } from './fs.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Marker file written inside every directory Agent Bridge manages under\n * `.agent-bridge/`. Used to gate destructive cleanup so we never delete\n * user-placed content.\n */\nconst SOURCE_MARKER = '.agent-bridge-managed';\n\nfunction git(args: string[], cwd?: string): string {\n return execFileSync('git', args, {\n cwd,\n encoding: 'utf-8',\n stdio: 'pipe',\n }).trim();\n}\n\n/**\n * Branch names must not contain shell metacharacters or leading dashes\n * (which could be mistaken for git flags). Conservative but safe.\n */\nfunction assertSafeBranch(branch: string, sourceName: string): void {\n if (!/^[A-Za-z0-9._/-]+$/.test(branch) || branch.startsWith('-')) {\n throw new Error(\n `Source '${sourceName}': invalid branch name '${branch}'. ` +\n `Branch must match [A-Za-z0-9._/-]+ and not start with '-'.`\n );\n }\n}\n\n/**\n * Reject source URLs that begin with '-' to prevent them being interpreted\n * as CLI flags by git.\n */\nfunction assertSafeSourceUrl(source: string, sourceName: string): void {\n if (source.startsWith('-')) {\n throw new Error(\n `Source '${sourceName}': URL must not start with '-' (got '${source}').`\n );\n }\n}\n\nasync function writeSourceMarker(dest: string): Promise<void> {\n await writeFile(\n join(dest, SOURCE_MARKER),\n 'This directory is managed by agent-bridge. Do not edit manually.\\n',\n 'utf-8'\n );\n}\n\nasync function hasSourceMarker(dir: string): Promise<boolean> {\n try {\n await access(join(dir, SOURCE_MARKER));\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Git operations for a single remote source\n// ---------------------------------------------------------------------------\n\nexport async function cloneSource(\n repoRoot: string,\n source: SourceConfig\n): Promise<void> {\n assertSafeSourceUrl(source.source, source.name);\n if (source.branch) assertSafeBranch(source.branch, source.name);\n\n const dest = sourceDir(repoRoot, source.name);\n\n await mkdir(bridgeDir(repoRoot), { recursive: true });\n\n const args = ['clone', '--depth', '1'];\n if (source.branch) {\n args.push('--single-branch', '--branch', source.branch);\n }\n // '--' terminates option parsing so the URL / dest can never be read as flags.\n args.push('--', source.source, dest);\n\n execFileSync('git', args, { stdio: 'pipe' });\n\n await writeSourceMarker(dest);\n}\n\nexport async function fetchSource(\n repoRoot: string,\n source: SourceConfig\n): Promise<void> {\n assertSafeSourceUrl(source.source, source.name);\n if (source.branch) assertSafeBranch(source.branch, source.name);\n\n const dest = sourceDir(repoRoot, source.name);\n\n git(['fetch', '--prune', 'origin'], dest);\n\n if (source.branch) {\n const currentBranch = git(['rev-parse', '--abbrev-ref', 'HEAD'], dest);\n if (currentBranch !== source.branch) {\n // Branch changed in config — ensure we have the ref then check it out.\n // A shallow --single-branch clone only has one branch, so fetch the\n // new branch explicitly before checkout.\n try {\n git(['fetch', '--depth', '1', 'origin', source.branch], dest);\n } catch {\n // If fetch fails, let checkout surface the real error.\n }\n git(['checkout', source.branch], dest);\n }\n }\n\n try {\n git(['pull', '--ff-only'], dest);\n } catch {\n // pull may fail for tags or detached HEAD — that's okay after fetch\n }\n\n // Refresh marker (in case the directory was restored from backup without it).\n await writeSourceMarker(dest);\n}\n\n// ---------------------------------------------------------------------------\n// Local source resolution\n// ---------------------------------------------------------------------------\n\nexport function resolveLocalSource(\n repoRoot: string,\n source: SourceConfig\n): string {\n const raw = source.source;\n if (isAbsolute(raw)) return raw;\n return resolve(repoRoot, raw);\n}\n\n// ---------------------------------------------------------------------------\n// Resolve the effective filesystem path for a source\n// ---------------------------------------------------------------------------\n\nexport function resolveSourcePath(\n repoRoot: string,\n source: SourceConfig\n): string {\n if (isRemoteSource(source.source)) {\n return sourceDir(repoRoot, source.name);\n }\n return resolveLocalSource(repoRoot, source);\n}\n\n// ---------------------------------------------------------------------------\n// Sync all sources\n// ---------------------------------------------------------------------------\n\nexport interface SourceSyncResult {\n name: string;\n action: 'cloned' | 'updated' | 'local';\n error?: string;\n}\n\nexport async function syncSource(\n repoRoot: string,\n source: SourceConfig\n): Promise<SourceSyncResult> {\n if (!isRemoteSource(source.source)) {\n const resolved = resolveLocalSource(repoRoot, source);\n if (!(await dirExists(resolved))) {\n return {\n name: source.name,\n action: 'local',\n error: `Local source path does not exist: ${resolved}\\n Update the path in .agent-bridge/config.yml or run \"agent-bridge init\" to reconfigure.`,\n };\n }\n return { name: source.name, action: 'local' };\n }\n\n const dest = sourceDir(repoRoot, source.name);\n\n if (await dirExists(dest)) {\n try {\n await fetchSource(repoRoot, source);\n return { name: source.name, action: 'updated' };\n } catch (err) {\n return {\n name: source.name,\n action: 'updated',\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n try {\n await cloneSource(repoRoot, source);\n return { name: source.name, action: 'cloned' };\n } catch (err) {\n return {\n name: source.name,\n action: 'cloned',\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nexport async function syncAllSources(\n repoRoot: string,\n config: BridgeConfig\n): Promise<SourceSyncResult[]> {\n await ensureBridgeGitignore(repoRoot);\n return Promise.all(config.sources.map((source) => syncSource(repoRoot, source)));\n}\n\n// ---------------------------------------------------------------------------\n// Ensure .gitignore in .agent-bridge/\n// ---------------------------------------------------------------------------\n\n/**\n * Write a `.gitignore` inside `.agent-bridge/` that ignores cloned source\n * directories (which are nested git repos) while keeping `config.yml` tracked.\n * Without this, git sees the nested repos as gitlinks/submodules and creates\n * phantom dirty-state changes.\n */\nexport async function ensureBridgeGitignore(repoRoot: string): Promise<void> {\n const bridge = bridgeDir(repoRoot);\n await mkdir(bridge, { recursive: true });\n\n const gitignorePath = join(bridge, '.gitignore');\n const lines = ['# Ignore cloned sources', '*', '!config.yml', '!.gitignore'];\n const content = lines.join('\\n') + '\\n';\n\n await writeFile(gitignorePath, content, 'utf-8');\n}\n\n// ---------------------------------------------------------------------------\n// Remove sources that no longer exist in config\n// ---------------------------------------------------------------------------\n\n/**\n * Remove cloned source directories under `.agent-bridge/` that are no longer\n * referenced in config. Only directories carrying the Agent Bridge marker\n * file are eligible for deletion — user-placed content is always preserved.\n *\n * For backwards compatibility with clones created before the marker existed,\n * directories containing a `.git` folder are also treated as stale.\n */\nexport async function removeStaleSourceDirs(\n repoRoot: string,\n config: BridgeConfig\n): Promise<string[]> {\n const bridge = bridgeDir(repoRoot);\n if (!(await dirExists(bridge))) return [];\n\n const entries = await readdir(bridge, { withFileTypes: true });\n\n const configuredNames = new Set(\n config.sources.filter((s) => isRemoteSource(s.source)).map((s) => s.name)\n );\n\n const removed: string[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (configuredNames.has(entry.name)) continue;\n\n const candidate = join(bridge, entry.name);\n\n if (\n (await hasSourceMarker(candidate)) ||\n (await dirExists(join(candidate, '.git')))\n ) {\n await rm(candidate, { recursive: true, force: true });\n removed.push(entry.name);\n }\n }\n\n return removed;\n}\n","","import pkg from '../../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n","import * as p from '@clack/prompts';\nimport { resolve } from 'node:path';\nimport {\n configExists,\n isRemoteSource,\n loadConfig,\n saveConfig,\n isOptedOut,\n removeOptOutMarker,\n OPT_OUT_MARKER,\n type BridgeConfig,\n type ToolConfig,\n type SourceConfig,\n} from '../lib/config.js';\nimport { findRepoRoot, isInGitRepo, installGitHooks } from '../lib/git.js';\nimport { syncAllSources, ensureBridgeGitignore } from '../lib/sources.js';\nimport { VERSION } from '../lib/version.js';\n\nconst WELL_KNOWN_TOOLS = [\n { value: { name: 'vscode', folder: '.github' }, label: 'VS Code (.github/)' },\n { value: { name: 'cursor', folder: '.cursor' }, label: 'Cursor (.cursor/)' },\n { value: { name: 'claude', folder: '.claude' }, label: 'Claude (.claude/)' },\n { value: { name: 'pi', folder: '.pi' }, label: 'Pi (.pi/)' },\n];\n\nconst WELL_KNOWN_TOOL_MAP: Record<string, ToolConfig> = Object.fromEntries(\n WELL_KNOWN_TOOLS.map((t) => [t.value.name, t.value])\n);\n\nconst CUSTOM_TOOL_SENTINEL: ToolConfig = { name: '__custom__', folder: '__custom__' };\n\nconst DEFAULT_DOMAINS = ['backend', 'frontend', 'shared'];\n\nexport interface InitOptions {\n force?: boolean;\n domains?: string;\n tools?: string;\n source?: string[];\n hooks?: boolean;\n}\n\n/**\n * Derive a short source name from a URL or local path.\n *\n * Examples:\n * https://github.com/org/repo.git → repo\n * git@github.com:org/repo.git → repo\n * file:///tmp/bare.git → bare\n * /path/to/my-folder → my-folder\n */\nexport function deriveSourceName(source: string): string {\n let segment = source;\n\n // SSH: git@host:org/repo.git → org/repo.git\n const sshMatch = segment.match(/^[\\w.-]+@[\\w.-]+:(.+)$/);\n if (sshMatch) segment = sshMatch[1];\n\n // Strip protocol + host for URLs\n try {\n const url = new URL(segment);\n segment = url.pathname;\n } catch {\n // not a URL — keep as-is (local path or already stripped)\n }\n\n // Take the last path component, strip trailing slashes and .git suffix\n const base = segment.replace(/\\/+$/, '').split('/').pop() ?? segment;\n return base.replace(/\\.git$/, '') || 'source';\n}\n\n/**\n * Parse a comma-separated `--tools` argument into ToolConfig[].\n * Accepts well-known names (cursor, vscode, claude) or `name:folder` pairs.\n */\nexport function parseToolsArg(input: string): ToolConfig[] {\n return input.split(',').map((t) => {\n const trimmed = t.trim();\n if (!trimmed) throw new Error('Empty tool name in --tools');\n\n if (WELL_KNOWN_TOOL_MAP[trimmed]) return WELL_KNOWN_TOOL_MAP[trimmed];\n\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx > 0) {\n return { name: trimmed.slice(0, colonIdx), folder: trimmed.slice(colonIdx + 1) };\n }\n\n throw new Error(\n `Unknown tool \"${trimmed}\". Use a known name (${Object.keys(WELL_KNOWN_TOOL_MAP).join(', ')}) or name:folder format.`\n );\n });\n}\n\n/**\n * Parse a single `--source` argument into a SourceConfig.\n * Supports `#branch` suffix for remote sources.\n */\nexport function parseSourceArg(input: string, repoRoot: string): SourceConfig {\n let source = input.trim();\n let branch: string | undefined;\n\n const hashIdx = source.lastIndexOf('#');\n if (hashIdx > 0) {\n branch = source.slice(hashIdx + 1);\n source = source.slice(0, hashIdx);\n }\n\n if (!source) throw new Error('Empty source in --source');\n\n const name = deriveSourceName(source);\n const entry: SourceConfig = { name, source };\n\n if (!isRemoteSource(entry.source)) {\n entry.source = resolve(repoRoot, entry.source);\n }\n\n if (branch) {\n entry.branch = branch;\n }\n\n return entry;\n}\n\nexport async function initCommand(\n cwd?: string,\n opts?: InitOptions\n): Promise<void> {\n const repoRoot = cwd ?? findRepoRoot();\n\n // Respect an opt-out tombstone so a postinstall guard doesn't reinstall.\n // `--force` clears it (deliberate re-opt-in).\n if (await isOptedOut(repoRoot)) {\n if (opts?.force) {\n await removeOptOutMarker(repoRoot);\n } else {\n p.log.warn(\n `${OPT_OUT_MARKER} present — Agent Bridge is opted out. ` +\n `Skipping init. Delete the file or run with --force to re-enable.`\n );\n return;\n }\n }\n\n const hasToolsArg = !!opts?.tools;\n const hasSourceArg = !!(opts?.source && opts.source.length > 0);\n\n // Require both --tools and --source for non-interactive mode\n if (hasToolsArg !== hasSourceArg) {\n p.log.error('Both --tools and --source are required for non-interactive init.');\n process.exit(1);\n }\n\n // --- Non-interactive mode ---\n if (hasToolsArg && hasSourceArg) {\n const domains = opts!.domains\n ? opts!.domains.split(',').map((d) => d.trim()).filter(Boolean)\n : [...DEFAULT_DOMAINS];\n\n const tools = parseToolsArg(opts!.tools!);\n const sources = opts!.source!.map((s) => parseSourceArg(s, repoRoot));\n\n // Check duplicate source names\n const seen = new Set<string>();\n for (const s of sources) {\n if (seen.has(s.name)) {\n throw new Error(`Duplicate source name \"${s.name}\" derived from --source arguments`);\n }\n seen.add(s.name);\n }\n\n const config: BridgeConfig = {\n version: VERSION,\n domains,\n tools,\n sources,\n };\n\n await saveConfig(repoRoot, config);\n await ensureBridgeGitignore(repoRoot);\n p.log.success('Saved .agent-bridge/config.yml');\n\n // Fetch remote sources\n const spinner = p.spinner();\n spinner.start('Fetching remote sources…');\n const results = await syncAllSources(repoRoot, config);\n const fetchErrors = results.filter((r) => r.error);\n if (fetchErrors.length > 0) {\n spinner.stop('Some sources failed');\n for (const err of fetchErrors) {\n p.log.error(`${err.name}: ${err.error}`);\n }\n } else {\n spinner.stop('All sources ready');\n }\n\n // Git hooks (--hooks flag)\n if (opts!.hooks && isInGitRepo(repoRoot)) {\n const hookResult = await installGitHooks(repoRoot, opts!.force === true);\n if (hookResult.installed.length > 0) {\n p.log.success(`Installed git hooks: ${hookResult.installed.join(', ')}`);\n }\n if (hookResult.skipped.length > 0) {\n p.log.warn(`Skipped hooks: ${hookResult.skipped.join(', ')}`);\n }\n if (hookResult.errors.length > 0) {\n for (const e of hookResult.errors) {\n p.log.error(`Hook ${e.hook}: ${e.error}`);\n }\n }\n }\n\n p.outro('Done! Run `agent-bridge sync` to sync features.');\n return;\n }\n\n // --- Interactive mode ---\n\n p.intro('Welcome to Agent Bridge — Project Setup');\n\n if (await configExists(repoRoot)) {\n const existing = await loadConfig(repoRoot);\n p.log.info(\n `Config already exists with ${existing.sources?.length ?? 0} source(s). Re-running will overwrite.`\n );\n }\n\n // --- Domains ---\n const domainsInput = await p.text({\n message: 'Domains (comma-separated)',\n placeholder: DEFAULT_DOMAINS.join(', '),\n defaultValue: DEFAULT_DOMAINS.join(', '),\n validate: (v) => {\n if (!v.trim()) return 'At least one domain is required';\n },\n });\n if (p.isCancel(domainsInput)) {\n p.cancel('Setup cancelled.');\n process.exit(1);\n }\n\n const domains = domainsInput\n .split(',')\n .map((d) => d.trim())\n .filter(Boolean);\n\n // --- Tools ---\n const selectedTools = await p.multiselect({\n message: 'Which tools (IDEs) should receive Agent Bridge files?',\n options: [\n ...WELL_KNOWN_TOOLS,\n { value: CUSTOM_TOOL_SENTINEL, label: 'Other (add custom tool)' },\n ],\n required: true,\n });\n if (p.isCancel(selectedTools)) {\n p.cancel('Setup cancelled.');\n process.exit(1);\n }\n\n const tools: ToolConfig[] = (selectedTools as ToolConfig[]).filter(\n (t) => t.name !== '__custom__'\n );\n\n // If the user selected the custom option, prompt for custom tools\n if ((selectedTools as ToolConfig[]).some((t) => t.name === '__custom__')) {\n let addingCustom = true;\n while (addingCustom) {\n const name = await p.text({\n message: 'Custom tool name (used for <tool>-- prefix matching)',\n placeholder: 'windsurf',\n defaultValue: '',\n validate: (v) => {\n if (!v.trim()) return 'Tool name cannot be empty';\n if (tools.some((t) => t.name === v.trim())) return 'Tool name already used';\n },\n });\n if (p.isCancel(name)) break;\n\n const folder = await p.text({\n message: `Target folder for \"${name}\"`,\n placeholder: `.${name}`,\n defaultValue: '',\n validate: (v) => {\n if (!v.trim()) return 'Folder cannot be empty';\n if (tools.some((t) => t.folder === v.trim())) return 'Folder already used by another tool';\n },\n });\n if (p.isCancel(folder)) break;\n\n tools.push({ name: name.trim(), folder: folder.trim() });\n\n const addMore = await p.confirm({\n message: 'Add another custom tool?',\n initialValue: false,\n });\n if (p.isCancel(addMore) || !addMore) {\n addingCustom = false;\n }\n }\n\n if (tools.length === 0) {\n p.cancel('At least one tool is required.');\n process.exit(1);\n }\n }\n\n // --- Sources ---\n const sources: SourceConfig[] = [];\n\n const addSource = async (): Promise<boolean> => {\n const source = await p.text({\n message: 'Source URL or local path',\n placeholder: 'https://github.com/org/repo.git',\n defaultValue: '',\n validate: (v) => {\n if (!v.trim()) return 'Source URL/path cannot be empty';\n const derived = deriveSourceName(v.trim());\n if (sources.some((s) => s.name === derived))\n return `Source name \"${derived}\" (derived from URL) already used`;\n },\n });\n if (p.isCancel(source)) return false;\n\n const name = deriveSourceName(source.trim());\n const entry: SourceConfig = { name, source: source.trim() };\n\n // Resolve local paths to absolute\n if (!isRemoteSource(entry.source)) {\n entry.source = resolve(repoRoot, entry.source);\n }\n\n // Ask for branch if remote\n if (isRemoteSource(entry.source)) {\n const branch = await p.text({\n message: 'Branch (leave empty for remote default)',\n placeholder: 'main',\n defaultValue: '',\n });\n if (p.isCancel(branch)) return false;\n if (branch.trim()) {\n entry.branch = branch.trim();\n }\n }\n\n sources.push(entry);\n return true;\n };\n\n p.log.info('Add at least one source.');\n let addingSource = true;\n while (addingSource) {\n const added = await addSource();\n if (!added) {\n if (sources.length === 0) {\n p.cancel('At least one source is required.');\n process.exit(1);\n }\n break;\n }\n\n const addMore = await p.confirm({\n message: 'Add another source?',\n initialValue: false,\n });\n if (p.isCancel(addMore) || !addMore) {\n addingSource = false;\n }\n }\n\n const config: BridgeConfig = {\n version: VERSION,\n domains,\n tools,\n sources,\n };\n\n await saveConfig(repoRoot, config);\n await ensureBridgeGitignore(repoRoot);\n p.log.success('Saved .agent-bridge/config.yml');\n\n // Clone remote sources\n const s = p.spinner();\n s.start('Fetching remote sources…');\n const results = await syncAllSources(repoRoot, config);\n const errors = results.filter((r) => r.error);\n if (errors.length > 0) {\n s.stop('Some sources failed');\n for (const err of errors) {\n p.log.error(`${err.name}: ${err.error}`);\n }\n } else {\n s.stop('All sources ready');\n }\n\n // --- Git Hooks ---\n if (isInGitRepo(repoRoot)) {\n const installHooks = await p.confirm({\n message: 'Install git hooks to auto-sync on checkout/merge?',\n initialValue: false,\n });\n\n if (!p.isCancel(installHooks) && installHooks) {\n const hookResult = await installGitHooks(repoRoot, opts?.force === true);\n \n if (hookResult.installed.length > 0) {\n p.log.success(`Installed git hooks: ${hookResult.installed.join(', ')}`);\n }\n \n if (hookResult.skipped.length > 0) {\n p.log.warn(\n `Skipped hooks (existing non-Agent-Bridge hooks): ${hookResult.skipped.join(', ')}`\n );\n p.log.info('Re-run `agent-bridge init --force` to overwrite, or integrate manually.');\n }\n \n if (hookResult.errors.length > 0) {\n for (const err of hookResult.errors) {\n p.log.error(`Hook ${err.hook}: ${err.error}`);\n }\n }\n }\n }\n\n p.outro('Done! Run `agent-bridge sync` to sync features.');\n}\n","import { type BridgeConfig, saveConfig, loadConfig } from '../config.js';\nimport { VERSION } from '../version.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A migration function receives the repo root and current config,\n * and returns the (possibly modified) config. It may also perform\n * filesystem operations (rename dirs, update files, etc.).\n */\nexport type MigrationFn = (\n repoRoot: string,\n config: BridgeConfig\n) => Promise<BridgeConfig>;\n\nexport interface Migration {\n /** Semver version this migration upgrades TO (e.g. \"0.6.0\"). */\n version: string;\n /** Human-readable description shown when running. */\n description: string;\n /** The migration logic. */\n migrate: MigrationFn;\n}\n\nexport interface MigrationResult {\n fromVersion: string;\n toVersion: string;\n applied: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Registry — add new migrations here in semver order\n// ---------------------------------------------------------------------------\n\nexport const migrations: Migration[] = [\n // Example (uncomment when first real migration is needed):\n // {\n // version: '0.6.0',\n // description: 'rename domains key',\n // migrate: async (_repoRoot, config) => {\n // // transform config...\n // return config;\n // },\n // },\n];\n\n// ---------------------------------------------------------------------------\n// Semver helpers (minimal — no external dep needed)\n// ---------------------------------------------------------------------------\n\n/** Parse \"1.2.3\" or \"1.2.3-beta.1\" into [major, minor, patch]. */\nexport function parseSemver(version: string): [number, number, number] {\n const clean = version.replace(/^v/, '').split('-')[0];\n const parts = clean.split('.').map(Number);\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];\n}\n\n/** Returns -1 | 0 | 1 comparing a to b (ignores prerelease). */\nexport function compareSemver(a: string, b: string): number {\n const [aMaj, aMin, aPat] = parseSemver(a);\n const [bMaj, bMin, bPat] = parseSemver(b);\n\n if (aMaj !== bMaj) return aMaj < bMaj ? -1 : 1;\n if (aMin !== bMin) return aMin < bMin ? -1 : 1;\n if (aPat !== bPat) return aPat < bPat ? -1 : 1;\n return 0;\n}\n\n// ---------------------------------------------------------------------------\n// Migration runner\n// ---------------------------------------------------------------------------\n\n/**\n * Find migrations that should run when upgrading from `fromVersion` to\n * `toVersion`. Returns them sorted in ascending version order.\n */\nexport function pendingMigrations(\n fromVersion: string,\n toVersion: string\n): Migration[] {\n return migrations\n .filter(\n (m) =>\n compareSemver(m.version, fromVersion) > 0 &&\n compareSemver(m.version, toVersion) <= 0\n )\n .sort((a, b) => compareSemver(a.version, b.version));\n}\n\n/**\n * Run all pending migrations between the config's version and the\n * currently installed VERSION. Updates and saves the config afterwards.\n *\n * Returns null if no migration was needed.\n */\nexport async function runMigrations(\n repoRoot: string\n): Promise<MigrationResult | null> {\n let config = await loadConfig(repoRoot);\n const configVersion = config.version ?? '0.0.0';\n\n const cmp = compareSemver(configVersion, VERSION);\n\n // Already current\n if (cmp === 0) return null;\n\n // Config is newer than installed package (downgrade)\n if (cmp > 0) return null;\n\n // Config is older — find and run migrations\n const pending = pendingMigrations(configVersion, VERSION);\n const applied: string[] = [];\n\n for (const migration of pending) {\n config = await migration.migrate(repoRoot, config);\n applied.push(migration.version);\n }\n\n // Always update the version, even if no migrations ran\n // (e.g. patch bump with no structural changes)\n config = { ...config, version: VERSION };\n await saveConfig(repoRoot, config);\n\n return {\n fromVersion: configVersion,\n toVersion: VERSION,\n applied,\n };\n}\n","import { readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { BridgeConfig, SourceConfig } from './config.js';\nimport { dirExists, fileExists } from './fs.js';\nimport { resolveSourcePath } from './sources.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nconst TOOL_PREFIX_SEPARATOR = '--';\n\nexport interface Feature {\n name: string;\n /** Raw feature-type directory name (may contain tool prefix) */\n type: string;\n /** Display type with tool prefix stripped (used for destination dir) */\n displayType: string;\n source: string;\n domain: string;\n /** Absolute path to the feature (directory or file) */\n absolutePath: string;\n /** Tool prefix if present (e.g. \"cursor\" from \"cursor--instructions\") */\n toolPrefix?: string;\n /** True if feature is a single file, false if a directory */\n isFile: boolean;\n}\n\nexport interface DuplicateConflict {\n name: string;\n type: string;\n paths: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport { dirExists } from './fs.js';\n\n\nexport function parseToolPrefix(name: string): {\n toolPrefix?: string;\n baseName: string;\n} {\n const idx = name.indexOf(TOOL_PREFIX_SEPARATOR);\n if (idx > 0) {\n return {\n toolPrefix: name.substring(0, idx),\n baseName: name.substring(idx + TOOL_PREFIX_SEPARATOR.length),\n };\n }\n return { baseName: name };\n}\n\nexport function featureMatchesTool(\n feature: Feature,\n toolName: string\n): boolean {\n if (!feature.toolPrefix) return true;\n return feature.toolPrefix === toolName;\n}\n\nexport function featureName(feature: Feature): string {\n if (feature.toolPrefix) {\n return parseToolPrefix(feature.name).baseName;\n }\n return feature.name;\n}\n\n/** @deprecated Use featureName instead */\n/** @deprecated Use featureName directly */\nexport const syncName = featureName;\n\n// ---------------------------------------------------------------------------\n// Discovery\n// ---------------------------------------------------------------------------\n\n/**\n * Discover all feature types across all sources and domains.\n */\nexport async function discoverFeatureTypes(\n repoRoot: string,\n config: BridgeConfig\n): Promise<string[]> {\n const types = new Set<string>();\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n for (const domain of config.domains) {\n const domainDir = join(srcPath, domain);\n if (!(await dirExists(domainDir))) continue;\n\n const entries = await readdir(domainDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n types.add(entry.name);\n }\n }\n }\n }\n\n return [...types].sort();\n}\n\n/**\n * Scan all features across sources × domains × feature types.\n *\n * Structure: `<source-path>/<domain>/<feature-type>/<feature>/` (folder-based)\n * or `<source-path>/<domain>/<feature-type>/<feature.ext>` (file-based)\n */\nexport async function scanFeatures(\n repoRoot: string,\n config: BridgeConfig,\n featureTypes: string[]\n): Promise<Feature[]> {\n const features: Feature[] = [];\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n\n for (const domain of config.domains) {\n for (const ft of featureTypes) {\n const { toolPrefix: typeToolPrefix, baseName: baseType } =\n parseToolPrefix(ft);\n const ftDir = join(srcPath, domain, ft);\n\n if (!(await dirExists(ftDir))) continue;\n\n const entries = await readdir(ftDir, { withFileTypes: true });\n for (const entry of entries) {\n const isFile = entry.isFile();\n const isDir = entry.isDirectory();\n if (!isFile && !isDir) continue;\n\n const { toolPrefix: itemToolPrefix } = parseToolPrefix(entry.name);\n const toolPrefix = itemToolPrefix ?? typeToolPrefix;\n\n features.push({\n name: entry.name,\n type: ft,\n displayType: baseType,\n source: source.name,\n domain,\n absolutePath: join(ftDir, entry.name),\n toolPrefix,\n isFile,\n });\n }\n }\n }\n }\n\n return features;\n}\n\n// ---------------------------------------------------------------------------\n// Duplicate detection\n// ---------------------------------------------------------------------------\n\nexport function detectDuplicates(features: Feature[]): DuplicateConflict[] {\n const byKey = new Map<string, Feature[]>();\n\n for (const f of features) {\n const linkName = featureName(f);\n const key = `${f.displayType}/${linkName}`;\n const group = byKey.get(key) ?? [];\n group.push(f);\n byKey.set(key, group);\n }\n\n const conflicts: DuplicateConflict[] = [];\n for (const [, group] of byKey) {\n if (group.length > 1) {\n conflicts.push({\n name: featureName(group[0]),\n type: group[0].type,\n paths: group.map((f) => f.absolutePath),\n });\n }\n }\n\n return conflicts;\n}\n\n// ---------------------------------------------------------------------------\n// Root file scanning\n// ---------------------------------------------------------------------------\n\n/**\n * Well-known root files that live at the domain root and should be synced to the\n * workspace root. When a source contains `<domain>/AGENTS.md` (etc.), Agent Bridge\n * copies it to the project root.\n */\nexport const ROOT_FILES = ['AGENTS.md', 'CLAUDE.md', 'SYSTEM.md'] as const;\nexport type RootFileName = (typeof ROOT_FILES)[number];\n\nexport interface RootFile {\n /** The well-known filename (e.g. \"AGENTS.md\") */\n fileName: RootFileName;\n /** Source that provides this file */\n source: string;\n /** Domain where it was found */\n domain: string;\n /** Absolute path to the source file */\n absolutePath: string;\n}\n\nexport interface RootFileDuplicate {\n fileName: RootFileName;\n paths: string[];\n}\n\n/**\n * Scan all sources × domains for well-known root files.\n * Returns one entry per found file.\n */\nexport async function scanRootFiles(\n repoRoot: string,\n config: BridgeConfig\n): Promise<RootFile[]> {\n const found: RootFile[] = [];\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n for (const domain of config.domains) {\n for (const fileName of ROOT_FILES) {\n const filePath = join(srcPath, domain, fileName);\n if (await fileExists(filePath)) {\n found.push({\n fileName,\n source: source.name,\n domain,\n absolutePath: filePath,\n });\n }\n }\n }\n }\n\n return found;\n}\n\n/**\n * Detect duplicate root files (same filename provided by multiple sources/domains).\n */\nexport function detectRootFileDuplicates(\n rootFiles: RootFile[]\n): RootFileDuplicate[] {\n const byName = new Map<RootFileName, RootFile[]>();\n for (const rf of rootFiles) {\n const group = byName.get(rf.fileName) ?? [];\n group.push(rf);\n byName.set(rf.fileName, group);\n }\n\n const duplicates: RootFileDuplicate[] = [];\n for (const [fileName, group] of byName) {\n if (group.length > 1) {\n duplicates.push({\n fileName,\n paths: group.map((rf) => rf.absolutePath),\n });\n }\n }\n\n return duplicates;\n}\n\n// ---------------------------------------------------------------------------\n// Tool root file scanning (tool-prefixed flat files at domain level)\n// ---------------------------------------------------------------------------\n\nexport interface ToolRootEntry {\n /** The tool name this entry targets (e.g. \"pi\") */\n toolName: string;\n /** Destination filename (e.g. \"settings.json\" from \"cursor--settings.json\") */\n name: string;\n /** Source that provides this entry */\n source: string;\n /** Domain where it was found */\n domain: string;\n /** Absolute path to the source file */\n absolutePath: string;\n}\n\nexport interface ToolRootDuplicate {\n /** The tool name */\n toolName: string;\n /** Name of the duplicate entry */\n name: string;\n /** Paths where the duplicates were found */\n paths: string[];\n}\n\n/**\n * Scan all sources × domains for tool-prefixed flat files at the domain level.\n * A file named `cursor--settings.json` targets the tool \"cursor\" with\n * destination filename \"settings.json\".\n */\nexport async function scanToolRootEntries(\n repoRoot: string,\n config: BridgeConfig\n): Promise<ToolRootEntry[]> {\n const entries: ToolRootEntry[] = [];\n const toolNames = new Set(config.tools.map((t) => t.name));\n\n for (const source of config.sources) {\n const srcPath = resolveSourcePath(repoRoot, source);\n for (const domain of config.domains) {\n const domainDir = join(srcPath, domain);\n if (!(await dirExists(domainDir))) continue;\n\n const domainEntries = await readdir(domainDir, { withFileTypes: true });\n for (const entry of domainEntries) {\n if (!entry.isFile()) continue;\n\n const { toolPrefix, baseName } = parseToolPrefix(entry.name);\n if (!toolPrefix || !toolNames.has(toolPrefix)) continue;\n\n entries.push({\n toolName: toolPrefix,\n name: baseName,\n source: source.name,\n domain,\n absolutePath: join(domainDir, entry.name),\n });\n }\n }\n }\n\n return entries;\n}\n\n/**\n * Detect duplicate tool root entries (same tool + name from multiple sources/domains).\n */\nexport function detectToolRootDuplicates(\n entries: ToolRootEntry[]\n): ToolRootDuplicate[] {\n const byKey = new Map<string, ToolRootEntry[]>();\n for (const entry of entries) {\n const key = `${entry.toolName}/${entry.name}`;\n const group = byKey.get(key) ?? [];\n group.push(entry);\n byKey.set(key, group);\n }\n\n const duplicates: ToolRootDuplicate[] = [];\n for (const [, group] of byKey) {\n if (group.length > 1) {\n duplicates.push({\n toolName: group[0].toolName,\n name: group[0].name,\n paths: group.map((e) => e.absolutePath),\n });\n }\n }\n\n return duplicates;\n}\n","import { readdir, mkdir, rmdir, copyFile, readFile, writeFile } from 'node:fs/promises';\nimport { join, dirname, basename } from 'node:path';\nimport type { BridgeConfig } from './config.js';\nimport {\n dirExists,\n fileExists,\n copyDirContents,\n removeDir,\n removeFile,\n readManifest,\n writeManifest,\n addToManifest,\n removeFromManifest,\n isManifestFolder,\n manifestEntryName,\n MARKER_FILENAME,\n} from './fs.js';\nimport {\n type Feature,\n type RootFile,\n type ToolRootEntry,\n ROOT_FILES,\n featureMatchesTool,\n featureName,\n} from './manifest.js';\n\n// ---------------------------------------------------------------------------\n// Feature path helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Compute the destination path for a feature inside a tool's folder.\n * For folder-based features: returns the folder path.\n * For file-based features: returns the file path.\n */\nexport function featureDestPath(\n repoRoot: string,\n toolFolder: string,\n featureType: string,\n featureName: string\n): string {\n return join(repoRoot, toolFolder, featureType, featureName);\n}\n\n// ---------------------------------------------------------------------------\n// Conflict detection\n// ---------------------------------------------------------------------------\n\n/**\n * Check whether a folder-based feature destination conflicts with existing user content.\n * Returns `true` when the folder exists and is not tracked in the manifest.\n */\nexport async function checkFolderConflict(featureTypeDir: string, folderName: string): Promise<boolean> {\n const destPath = join(featureTypeDir, folderName);\n if (!(await dirExists(destPath))) return false;\n \n const manifest = await readManifest(featureTypeDir);\n return !manifest.includes(folderName + '/');\n}\n\n/**\n * Check whether a file-based feature destination conflicts with existing user content.\n * Returns `true` when the file exists and is not tracked in the manifest.\n */\nexport async function checkFileConflict(featureTypeDir: string, fileName: string): Promise<boolean> {\n const filePath = join(featureTypeDir, fileName);\n if (!(await fileExists(filePath))) return false;\n \n const manifest = await readManifest(featureTypeDir);\n return !manifest.includes(fileName);\n}\n\n/**\n * Check whether a feature destination conflicts with existing user content.\n * Handles both folder-based and file-based features.\n */\nexport async function checkPathConflict(\n featureTypeDir: string,\n featureName: string,\n isFile: boolean\n): Promise<boolean> {\n if (isFile) {\n return checkFileConflict(featureTypeDir, featureName);\n } else {\n return checkFolderConflict(featureTypeDir, featureName);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Single feature sync\n// ---------------------------------------------------------------------------\n\n/**\n * Sync a folder-based feature: clear destination, copy files, add to manifest.\n */\nexport async function syncFolderFeature(\n sourcePath: string,\n featureTypeDir: string,\n folderName: string\n): Promise<'created' | 'updated'> {\n const destPath = join(featureTypeDir, folderName);\n const existed = await dirExists(destPath);\n\n if (existed) {\n await removeDir(destPath);\n }\n\n await mkdir(destPath, { recursive: true });\n await copyDirContents(sourcePath, destPath);\n await addToManifest(featureTypeDir, folderName + '/');\n\n return existed ? 'updated' : 'created';\n}\n\n/**\n * Sync a file-based feature: copy file, add to manifest.\n */\nexport async function syncFileFeature(\n sourcePath: string,\n featureTypeDir: string,\n fileName: string\n): Promise<'created' | 'updated'> {\n const destPath = join(featureTypeDir, fileName);\n const existed = await fileExists(destPath);\n\n await mkdir(featureTypeDir, { recursive: true });\n await copyFile(sourcePath, destPath);\n await addToManifest(featureTypeDir, fileName);\n\n return existed ? 'updated' : 'created';\n}\n\n/**\n * Sync a feature (folder or file based).\n * @deprecated Use syncFolderFeature or syncFileFeature directly\n */\nexport async function syncFeature(\n sourcePath: string,\n destPath: string\n): Promise<'created' | 'updated'> {\n const featureTypeDir = dirname(destPath);\n const folderName = basename(destPath);\n return syncFolderFeature(sourcePath, featureTypeDir, folderName);\n}\n\n// ---------------------------------------------------------------------------\n// Empty directory cleanup\n// ---------------------------------------------------------------------------\n\nexport async function removeEmptyParents(\n dirPath: string,\n stopAt: string\n): Promise<void> {\n let current = dirPath;\n while (current !== stopAt && current.startsWith(stopAt)) {\n try {\n const entries = await readdir(current);\n if (entries.length > 0) break;\n await rmdir(current);\n current = dirname(current);\n } catch {\n break;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Collect all managed entries from manifests\n// ---------------------------------------------------------------------------\n\ninterface ManagedEntry {\n /** Full path to the file or folder */\n path: string;\n /** Directory containing the manifest (feature-type dir) */\n manifestDir: string;\n /** Entry as it appears in manifest (with trailing / for folders) */\n manifestEntry: string;\n /** True if folder, false if file */\n isFolder: boolean;\n}\n\n/**\n * Recursively collect all managed entries (files and folders) from manifests.\n * Scans for .agentbridge files and reads their contents.\n */\nasync function collectManagedEntries(dir: string): Promise<ManagedEntry[]> {\n if (!(await dirExists(dir))) return [];\n\n const result: ManagedEntry[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n\n // Check if this directory has a manifest\n const manifestEntries = await readManifest(dir);\n for (const entry of manifestEntries) {\n const isFolder = isManifestFolder(entry);\n const name = manifestEntryName(entry);\n result.push({\n path: join(dir, name),\n manifestDir: dir,\n manifestEntry: entry,\n isFolder,\n });\n }\n\n // Recurse into subdirectories (to find manifests in nested feature-type dirs)\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (entry.name === MARKER_FILENAME) continue;\n \n const fullPath = join(dir, entry.name);\n const sub = await collectManagedEntries(fullPath);\n result.push(...sub);\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Reconciliation (high-level)\n// ---------------------------------------------------------------------------\n\nexport interface ReconcileResult {\n added: number;\n updated: number;\n removed: number;\n errors: Array<{ path: string; error: string }>;\n}\n\ninterface ExpectedFeature {\n sourcePath: string;\n featureTypeDir: string;\n name: string;\n manifestEntry: string;\n isFile: boolean;\n}\n\nexport async function reconcileFeatures(\n repoRoot: string,\n config: BridgeConfig,\n features: Feature[]\n): Promise<ReconcileResult> {\n let added = 0;\n let updated = 0;\n let removed = 0;\n const errors: Array<{ path: string; error: string }> = [];\n\n // Phase 1: Compute all expected features\n // Key = full destination path\n const expectedFeatures = new Map<string, ExpectedFeature>();\n\n for (const tool of config.tools) {\n for (const feature of features) {\n if (!featureMatchesTool(feature, tool.name)) continue;\n\n const name = featureName(feature);\n const featureTypeDir = join(repoRoot, tool.folder, feature.displayType);\n const destPath = join(featureTypeDir, name);\n const manifestEntry = feature.isFile ? name : name + '/';\n\n expectedFeatures.set(destPath, {\n sourcePath: feature.absolutePath,\n featureTypeDir,\n name,\n manifestEntry,\n isFile: feature.isFile,\n });\n }\n }\n\n // Phase 2: Remove orphaned managed entries (previously synced but no longer expected)\n for (const tool of config.tools) {\n const toolDir = join(repoRoot, tool.folder);\n const managedEntries = await collectManagedEntries(toolDir);\n\n for (const entry of managedEntries) {\n if (expectedFeatures.has(entry.path)) continue;\n\n try {\n if (entry.isFolder) {\n await removeDir(entry.path);\n } else {\n await removeFile(entry.path);\n }\n await removeFromManifest(entry.manifestDir, entry.manifestEntry);\n await removeEmptyParents(entry.manifestDir, toolDir);\n removed++;\n } catch (err) {\n errors.push({\n path: entry.path,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n\n // Phase 3: Create / update features (isolate failures so one bad feature\n // doesn't abort the entire sync).\n for (const [destPath, expected] of expectedFeatures) {\n try {\n const result = expected.isFile\n ? await syncFileFeature(\n expected.sourcePath,\n expected.featureTypeDir,\n expected.name\n )\n : await syncFolderFeature(\n expected.sourcePath,\n expected.featureTypeDir,\n expected.name\n );\n\n if (result === 'created') added++;\n else if (result === 'updated') updated++;\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { added, updated, removed, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Root file sync\n// ---------------------------------------------------------------------------\n\nconst ROOT_FILE_MARKER = '<!-- Managed by Agent Bridge -->';\n\n/**\n * Check if a root file at `destPath` is managed by Agent Bridge.\n * A file is managed if it starts with the marker comment.\n */\nexport async function isRootFileManaged(destPath: string): Promise<boolean> {\n if (!(await fileExists(destPath))) return false;\n const content = await readFile(destPath, 'utf-8');\n return content.startsWith(ROOT_FILE_MARKER);\n}\n\nexport interface RootFileSyncResult {\n synced: string[];\n removed: string[];\n errors: Array<{ path: string; error: string }>;\n}\n\n/**\n * Sync root files: copy source root files to the workspace root, and clean up\n * managed root files that are no longer provided by any source.\n */\nexport async function syncRootFiles(\n repoRoot: string,\n rootFiles: RootFile[]\n): Promise<RootFileSyncResult> {\n const synced: string[] = [];\n const removed: string[] = [];\n const errors: Array<{ path: string; error: string }> = [];\n\n // Build map: fileName → rootFile (already deduplicated by caller)\n const expected = new Map<string, RootFile>();\n for (const rf of rootFiles) {\n expected.set(rf.fileName, rf);\n }\n\n // Sync expected root files\n for (const [fileName, rf] of expected) {\n const destPath = join(repoRoot, fileName);\n try {\n // If file exists and is NOT managed by us, skip (don't overwrite user files)\n if (await fileExists(destPath)) {\n if (!(await isRootFileManaged(destPath))) {\n continue;\n }\n }\n\n const sourceContent = await readFile(rf.absolutePath, 'utf-8');\n const managedContent = ROOT_FILE_MARKER + '\\n' + sourceContent;\n await mkdir(dirname(destPath), { recursive: true });\n await writeFile(destPath, managedContent, 'utf-8');\n synced.push(fileName);\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n // Remove managed root files no longer provided by any source\n for (const fileName of ROOT_FILES) {\n if (expected.has(fileName)) continue;\n const destPath = join(repoRoot, fileName);\n try {\n if (await isRootFileManaged(destPath)) {\n await removeFile(destPath);\n removed.push(fileName);\n }\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { synced, removed, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Tool root entry sync (tool-prefixed flat files)\n// ---------------------------------------------------------------------------\n\nexport interface ToolRootSyncResult {\n added: number;\n updated: number;\n removed: number;\n errors: Array<{ path: string; error: string }>;\n}\n\n/**\n * Reconcile tool root entries: sync expected entries and remove orphans.\n * Tool-prefixed flat files (e.g. `cursor--settings.json`) are copied directly\n * into the tool's root folder (e.g. `.cursor/settings.json`).\n */\nexport async function reconcileToolRootEntries(\n repoRoot: string,\n config: BridgeConfig,\n entries: ToolRootEntry[]\n): Promise<ToolRootSyncResult> {\n let added = 0;\n let updated = 0;\n let removed = 0;\n const errors: Array<{ path: string; error: string }> = [];\n\n // Build tool name → folder mapping\n const toolFolders = new Map<string, string>();\n for (const tool of config.tools) {\n toolFolders.set(tool.name, tool.folder);\n }\n\n // Key = destination path, value = entry info\n const expectedEntries = new Map<\n string,\n { sourcePath: string; name: string; toolDir: string }\n >();\n\n for (const entry of entries) {\n const folder = toolFolders.get(entry.toolName);\n if (!folder) continue;\n\n const toolDir = join(repoRoot, folder);\n const destPath = join(toolDir, entry.name);\n\n expectedEntries.set(destPath, {\n sourcePath: entry.absolutePath,\n name: entry.name,\n toolDir,\n });\n }\n\n // Remove orphaned managed entries from tool root directories\n for (const tool of config.tools) {\n const toolDir = join(repoRoot, tool.folder);\n const manifest = await readManifest(toolDir);\n\n for (const manifestEntry of manifest) {\n const isFolder = isManifestFolder(manifestEntry);\n const name = manifestEntryName(manifestEntry);\n const destPath = join(toolDir, name);\n\n if (expectedEntries.has(destPath)) continue;\n\n try {\n if (isFolder) {\n await removeDir(destPath);\n } else {\n await removeFile(destPath);\n }\n await removeFromManifest(toolDir, manifestEntry);\n removed++;\n } catch (err) {\n errors.push({\n path: destPath,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n\n // Sync expected entries (always files)\n for (const [, expected] of expectedEntries) {\n try {\n const result = await syncFileFeature(\n expected.sourcePath,\n expected.toolDir,\n expected.name\n );\n\n if (result === 'created') added++;\n else if (result === 'updated') updated++;\n } catch (err) {\n errors.push({\n path: join(expected.toolDir, expected.name),\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { added, updated, removed, errors };\n}\n","import * as p from '@clack/prompts';\nimport { loadConfig, isOptedOut, OPT_OUT_MARKER } from '../lib/config.js';\nimport { findRepoRoot } from '../lib/git.js';\nimport { runMigrations } from '../lib/migrations/index.js';\nimport {\n discoverFeatureTypes,\n scanFeatures,\n detectDuplicates,\n scanRootFiles,\n detectRootFileDuplicates,\n scanToolRootEntries,\n detectToolRootDuplicates,\n featureMatchesTool,\n featureName,\n} from '../lib/manifest.js';\nimport {\n featureDestPath,\n checkPathConflict,\n reconcileFeatures,\n syncRootFiles,\n reconcileToolRootEntries,\n} from '../lib/sync.js';\nimport { syncAllSources, removeStaleSourceDirs } from '../lib/sources.js';\nimport { join } from 'node:path';\n\nexport async function syncCommand(cwd?: string, _opts?: unknown): Promise<void> {\n const repoRoot = cwd ?? findRepoRoot();\n\n p.intro('Agent Bridge Sync');\n\n // Respect an opt-out tombstone so a postinstall guard doesn't re-sync.\n if (await isOptedOut(repoRoot)) {\n p.log.warn(`${OPT_OUT_MARKER} present — Agent Bridge is opted out. Skipping sync.`);\n p.outro('Skipped (opted out).');\n return;\n }\n\n const s = p.spinner();\n\n // --- Phase 1: Load & validate config ---\n s.start('Loading configuration…');\n\n const config = await loadConfig(repoRoot);\n\n // Run pending migrations if config version is outdated\n const migrationResult = await runMigrations(repoRoot);\n if (migrationResult) {\n p.log.info(\n `Config upgraded ${migrationResult.fromVersion} → ${migrationResult.toVersion}` +\n (migrationResult.applied.length > 0\n ? ` (${migrationResult.applied.length} migration(s))`\n : '')\n );\n }\n\n s.stop('Configuration valid');\n\n // --- Phase 2: Sync sources ---\n s.start('Syncing sources…');\n\n const sourceResults = await syncAllSources(repoRoot, config);\n const sourceErrors = sourceResults.filter((r) => r.error);\n if (sourceErrors.length > 0) {\n s.stop('Some sources failed');\n for (const err of sourceErrors) {\n p.log.error(`${err.name}: ${err.error}`);\n }\n process.exit(1);\n }\n\n // Clean up stale source directories\n const staleRemoved = await removeStaleSourceDirs(repoRoot, config);\n if (staleRemoved.length > 0) {\n for (const name of staleRemoved) {\n p.log.info(`Removed stale source: ${name}`);\n }\n }\n\n for (const r of sourceResults) {\n if (r.action !== 'local') {\n p.log.info(`${r.name}: ${r.action}`);\n }\n }\n\n s.stop('Sources synced');\n\n // --- Phase 3: Discover & validate features ---\n s.start('Discovering features…');\n\n const featureTypes = await discoverFeatureTypes(repoRoot, config);\n const features = await scanFeatures(repoRoot, config, featureTypes);\n const rootFiles = await scanRootFiles(repoRoot, config);\n const toolRootEntries = await scanToolRootEntries(repoRoot, config);\n\n const duplicates = detectDuplicates(features);\n if (duplicates.length > 0) {\n s.stop('Duplicate features detected');\n for (const dup of duplicates) {\n p.log.error(\n `Duplicate \"${dup.name}\" (${dup.type}): ${dup.paths.join(', ')}`\n );\n }\n process.exit(1);\n }\n\n const rootDuplicates = detectRootFileDuplicates(rootFiles);\n if (rootDuplicates.length > 0) {\n s.stop('Duplicate root files detected');\n for (const dup of rootDuplicates) {\n p.log.error(\n `Duplicate \"${dup.fileName}\": ${dup.paths.join(', ')}`\n );\n }\n process.exit(1);\n }\n\n const toolRootDuplicates = detectToolRootDuplicates(toolRootEntries);\n if (toolRootDuplicates.length > 0) {\n s.stop('Duplicate tool root entries detected');\n for (const dup of toolRootDuplicates) {\n p.log.error(\n `Duplicate \"${dup.name}\" for tool \"${dup.toolName}\": ${dup.paths.join(', ')}`\n );\n }\n process.exit(1);\n }\n\n s.stop(`${features.length} features found${rootFiles.length > 0 ? `, ${rootFiles.length} root file(s)` : ''}${toolRootEntries.length > 0 ? `, ${toolRootEntries.length} tool root entr${toolRootEntries.length === 1 ? 'y' : 'ies'}` : ''}`);\n\n // --- Phase 3b: Detect path conflicts ---\n s.start('Checking for path conflicts…');\n\n const conflicts: string[] = [];\n for (const tool of config.tools) {\n for (const feature of features) {\n if (!featureMatchesTool(feature, tool.name)) continue;\n\n const linkName = featureName(feature);\n const featureTypeDir = join(repoRoot, tool.folder, feature.displayType);\n const dest = featureDestPath(\n repoRoot,\n tool.folder,\n feature.displayType,\n linkName\n );\n if (await checkPathConflict(featureTypeDir, linkName, feature.isFile)) {\n conflicts.push(dest);\n }\n }\n }\n\n if (conflicts.length > 0) {\n s.stop('Path conflicts detected');\n for (const c of conflicts) {\n p.log.error(`Conflict: \"${c}\" exists as a real file or directory`);\n }\n p.log.info('Remove or rename the conflicting paths, then re-run sync.');\n process.exit(1);\n }\n\n s.stop('No path conflicts');\n\n // --- Phase 4: Reconcile features ---\n s.start('Reconciling features…');\n\n const result = await reconcileFeatures(repoRoot, config, features);\n\n s.stop('Features reconciled');\n\n p.log.info(\n `Added: ${result.added} Updated: ${result.updated} Removed: ${result.removed}`\n );\n\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n p.outro(`Sync completed with ${result.errors.length} error(s).`);\n process.exit(1);\n }\n\n // --- Phase 5: Sync root files ---\n if (rootFiles.length > 0) {\n s.start('Syncing root files…');\n\n const rootResult = await syncRootFiles(repoRoot, rootFiles);\n\n for (const name of rootResult.synced) {\n p.log.info(`Root file synced: ${name}`);\n }\n for (const name of rootResult.removed) {\n p.log.info(`Root file removed: ${name}`);\n }\n for (const err of rootResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n\n s.stop('Root files synced');\n } else {\n // Clean up any managed root files when no sources provide them\n const rootResult = await syncRootFiles(repoRoot, []);\n for (const name of rootResult.removed) {\n p.log.info(`Root file removed: ${name}`);\n }\n }\n\n // --- Phase 6: Sync tool root entries ---\n s.start('Syncing tool root entries…');\n\n const toolRootResult = await reconcileToolRootEntries(\n repoRoot,\n config,\n toolRootEntries\n );\n\n if (\n toolRootResult.added > 0 ||\n toolRootResult.updated > 0 ||\n toolRootResult.removed > 0\n ) {\n p.log.info(\n `Tool root: Added: ${toolRootResult.added} Updated: ${toolRootResult.updated} Removed: ${toolRootResult.removed}`\n );\n }\n\n if (toolRootResult.errors.length > 0) {\n for (const err of toolRootResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n s.stop('Tool root entries synced with errors');\n p.outro(`Sync completed with ${toolRootResult.errors.length} error(s).`);\n process.exit(1);\n }\n\n s.stop('Tool root entries synced');\n\n p.outro('Sync complete.');\n}\n","import * as p from '@clack/prompts';\nimport { loadConfig } from '../lib/config.js';\nimport { findRepoRoot } from '../lib/git.js';\nimport { runMigrations } from '../lib/migrations/index.js';\nimport { syncAllSources } from '../lib/sources.js';\n\nexport async function updateCommand(cwd?: string, _opts?: unknown): Promise<void> {\n const repoRoot = cwd ?? findRepoRoot();\n\n p.intro('Agent Bridge — Update Sources');\n\n const config = await loadConfig(repoRoot);\n\n // Run pending migrations if config version is outdated\n const migrationResult = await runMigrations(repoRoot);\n if (migrationResult) {\n p.log.info(\n `Config upgraded ${migrationResult.fromVersion} → ${migrationResult.toVersion}` +\n (migrationResult.applied.length > 0\n ? ` (${migrationResult.applied.length} migration(s))`\n : '')\n );\n }\n\n const s = p.spinner();\n s.start('Updating all remote sources…');\n\n const results = await syncAllSources(repoRoot, config);\n\n s.stop('Update complete');\n\n for (const r of results) {\n if (r.error) {\n p.log.error(`${r.name}: ${r.error}`);\n } else {\n p.log.info(`${r.name}: ${r.action}`);\n }\n }\n\n const errors = results.filter((r) => r.error);\n if (errors.length > 0) {\n p.outro(`Done with ${errors.length} error(s).`);\n } else {\n p.outro('All sources up to date.');\n }\n}\n","import * as p from '@clack/prompts';\nimport {\n bridgeDir,\n configExists,\n loadConfig,\n writeOptOutMarker,\n OPT_OUT_MARKER,\n type BridgeConfig,\n} from '../lib/config.js';\nimport { removeDir, dirExists } from '../lib/fs.js';\nimport { findRepoRoot, isInGitRepo, removeGitHooks } from '../lib/git.js';\nimport { reconcileFeatures, reconcileToolRootEntries } from '../lib/sync.js';\n\nfunction summarizeTools(config: BridgeConfig): string {\n return config.tools.map((t) => t.name).join(', ');\n}\n\nexport async function optOutCommand(\n cwd?: string,\n _opts?: unknown\n): Promise<void> {\n const repoRoot = cwd ?? findRepoRoot();\n\n p.intro('Agent Bridge Opt-out');\n\n const hasConfig = await configExists(repoRoot);\n let config: BridgeConfig | undefined;\n\n if (hasConfig) {\n config = await loadConfig(repoRoot);\n }\n\n const toolSummary = config ? summarizeTools(config) : 'unknown (no config found)';\n p.log.info(\n `Non-interactive opt-out: removing Agent Bridge managed files for tools: ${toolSummary}`\n );\n\n const s = p.spinner();\n\n let featureErrors = 0;\n let toolRootErrors = 0;\n\n if (config) {\n s.start('Removing synced Agent Bridge files…');\n\n const featureResult = await reconcileFeatures(repoRoot, config, []);\n const toolRootResult = await reconcileToolRootEntries(repoRoot, config, []);\n\n featureErrors = featureResult.errors.length;\n toolRootErrors = toolRootResult.errors.length;\n\n s.stop('Synced files removed');\n\n p.log.info(\n `Features removed: ${featureResult.removed} (errors: ${featureErrors})`\n );\n p.log.info(\n `Tool-root files removed: ${toolRootResult.removed} (errors: ${toolRootErrors})`\n );\n p.log.info('Root files are not removed by opt-out (manifest-only cleanup).');\n\n for (const err of featureResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n for (const err of toolRootResult.errors) {\n p.log.error(`${err.path}: ${err.error}`);\n }\n } else {\n p.log.warn('No .agent-bridge/config.yml found. Skipping synced file cleanup.');\n }\n\n s.start('Removing Agent Bridge git hooks…');\n const removedHooks = isInGitRepo(repoRoot) ? await removeGitHooks(repoRoot) : [];\n s.stop('Hooks cleanup complete');\n\n if (removedHooks.length > 0) {\n p.log.info(`Removed hooks: ${removedHooks.join(', ')}`);\n } else if (isInGitRepo(repoRoot)) {\n p.log.info('No Agent Bridge hooks found.');\n } else {\n p.log.info('Not a git repository; hook cleanup skipped.');\n }\n\n s.start('Removing .agent-bridge directory…');\n const bridgePath = bridgeDir(repoRoot);\n if (await dirExists(bridgePath)) {\n await removeDir(bridgePath);\n s.stop('.agent-bridge removed');\n } else {\n s.stop('.agent-bridge not found');\n }\n\n // Write a tombstone that survives `.agent-bridge/` deletion so a postinstall\n // guard (or a manual init/sync) won't silently reinstall on the next install.\n await writeOptOutMarker(repoRoot);\n p.log.info(\n `Wrote ${OPT_OUT_MARKER} (gitignored, local to this machine). ` +\n 'Force-add it (`git add -f`) for a repo-wide opt-out. ' +\n 'Run `agent-bridge init --force` to re-enable.'\n );\n\n const totalErrors = featureErrors + toolRootErrors;\n if (totalErrors > 0) {\n p.outro(`Opt-out completed with ${totalErrors} cleanup error(s).`);\n process.exit(1);\n }\n\n p.outro('Opt-out complete. Agent Bridge is removed from this repository.');\n}\n","#!/usr/bin/env node\n\nimport { resolve } from 'node:path';\nimport { stat } from 'node:fs/promises';\nimport { Command } from 'commander';\nimport { initCommand } from './commands/init.js';\nimport { syncCommand } from './commands/sync.js';\nimport { updateCommand } from './commands/update.js';\nimport { optOutCommand } from './commands/opt-out.js';\nimport { VERSION } from './lib/version.js';\n\nexport interface CliOptions {\n cwd?: string;\n force?: boolean;\n // Init-specific options (ignored by other commands)\n domains?: string;\n tools?: string;\n source?: string[];\n hooks?: boolean;\n}\n\nasync function assertCwdExists(cwd: string): Promise<void> {\n try {\n const s = await stat(cwd);\n if (!s.isDirectory()) {\n throw new Error(`--cwd path is not a directory: ${cwd}`);\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error(`--cwd path does not exist: ${cwd}`);\n }\n throw err;\n }\n}\n\nasync function withCwdValidation(\n action: (cwd?: string, opts?: CliOptions) => Promise<void>\n): Promise<(opts: CliOptions) => Promise<void>> {\n return async (opts: CliOptions) => {\n if (opts.cwd) {\n opts.cwd = resolve(opts.cwd);\n await assertCwdExists(opts.cwd);\n }\n await action(opts.cwd, opts);\n };\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n previous.push(value);\n return previous;\n}\n\nconst program = new Command()\n .name('agent-bridge')\n .description('Manage AI tool configurations from multiple sources')\n .version(VERSION, '-v, --version');\n\nprogram\n .command('init')\n .description('Initialize Agent Bridge (creates .agent-bridge/config.yml)')\n .option('--cwd <path>', 'Override the working directory')\n .option('--force', 'Overwrite existing non-Agent-Bridge git hooks')\n .option('--domains <list>', 'Comma-separated domain list (default: backend,frontend,shared)')\n .option('--tools <list>', 'Comma-separated tool names (cursor,vscode,claude) or name:folder pairs')\n .option('-s, --source <url>', 'Source URL or path (repeatable, append #branch for branch)', collect, [])\n .option('--hooks', 'Auto-install git hooks without prompting')\n .action(await withCwdValidation(initCommand));\n\nprogram\n .command('sync')\n .description('Fetch sources, discover features, and sync files')\n .option('--cwd <path>', 'Override the working directory')\n .action(await withCwdValidation(syncCommand));\n\nprogram\n .command('update')\n .description('Fetch latest changes for all remote sources')\n .option('--cwd <path>', 'Override the working directory')\n .action(await withCwdValidation(updateCommand));\n\nprogram\n .command('opt-out')\n .description('Remove Agent Bridge hooks, synced files, and .agent-bridge state')\n .option('--cwd <path>', 'Override the working directory')\n .action(await withCwdValidation(optOutCommand));\n\nprogram.parse();\n"],"mappings":";;;;;;;;;;AASA,MAAM,eAAe;AAErB,MAAM,WAAW,EACd,QAAQ,CACR,IAAI,EAAE,CACN,QAAQ,MAAM,aAAa,KAAK,EAAE,IAAI,MAAM,OAAO,MAAM,MAAM,EAC9D,SAAS,6DACV,CAAC;AAEJ,MAAM,qBAAqB,EACxB,QAAQ,CACR,IAAI,EAAE,CACN,QACE,UAAU;AACT,KAAI,WAAW,MAAM,IAAI,MAAM,SAAS,KAAK,CAAE,QAAO;CACtD,MAAM,WAAW,MAAM,MAAM,QAAQ,CAAC,QAAQ,MAAM,EAAE,SAAS,EAAE;AACjE,KAAI,SAAS,WAAW,EAAG,QAAO;AAClC,QAAO,SAAS,OACb,QAAQ,QAAQ,QAAQ,QAAQ,OAAO,uBAAuB,KAAK,IAAI,CACzE;GAEH,EAAE,SAAS,qDAAqD,CACjE;AAEH,MAAM,mBAAmB,EAAE,OAAO;CAChC,MAAM,SAAS,QAAQ,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE,EAC9C,SAAS,4DACV,CAAC;CACF,QAAQ;CACT,CAAC;AAEF,MAAM,qBAAqB,EAAE,OAAO;CAClC,MAAM;CACN,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,MAAM,CAAC,EAAE,WAAW,IAAI,EAAE,EAC1D,SAAS,2BACV,CAAC;CACF,QAAQ,EACL,QAAQ,CACR,QAAQ,MAAM,qBAAqB,KAAK,EAAE,IAAI,CAAC,EAAE,WAAW,IAAI,EAAE,EACjE,SAAS,qDACV,CAAC,CACD,UAAU;CACd,CAAC;AAEF,MAAM,qBAAqB,EACxB,OAAO;CACN,SAAS,EAAE,QAAQ,CAAC,UAAU;CAC9B,SAAS,EAAE,MAAM,SAAS,CAAC,IAAI,GAAG,sCAAsC;CACxE,OAAO,EAAE,MAAM,iBAAiB,CAAC,IAAI,GAAG,oCAAoC;CAC5E,SAAS,EAAE,MAAM,mBAAmB,CAAC,IAAI,GAAG,sCAAsC;CACnF,CAAC,CACD,aAAa,MAAM,QAAQ;CAE1B,MAAM,4BAAY,IAAI,KAAa;CACnC,MAAM,8BAAc,IAAI,KAAa;AACrC,MAAK,MAAM,SAAS,GAAG,MAAM;AAC3B,MAAI,UAAU,IAAI,EAAE,KAAK,CACvB,KAAI,SAAS;GACX,MAAM,EAAE,aAAa;GACrB,SAAS,yBAAyB,EAAE,KAAK;GACzC,MAAM;IAAC;IAAS;IAAG;IAAO;GAC3B,CAAC;AAEJ,YAAU,IAAI,EAAE,KAAK;AACrB,MAAI,YAAY,IAAI,EAAE,OAAO,CAC3B,KAAI,SAAS;GACX,MAAM,EAAE,aAAa;GACrB,SAAS,2BAA2B,EAAE,OAAO;GAC7C,MAAM;IAAC;IAAS;IAAG;IAAS;GAC7B,CAAC;AAEJ,cAAY,IAAI,EAAE,OAAO;GACzB;CAGF,MAAM,8BAAc,IAAI,KAAa;AACrC,MAAK,QAAQ,SAAS,GAAG,MAAM;AAC7B,MAAI,YAAY,IAAI,EAAE,KAAK,CACzB,KAAI,SAAS;GACX,MAAM,EAAE,aAAa;GACrB,SAAS,2BAA2B,EAAE,KAAK;GAC3C,MAAM;IAAC;IAAW;IAAG;IAAO;GAC7B,CAAC;AAEJ,cAAY,IAAI,EAAE,KAAK;EAEvB,MAAM,WACJ,EAAE,OAAO,WAAW,WAAW,IAC/B,EAAE,OAAO,WAAW,UAAU,IAC9B,EAAE,OAAO,WAAW,UAAU,IAC9B,oBAAoB,KAAK,EAAE,OAAO;AAEpC,MAAI,EAAE,UAAU,CAAC,SACf,KAAI,SAAS;GACX,MAAM,EAAE,aAAa;GACrB,SAAS;GACT,MAAM;IAAC;IAAW;IAAG;IAAS;GAC/B,CAAC;AAEJ,MAAI,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CACpC,KAAI,SAAS;GACX,MAAM,EAAE,aAAa;GACrB,SAAS;GACT,MAAM;IAAC;IAAW;IAAG;IAAS;GAC/B,CAAC;GAEJ;EACF;AAeJ,MAAa,aAAa;AAC1B,MAAa,kBAAkB;;;;;;;;AAS/B,MAAa,iBAAiB,KAAK,YAAY,SAAS;AAMxD,SAAgB,iBAAiB,QAA4B;AAC3D,KACE,OAAO,WAAW,WAAW,IAC7B,OAAO,WAAW,UAAU,IAC5B,OAAO,WAAW,UAAU,CAE5B,QAAO;AAET,KAAI,oBAAoB,KAAK,OAAO,CAClC,QAAO;AAET,QAAO;;AAGT,SAAgB,eAAe,QAAyB;CACtD,MAAM,OAAO,iBAAiB,OAAO;AACrC,QAAO,SAAS,eAAe,SAAS;;AAO1C,SAAgB,UAAU,UAA0B;AAClD,QAAO,KAAK,UAAU,WAAW;;AAGnC,SAAgB,WAAW,UAA0B;AACnD,QAAO,KAAK,UAAU,YAAY,gBAAgB;;AAGpD,SAAgB,UAAU,UAAkB,YAA4B;AACtE,QAAO,KAAK,UAAU,YAAY,WAAW;;AAG/C,SAAgB,iBAAiB,UAA0B;AACzD,QAAO,KAAK,UAAU,eAAe;;;AAIvC,eAAsB,WAAW,UAAoC;AACnE,KAAI;AACF,QAAM,OAAO,iBAAiB,SAAS,CAAC;AACxC,SAAO;SACD;AACN,SAAO;;;;;;;AAQX,eAAsB,kBAAkB,UAAiC;CACvE,MAAM,MAAM,UAAU,SAAS;AAC/B,OAAM,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;AAErC,OAAM,UACJ,KAAK,KAAK,aAAa,EACvB;EAAC;EAA2B;EAAK;EAAe;EAAc,CAAC,KAAK,KAAK,GAAG,MAC5E,QACD;AACD,OAAM,UACJ,iBAAiB,SAAS,EAC1B,wGACA,QACD;;;AAIH,eAAsB,mBAAmB,UAAiC;AACxE,OAAM,GAAG,iBAAiB,SAAS,EAAE,EAAE,OAAO,MAAM,CAAC;;AAOvD,eAAsB,aAAa,UAAoC;AACrE,KAAI;AACF,QAAM,OAAO,WAAW,SAAS,CAAC;AAClC,SAAO;SACD;AACN,SAAO;;;AAIX,eAAsB,WAAW,UAAyC;CACxE,MAAM,MAAM,MAAM,SAAS,WAAW,SAAS,EAAE,QAAQ;CACzD,MAAM,OAAO,KAAK,KAAK,IAAI;CAE3B,MAAM,SAAS,mBAAmB,UAAU,KAAK;AACjD,KAAI,CAAC,OAAO,SAAS;EACnB,MAAM,SAAS,OAAO,MAAM,OAAO,KAChC,MAAM,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC,IAAI,EAAE,UAClC;AACD,QAAM,IAAI,MAAM,mBAAmB,OAAO,KAAK,KAAK,GAAG;;AAGzD,QAAO,OAAO;;AAGhB,eAAsB,WACpB,UACA,QACe;AAEf,OAAM,MADM,UAAU,SAAS,EACd,EAAE,WAAW,MAAM,CAAC;CACrC,MAAM,UAAU,KAAK,KAAK,QAAQ;EAAE,WAAW;EAAI,QAAQ;EAAM,CAAC;AAClE,OAAM,UAAU,WAAW,SAAS,EAAE,SAAS,QAAQ;;;;AC5PzD,SAAgB,eAAuB;AACrC,KAAI;AACF,SAAO,SAAS,iCAAiC;GAC/C,UAAU;GACV,OAAO;GACR,CAAC,CAAC,MAAM;SACH;AACN,SAAO,QAAQ,KAAK;;;;;;AAOxB,SAAgB,YAAY,KAAuB;AACjD,KAAI;AACF,WAAS,uCAAuC;GAC9C,UAAU;GACV,OAAO;GACP;GACD,CAAC;AACF,SAAO;SACD;AACN,SAAO;;;;;;AAOX,SAAgB,eAAe,UAA0B;AACvD,QAAO,KAAK,UAAU,QAAQ,QAAQ;;;;;AAMxC,MAAa,qBAAqB,CAAC,iBAAiB,aAAa;;;;AAMjE,MAAM,cAAc;;;;;;AAOpB,SAAgB,qBAA6B;AAC3C,QAAO;EACP,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCd,eAAsB,mBAAmB,UAAoC;AAC3E,KAAI;AAEF,UADgB,MAAM,SAAS,UAAU,QAAQ,EAClC,SAAS,YAAY;SAC9B;AACN,SAAO;;;;;;AAOX,eAAe,WAAW,UAAoC;AAC5D,KAAI;AACF,QAAM,OAAO,SAAS;AACtB,SAAO;SACD;AACN,SAAO;;;;;;;;;;AAiBX,eAAsB,gBACpB,UACA,QAAQ,OACqB;CAC7B,MAAM,SAA6B;EACjC,WAAW,EAAE;EACb,SAAS,EAAE;EACX,QAAQ,EAAE;EACX;AAED,KAAI,CAAC,YAAY,SAAS,EAAE;AAC1B,OAAK,MAAM,QAAQ,mBACjB,QAAO,OAAO,KAAK;GAAE;GAAM,OAAO;GAAwB,CAAC;AAE7D,SAAO;;CAGT,MAAM,WAAW,eAAe,SAAS;AAGzC,KAAI;AACF,QAAM,MAAM,UAAU,EAAE,WAAW,MAAM,CAAC;UACnC,KAAK;AACZ,OAAK,MAAM,QAAQ,mBACjB,QAAO,OAAO,KAAK;GAAE;GAAM,OAAO,qCAAqC;GAAO,CAAC;AAEjF,SAAO;;CAGT,MAAM,cAAc,oBAAoB;AAExC,MAAK,MAAM,YAAY,oBAAoB;EACzC,MAAM,WAAW,KAAK,UAAU,SAAS;AAEzC,MAAI;AAGF,OAFe,MAAM,WAAW,SAAS,CAKvC,KAFkB,MAAM,mBAAmB,SAAS,EAErC;AAEb,UAAM,UAAU,UAAU,aAAa,QAAQ;AAC/C,UAAM,MAAM,UAAU,IAAM;AAC5B,WAAO,UAAU,KAAK,SAAS;cACtB,OAAO;AAEhB,UAAM,UAAU,UAAU,aAAa,QAAQ;AAC/C,UAAM,MAAM,UAAU,IAAM;AAC5B,WAAO,UAAU,KAAK,SAAS;SAG/B,QAAO,QAAQ,KAAK,SAAS;QAE1B;AAEL,UAAM,UAAU,UAAU,aAAa,QAAQ;AAC/C,UAAM,MAAM,UAAU,IAAM;AAC5B,WAAO,UAAU,KAAK,SAAS;;WAE1B,KAAK;AACZ,UAAO,OAAO,KAAK;IAAE,MAAM;IAAU,OAAO,OAAO,IAAI;IAAE,CAAC;;;AAI9D,QAAO;;;;;;AAOT,eAAsB,eAAe,UAA8C;CACjF,MAAM,UAA6B,EAAE;AAErC,KAAI,CAAC,YAAY,SAAS,CACxB,QAAO;CAGT,MAAM,WAAW,eAAe,SAAS;AAEzC,MAAK,MAAM,YAAY,oBAAoB;EACzC,MAAM,WAAW,KAAK,UAAU,SAAS;AAEzC,MAAI;AACF,OAAI,MAAM,mBAAmB,SAAS,EAAE;IACtC,MAAM,EAAE,WAAW,MAAM,OAAO;AAChC,UAAM,OAAO,SAAS;AACtB,YAAQ,KAAK,SAAS;;UAElB;;AAKV,QAAO;;;;AC1NT,MAAM,EAAE,YAAY,QAAQ,YAAY,UAAU,YAAY,cAAc;;AAG5E,MAAa,kBAAkB;AAE/B,eAAsB,UAAU,GAA6B;AAC3D,KAAI;AAEF,UADU,MAAM,KAAK,EAAE,EACd,aAAa;SAChB;AACN,SAAO;;;AAIX,eAAsB,WAAW,GAA6B;AAC5D,QAAO,WAAW,EAAE;;AAGtB,eAAsB,mBAAmB,KAAgC;CACvE,MAAM,QAAkB,EAAE;CAC1B,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;AAC3D,MAAK,MAAM,SAAS,SAAS;AAI3B,MAAI,MAAM,gBAAgB,CAAE;AAE5B,MAAI,MAAM,SAAA,eAA0B;AACpC,MAAI,MAAM,aAAa,EAAE;GACvB,MAAM,WAAW,MAAM,mBAAmB,KAAK,KAAK,MAAM,KAAK,CAAC;AAChE,SAAM,KAAK,GAAG,SAAS,KAAK,MAAM,KAAK,MAAM,MAAM,EAAE,CAAC,CAAC;aAC9C,MAAM,QAAQ,CACvB,OAAM,KAAK,MAAM,KAAK;;AAG1B,QAAO;;;;;;AAOT,eAAsB,gBAAgB,QAAgB,SAAgC;CACpF,MAAM,QAAQ,MAAM,mBAAmB,OAAO;AAC9C,MAAK,MAAM,WAAW,OAAO;EAC3B,MAAM,UAAU,KAAK,QAAQ,QAAQ;EACrC,MAAM,WAAW,KAAK,SAAS,QAAQ;AACvC,QAAM,UAAU,QAAQ,SAAS,CAAC;AAClC,QAAM,SAAS,SAAS,SAAS;;;;;;AAqBrC,eAAsB,UAAU,KAA4B;AAC1D,OAAM,OAAO,IAAI;;;;;AAMnB,eAAsB,WAAW,UAAiC;AAChE,OAAM,OAAO,SAAS;;;;;;AAWxB,eAAsB,aAAa,KAAgC;CACjE,MAAM,eAAe,KAAK,KAAK,gBAAgB;AAC/C,KAAI;AAEF,UADgB,MAAM,WAAW,cAAc,QAAQ,EACxC,MAAM,KAAK,CAAC,QAAQ,SAAS,KAAK,MAAM,CAAC,SAAS,EAAE;SAC7D;AACN,SAAO,EAAE;;;;;;AAOb,SAAgB,iBAAiB,OAAwB;AACvD,QAAO,MAAM,SAAS,IAAI;;;;;AAM5B,SAAgB,kBAAkB,OAAuB;AACvD,QAAO,MAAM,SAAS,IAAI,GAAG,MAAM,MAAM,GAAG,GAAG,GAAG;;;;;AAMpD,eAAsB,cAAc,KAAa,SAAkC;AAGjF,OAAM,WAFe,KAAK,KAAK,gBAAgB,EAC/B,QAAQ,SAAS,IAAI,QAAQ,KAAK,KAAK,GAAG,OAAO,GAC1B;;;;;;AAOzC,eAAsB,cAAc,KAAa,OAA8B;CAC7E,MAAM,WAAW,MAAM,aAAa,IAAI;AACxC,KAAI,CAAC,SAAS,SAAS,MAAM,EAAE;AAC7B,WAAS,KAAK,MAAM;AACpB,QAAM,cAAc,KAAK,SAAS;;;;;;;AAQtC,eAAsB,mBAAmB,KAAa,OAA8B;CAClF,MAAM,WAAW,MAAM,aAAa,IAAI;CACxC,MAAM,UAAU,SAAS,QAAQ,MAAM,MAAM,MAAM;AACnD,KAAI,QAAQ,WAAW,SAAS,OAC9B,KAAI,QAAQ,WAAW,EAErB,OAAM,OAAO,KAAK,KAAK,gBAAgB,CAAC;KAExC,OAAM,cAAc,KAAK,QAAQ;;;;;;;;;AChIvC,MAAM,gBAAgB;AAEtB,SAAS,IAAI,MAAgB,KAAsB;AACjD,QAAO,aAAa,OAAO,MAAM;EAC/B;EACA,UAAU;EACV,OAAO;EACR,CAAC,CAAC,MAAM;;;;;;AAOX,SAAS,iBAAiB,QAAgB,YAA0B;AAClE,KAAI,CAAC,qBAAqB,KAAK,OAAO,IAAI,OAAO,WAAW,IAAI,CAC9D,OAAM,IAAI,MACR,WAAW,WAAW,0BAA0B,OAAO,+DAExD;;;;;;AAQL,SAAS,oBAAoB,QAAgB,YAA0B;AACrE,KAAI,OAAO,WAAW,IAAI,CACxB,OAAM,IAAI,MACR,WAAW,WAAW,uCAAuC,OAAO,KACrE;;AAIL,eAAe,kBAAkB,MAA6B;AAC5D,OAAM,UACJ,KAAK,MAAM,cAAc,EACzB,sEACA,QACD;;AAGH,eAAe,gBAAgB,KAA+B;AAC5D,KAAI;AACF,QAAM,OAAO,KAAK,KAAK,cAAc,CAAC;AACtC,SAAO;SACD;AACN,SAAO;;;AAQX,eAAsB,YACpB,UACA,QACe;AACf,qBAAoB,OAAO,QAAQ,OAAO,KAAK;AAC/C,KAAI,OAAO,OAAQ,kBAAiB,OAAO,QAAQ,OAAO,KAAK;CAE/D,MAAM,OAAO,UAAU,UAAU,OAAO,KAAK;AAE7C,OAAM,MAAM,UAAU,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;CAErD,MAAM,OAAO;EAAC;EAAS;EAAW;EAAI;AACtC,KAAI,OAAO,OACT,MAAK,KAAK,mBAAmB,YAAY,OAAO,OAAO;AAGzD,MAAK,KAAK,MAAM,OAAO,QAAQ,KAAK;AAEpC,cAAa,OAAO,MAAM,EAAE,OAAO,QAAQ,CAAC;AAE5C,OAAM,kBAAkB,KAAK;;AAG/B,eAAsB,YACpB,UACA,QACe;AACf,qBAAoB,OAAO,QAAQ,OAAO,KAAK;AAC/C,KAAI,OAAO,OAAQ,kBAAiB,OAAO,QAAQ,OAAO,KAAK;CAE/D,MAAM,OAAO,UAAU,UAAU,OAAO,KAAK;AAE7C,KAAI;EAAC;EAAS;EAAW;EAAS,EAAE,KAAK;AAEzC,KAAI,OAAO;MACa,IAAI;GAAC;GAAa;GAAgB;GAAO,EAAE,KAAK,KAChD,OAAO,QAAQ;AAInC,OAAI;AACF,QAAI;KAAC;KAAS;KAAW;KAAK;KAAU,OAAO;KAAO,EAAE,KAAK;WACvD;AAGR,OAAI,CAAC,YAAY,OAAO,OAAO,EAAE,KAAK;;;AAI1C,KAAI;AACF,MAAI,CAAC,QAAQ,YAAY,EAAE,KAAK;SAC1B;AAKR,OAAM,kBAAkB,KAAK;;AAO/B,SAAgB,mBACd,UACA,QACQ;CACR,MAAM,MAAM,OAAO;AACnB,KAAI,WAAW,IAAI,CAAE,QAAO;AAC5B,QAAO,QAAQ,UAAU,IAAI;;AAO/B,SAAgB,kBACd,UACA,QACQ;AACR,KAAI,eAAe,OAAO,OAAO,CAC/B,QAAO,UAAU,UAAU,OAAO,KAAK;AAEzC,QAAO,mBAAmB,UAAU,OAAO;;AAa7C,eAAsB,WACpB,UACA,QAC2B;AAC3B,KAAI,CAAC,eAAe,OAAO,OAAO,EAAE;EAClC,MAAM,WAAW,mBAAmB,UAAU,OAAO;AACrD,MAAI,CAAE,MAAM,UAAU,SAAS,CAC7B,QAAO;GACL,MAAM,OAAO;GACb,QAAQ;GACR,OAAO,qCAAqC,SAAS;GACtD;AAEH,SAAO;GAAE,MAAM,OAAO;GAAM,QAAQ;GAAS;;AAK/C,KAAI,MAAM,UAFG,UAAU,UAAU,OAAO,KAAK,CAEpB,CACvB,KAAI;AACF,QAAM,YAAY,UAAU,OAAO;AACnC,SAAO;GAAE,MAAM,OAAO;GAAM,QAAQ;GAAW;UACxC,KAAK;AACZ,SAAO;GACL,MAAM,OAAO;GACb,QAAQ;GACR,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GACxD;;AAIL,KAAI;AACF,QAAM,YAAY,UAAU,OAAO;AACnC,SAAO;GAAE,MAAM,OAAO;GAAM,QAAQ;GAAU;UACvC,KAAK;AACZ,SAAO;GACL,MAAM,OAAO;GACb,QAAQ;GACR,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GACxD;;;AAIL,eAAsB,eACpB,UACA,QAC6B;AAC7B,OAAM,sBAAsB,SAAS;AACrC,QAAO,QAAQ,IAAI,OAAO,QAAQ,KAAK,WAAW,WAAW,UAAU,OAAO,CAAC,CAAC;;;;;;;;AAalF,eAAsB,sBAAsB,UAAiC;CAC3E,MAAM,SAAS,UAAU,SAAS;AAClC,OAAM,MAAM,QAAQ,EAAE,WAAW,MAAM,CAAC;AAMxC,OAAM,UAJgB,KAAK,QAAQ,aAAa,EAClC;EAAC;EAA2B;EAAK;EAAe;EAAc,CACtD,KAAK,KAAK,GAAG,MAEK,QAAQ;;;;;;;;;;AAelD,eAAsB,sBACpB,UACA,QACmB;CACnB,MAAM,SAAS,UAAU,SAAS;AAClC,KAAI,CAAE,MAAM,UAAU,OAAO,CAAG,QAAO,EAAE;CAEzC,MAAM,UAAU,MAAM,QAAQ,QAAQ,EAAE,eAAe,MAAM,CAAC;CAE9D,MAAM,kBAAkB,IAAI,IAC1B,OAAO,QAAQ,QAAQ,MAAM,eAAe,EAAE,OAAO,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAC1E;CAED,MAAM,UAAoB,EAAE;AAC5B,MAAK,MAAM,SAAS,SAAS;AAC3B,MAAI,CAAC,MAAM,aAAa,CAAE;AAC1B,MAAI,gBAAgB,IAAI,MAAM,KAAK,CAAE;EAErC,MAAM,YAAY,KAAK,QAAQ,MAAM,KAAK;AAE1C,MACG,MAAM,gBAAgB,UAAU,IAChC,MAAM,UAAU,KAAK,WAAW,OAAO,CAAC,EACzC;AACA,SAAM,GAAG,WAAW;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;AACrD,WAAQ,KAAK,MAAM,KAAK;;;AAI5B,QAAO;;;;AE5RT,MAAa;;;ACgBb,MAAM,mBAAmB;CACvB;EAAE,OAAO;GAAE,MAAM;GAAU,QAAQ;GAAW;EAAE,OAAO;EAAsB;CAC7E;EAAE,OAAO;GAAE,MAAM;GAAU,QAAQ;GAAW;EAAE,OAAO;EAAqB;CAC5E;EAAE,OAAO;GAAE,MAAM;GAAU,QAAQ;GAAW;EAAE,OAAO;EAAqB;CAC5E;EAAE,OAAO;GAAE,MAAM;GAAM,QAAQ;GAAO;EAAE,OAAO;EAAa;CAC7D;AAED,MAAM,sBAAkD,OAAO,YAC7D,iBAAiB,KAAK,MAAM,CAAC,EAAE,MAAM,MAAM,EAAE,MAAM,CAAC,CACrD;AAED,MAAM,uBAAmC;CAAE,MAAM;CAAc,QAAQ;CAAc;AAErF,MAAM,kBAAkB;CAAC;CAAW;CAAY;CAAS;;;;;;;;;;AAmBzD,SAAgB,iBAAiB,QAAwB;CACvD,IAAI,UAAU;CAGd,MAAM,WAAW,QAAQ,MAAM,yBAAyB;AACxD,KAAI,SAAU,WAAU,SAAS;AAGjC,KAAI;AAEF,YADY,IAAI,IAAI,QAAQ,CACd;SACR;AAMR,SADa,QAAQ,QAAQ,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,SACjD,QAAQ,UAAU,GAAG,IAAI;;;;;;AAOvC,SAAgB,cAAc,OAA6B;AACzD,QAAO,MAAM,MAAM,IAAI,CAAC,KAAK,MAAM;EACjC,MAAM,UAAU,EAAE,MAAM;AACxB,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,6BAA6B;AAE3D,MAAI,oBAAoB,SAAU,QAAO,oBAAoB;EAE7D,MAAM,WAAW,QAAQ,QAAQ,IAAI;AACrC,MAAI,WAAW,EACb,QAAO;GAAE,MAAM,QAAQ,MAAM,GAAG,SAAS;GAAE,QAAQ,QAAQ,MAAM,WAAW,EAAE;GAAE;AAGlF,QAAM,IAAI,MACR,iBAAiB,QAAQ,uBAAuB,OAAO,KAAK,oBAAoB,CAAC,KAAK,KAAK,CAAC,0BAC7F;GACD;;;;;;AAOJ,SAAgB,eAAe,OAAe,UAAgC;CAC5E,IAAI,SAAS,MAAM,MAAM;CACzB,IAAI;CAEJ,MAAM,UAAU,OAAO,YAAY,IAAI;AACvC,KAAI,UAAU,GAAG;AACf,WAAS,OAAO,MAAM,UAAU,EAAE;AAClC,WAAS,OAAO,MAAM,GAAG,QAAQ;;AAGnC,KAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,2BAA2B;CAGxD,MAAM,QAAsB;EAAE,MADjB,iBAAiB,OAAO;EACD;EAAQ;AAE5C,KAAI,CAAC,eAAe,MAAM,OAAO,CAC/B,OAAM,SAAS,QAAQ,UAAU,MAAM,OAAO;AAGhD,KAAI,OACF,OAAM,SAAS;AAGjB,QAAO;;AAGT,eAAsB,YACpB,KACA,MACe;CACf,MAAM,WAAW,OAAO,cAAc;AAItC,KAAI,MAAM,WAAW,SAAS,CAC5B,KAAI,MAAM,MACR,OAAM,mBAAmB,SAAS;MAC7B;AACL,IAAE,IAAI,KACJ,GAAG,eAAe,wGAEnB;AACD;;CAIJ,MAAM,cAAc,CAAC,CAAC,MAAM;CAC5B,MAAM,eAAe,CAAC,EAAE,MAAM,UAAU,KAAK,OAAO,SAAS;AAG7D,KAAI,gBAAgB,cAAc;AAChC,IAAE,IAAI,MAAM,mEAAmE;AAC/E,UAAQ,KAAK,EAAE;;AAIjB,KAAI,eAAe,cAAc;EAC/B,MAAM,UAAU,KAAM,UAClB,KAAM,QAAQ,MAAM,IAAI,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ,GAC7D,CAAC,GAAG,gBAAgB;EAExB,MAAM,QAAQ,cAAc,KAAM,MAAO;EACzC,MAAM,UAAU,KAAM,OAAQ,KAAK,MAAM,eAAe,GAAG,SAAS,CAAC;EAGrE,MAAM,uBAAO,IAAI,KAAa;AAC9B,OAAK,MAAM,KAAK,SAAS;AACvB,OAAI,KAAK,IAAI,EAAE,KAAK,CAClB,OAAM,IAAI,MAAM,0BAA0B,EAAE,KAAK,mCAAmC;AAEtF,QAAK,IAAI,EAAE,KAAK;;EAGlB,MAAM,SAAuB;GAC3B,SAAS;GACT;GACA;GACA;GACD;AAED,QAAM,WAAW,UAAU,OAAO;AAClC,QAAM,sBAAsB,SAAS;AACrC,IAAE,IAAI,QAAQ,iCAAiC;EAG/C,MAAM,UAAU,EAAE,SAAS;AAC3B,UAAQ,MAAM,2BAA2B;EAEzC,MAAM,eADU,MAAM,eAAe,UAAU,OAAO,EAC1B,QAAQ,MAAM,EAAE,MAAM;AAClD,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAQ,KAAK,sBAAsB;AACnC,QAAK,MAAM,OAAO,YAChB,GAAE,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,IAAI,QAAQ;QAG1C,SAAQ,KAAK,oBAAoB;AAInC,MAAI,KAAM,SAAS,YAAY,SAAS,EAAE;GACxC,MAAM,aAAa,MAAM,gBAAgB,UAAU,KAAM,UAAU,KAAK;AACxE,OAAI,WAAW,UAAU,SAAS,EAChC,GAAE,IAAI,QAAQ,wBAAwB,WAAW,UAAU,KAAK,KAAK,GAAG;AAE1E,OAAI,WAAW,QAAQ,SAAS,EAC9B,GAAE,IAAI,KAAK,kBAAkB,WAAW,QAAQ,KAAK,KAAK,GAAG;AAE/D,OAAI,WAAW,OAAO,SAAS,EAC7B,MAAK,MAAM,KAAK,WAAW,OACzB,GAAE,IAAI,MAAM,QAAQ,EAAE,KAAK,IAAI,EAAE,QAAQ;;AAK/C,IAAE,MAAM,kDAAkD;AAC1D;;AAKF,GAAE,MAAM,0CAA0C;AAElD,KAAI,MAAM,aAAa,SAAS,EAAE;EAChC,MAAM,WAAW,MAAM,WAAW,SAAS;AAC3C,IAAE,IAAI,KACJ,8BAA8B,SAAS,SAAS,UAAU,EAAE,wCAC7D;;CAIH,MAAM,eAAe,MAAM,EAAE,KAAK;EAChC,SAAS;EACT,aAAa,gBAAgB,KAAK,KAAK;EACvC,cAAc,gBAAgB,KAAK,KAAK;EACxC,WAAW,MAAM;AACf,OAAI,CAAC,EAAE,MAAM,CAAE,QAAO;;EAEzB,CAAC;AACF,KAAI,EAAE,SAAS,aAAa,EAAE;AAC5B,IAAE,OAAO,mBAAmB;AAC5B,UAAQ,KAAK,EAAE;;CAGjB,MAAM,UAAU,aACb,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,OAAO,QAAQ;CAGlB,MAAM,gBAAgB,MAAM,EAAE,YAAY;EACxC,SAAS;EACT,SAAS,CACP,GAAG,kBACH;GAAE,OAAO;GAAsB,OAAO;GAA2B,CAClE;EACD,UAAU;EACX,CAAC;AACF,KAAI,EAAE,SAAS,cAAc,EAAE;AAC7B,IAAE,OAAO,mBAAmB;AAC5B,UAAQ,KAAK,EAAE;;CAGjB,MAAM,QAAuB,cAA+B,QACzD,MAAM,EAAE,SAAS,aACnB;AAGD,KAAK,cAA+B,MAAM,MAAM,EAAE,SAAS,aAAa,EAAE;EACxE,IAAI,eAAe;AACnB,SAAO,cAAc;GACnB,MAAM,OAAO,MAAM,EAAE,KAAK;IACxB,SAAS;IACT,aAAa;IACb,cAAc;IACd,WAAW,MAAM;AACf,SAAI,CAAC,EAAE,MAAM,CAAE,QAAO;AACtB,SAAI,MAAM,MAAM,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAE,QAAO;;IAEtD,CAAC;AACF,OAAI,EAAE,SAAS,KAAK,CAAE;GAEtB,MAAM,SAAS,MAAM,EAAE,KAAK;IAC1B,SAAS,sBAAsB,KAAK;IACpC,aAAa,IAAI;IACjB,cAAc;IACd,WAAW,MAAM;AACf,SAAI,CAAC,EAAE,MAAM,CAAE,QAAO;AACtB,SAAI,MAAM,MAAM,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAAE,QAAO;;IAExD,CAAC;AACF,OAAI,EAAE,SAAS,OAAO,CAAE;AAExB,SAAM,KAAK;IAAE,MAAM,KAAK,MAAM;IAAE,QAAQ,OAAO,MAAM;IAAE,CAAC;GAExD,MAAM,UAAU,MAAM,EAAE,QAAQ;IAC9B,SAAS;IACT,cAAc;IACf,CAAC;AACF,OAAI,EAAE,SAAS,QAAQ,IAAI,CAAC,QAC1B,gBAAe;;AAInB,MAAI,MAAM,WAAW,GAAG;AACtB,KAAE,OAAO,iCAAiC;AAC1C,WAAQ,KAAK,EAAE;;;CAKnB,MAAM,UAA0B,EAAE;CAElC,MAAM,YAAY,YAA8B;EAC9C,MAAM,SAAS,MAAM,EAAE,KAAK;GAC1B,SAAS;GACT,aAAa;GACb,cAAc;GACd,WAAW,MAAM;AACf,QAAI,CAAC,EAAE,MAAM,CAAE,QAAO;IACtB,MAAM,UAAU,iBAAiB,EAAE,MAAM,CAAC;AAC1C,QAAI,QAAQ,MAAM,MAAM,EAAE,SAAS,QAAQ,CACzC,QAAO,gBAAgB,QAAQ;;GAEpC,CAAC;AACF,MAAI,EAAE,SAAS,OAAO,CAAE,QAAO;EAG/B,MAAM,QAAsB;GAAE,MADjB,iBAAiB,OAAO,MAAM,CAAC;GACR,QAAQ,OAAO,MAAM;GAAE;AAG3D,MAAI,CAAC,eAAe,MAAM,OAAO,CAC/B,OAAM,SAAS,QAAQ,UAAU,MAAM,OAAO;AAIhD,MAAI,eAAe,MAAM,OAAO,EAAE;GAChC,MAAM,SAAS,MAAM,EAAE,KAAK;IAC1B,SAAS;IACT,aAAa;IACb,cAAc;IACf,CAAC;AACF,OAAI,EAAE,SAAS,OAAO,CAAE,QAAO;AAC/B,OAAI,OAAO,MAAM,CACf,OAAM,SAAS,OAAO,MAAM;;AAIhC,UAAQ,KAAK,MAAM;AACnB,SAAO;;AAGT,GAAE,IAAI,KAAK,2BAA2B;CACtC,IAAI,eAAe;AACnB,QAAO,cAAc;AAEnB,MAAI,CADU,MAAM,WAAW,EACnB;AACV,OAAI,QAAQ,WAAW,GAAG;AACxB,MAAE,OAAO,mCAAmC;AAC5C,YAAQ,KAAK,EAAE;;AAEjB;;EAGF,MAAM,UAAU,MAAM,EAAE,QAAQ;GAC9B,SAAS;GACT,cAAc;GACf,CAAC;AACF,MAAI,EAAE,SAAS,QAAQ,IAAI,CAAC,QAC1B,gBAAe;;CAInB,MAAM,SAAuB;EAC3B,SAAS;EACT;EACA;EACA;EACD;AAED,OAAM,WAAW,UAAU,OAAO;AAClC,OAAM,sBAAsB,SAAS;AACrC,GAAE,IAAI,QAAQ,iCAAiC;CAG/C,MAAM,IAAI,EAAE,SAAS;AACrB,GAAE,MAAM,2BAA2B;CAEnC,MAAM,UADU,MAAM,eAAe,UAAU,OAAO,EAC/B,QAAQ,MAAM,EAAE,MAAM;AAC7C,KAAI,OAAO,SAAS,GAAG;AACrB,IAAE,KAAK,sBAAsB;AAC7B,OAAK,MAAM,OAAO,OAChB,GAAE,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,IAAI,QAAQ;OAG1C,GAAE,KAAK,oBAAoB;AAI7B,KAAI,YAAY,SAAS,EAAE;EACzB,MAAM,eAAe,MAAM,EAAE,QAAQ;GACnC,SAAS;GACT,cAAc;GACf,CAAC;AAEF,MAAI,CAAC,EAAE,SAAS,aAAa,IAAI,cAAc;GAC7C,MAAM,aAAa,MAAM,gBAAgB,UAAU,MAAM,UAAU,KAAK;AAExE,OAAI,WAAW,UAAU,SAAS,EAChC,GAAE,IAAI,QAAQ,wBAAwB,WAAW,UAAU,KAAK,KAAK,GAAG;AAG1E,OAAI,WAAW,QAAQ,SAAS,GAAG;AACjC,MAAE,IAAI,KACJ,oDAAoD,WAAW,QAAQ,KAAK,KAAK,GAClF;AACD,MAAE,IAAI,KAAK,0EAA0E;;AAGvF,OAAI,WAAW,OAAO,SAAS,EAC7B,MAAK,MAAM,OAAO,WAAW,OAC3B,GAAE,IAAI,MAAM,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ;;;AAMrD,GAAE,MAAM,kDAAkD;;;;AClY5D,MAAa,aAA0B,EAUtC;;AAOD,SAAgB,YAAY,SAA2C;CAErE,MAAM,QADQ,QAAQ,QAAQ,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,GAC/B,MAAM,IAAI,CAAC,IAAI,OAAO;AAC1C,QAAO;EAAC,MAAM,MAAM;EAAG,MAAM,MAAM;EAAG,MAAM,MAAM;EAAE;;;AAItD,SAAgB,cAAc,GAAW,GAAmB;CAC1D,MAAM,CAAC,MAAM,MAAM,QAAQ,YAAY,EAAE;CACzC,MAAM,CAAC,MAAM,MAAM,QAAQ,YAAY,EAAE;AAEzC,KAAI,SAAS,KAAM,QAAO,OAAO,OAAO,KAAK;AAC7C,KAAI,SAAS,KAAM,QAAO,OAAO,OAAO,KAAK;AAC7C,KAAI,SAAS,KAAM,QAAO,OAAO,OAAO,KAAK;AAC7C,QAAO;;;;;;AAWT,SAAgB,kBACd,aACA,WACa;AACb,QAAO,WACJ,QACE,MACC,cAAc,EAAE,SAAS,YAAY,GAAG,KACxC,cAAc,EAAE,SAAS,UAAU,IAAI,EAC1C,CACA,MAAM,GAAG,MAAM,cAAc,EAAE,SAAS,EAAE,QAAQ,CAAC;;;;;;;;AASxD,eAAsB,cACpB,UACiC;CACjC,IAAI,SAAS,MAAM,WAAW,SAAS;CACvC,MAAM,gBAAgB,OAAO,WAAW;CAExC,MAAM,MAAM,cAAc,eAAe,QAAQ;AAGjD,KAAI,QAAQ,EAAG,QAAO;AAGtB,KAAI,MAAM,EAAG,QAAO;CAGpB,MAAM,UAAU,kBAAkB,eAAe,QAAQ;CACzD,MAAM,UAAoB,EAAE;AAE5B,MAAK,MAAM,aAAa,SAAS;AAC/B,WAAS,MAAM,UAAU,QAAQ,UAAU,OAAO;AAClD,UAAQ,KAAK,UAAU,QAAQ;;AAKjC,UAAS;EAAE,GAAG;EAAQ,SAAS;EAAS;AACxC,OAAM,WAAW,UAAU,OAAO;AAElC,QAAO;EACL,aAAa;EACb,WAAW;EACX;EACD;;;;ACvHH,MAAM,wBAAwB;AA+B9B,SAAgB,gBAAgB,MAG9B;CACA,MAAM,MAAM,KAAK,QAAQ,sBAAsB;AAC/C,KAAI,MAAM,EACR,QAAO;EACL,YAAY,KAAK,UAAU,GAAG,IAAI;EAClC,UAAU,KAAK,UAAU,MAAM,EAA6B;EAC7D;AAEH,QAAO,EAAE,UAAU,MAAM;;AAG3B,SAAgB,mBACd,SACA,UACS;AACT,KAAI,CAAC,QAAQ,WAAY,QAAO;AAChC,QAAO,QAAQ,eAAe;;AAGhC,SAAgB,YAAY,SAA0B;AACpD,KAAI,QAAQ,WACV,QAAO,gBAAgB,QAAQ,KAAK,CAAC;AAEvC,QAAO,QAAQ;;;;;AAcjB,eAAsB,qBACpB,UACA,QACmB;CACnB,MAAM,wBAAQ,IAAI,KAAa;AAE/B,MAAK,MAAM,UAAU,OAAO,SAAS;EACnC,MAAM,UAAU,kBAAkB,UAAU,OAAO;AACnD,OAAK,MAAM,UAAU,OAAO,SAAS;GACnC,MAAM,YAAY,KAAK,SAAS,OAAO;AACvC,OAAI,CAAE,MAAM,UAAU,UAAU,CAAG;GAEnC,MAAM,UAAU,MAAM,QAAQ,WAAW,EAAE,eAAe,MAAM,CAAC;AACjE,QAAK,MAAM,SAAS,QAClB,KAAI,MAAM,aAAa,CACrB,OAAM,IAAI,MAAM,KAAK;;;AAM7B,QAAO,CAAC,GAAG,MAAM,CAAC,MAAM;;;;;;;;AAS1B,eAAsB,aACpB,UACA,QACA,cACoB;CACpB,MAAM,WAAsB,EAAE;AAE9B,MAAK,MAAM,UAAU,OAAO,SAAS;EACnC,MAAM,UAAU,kBAAkB,UAAU,OAAO;AAEnD,OAAK,MAAM,UAAU,OAAO,QAC1B,MAAK,MAAM,MAAM,cAAc;GAC7B,MAAM,EAAE,YAAY,gBAAgB,UAAU,aAC5C,gBAAgB,GAAG;GACrB,MAAM,QAAQ,KAAK,SAAS,QAAQ,GAAG;AAEvC,OAAI,CAAE,MAAM,UAAU,MAAM,CAAG;GAE/B,MAAM,UAAU,MAAM,QAAQ,OAAO,EAAE,eAAe,MAAM,CAAC;AAC7D,QAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,SAAS,MAAM,QAAQ;IAC7B,MAAM,QAAQ,MAAM,aAAa;AACjC,QAAI,CAAC,UAAU,CAAC,MAAO;IAEvB,MAAM,EAAE,YAAY,mBAAmB,gBAAgB,MAAM,KAAK;IAClE,MAAM,aAAa,kBAAkB;AAErC,aAAS,KAAK;KACZ,MAAM,MAAM;KACZ,MAAM;KACN,aAAa;KACb,QAAQ,OAAO;KACf;KACA,cAAc,KAAK,OAAO,MAAM,KAAK;KACrC;KACA;KACD,CAAC;;;;AAMV,QAAO;;AAOT,SAAgB,iBAAiB,UAA0C;CACzE,MAAM,wBAAQ,IAAI,KAAwB;AAE1C,MAAK,MAAM,KAAK,UAAU;EACxB,MAAM,WAAW,YAAY,EAAE;EAC/B,MAAM,MAAM,GAAG,EAAE,YAAY,GAAG;EAChC,MAAM,QAAQ,MAAM,IAAI,IAAI,IAAI,EAAE;AAClC,QAAM,KAAK,EAAE;AACb,QAAM,IAAI,KAAK,MAAM;;CAGvB,MAAM,YAAiC,EAAE;AACzC,MAAK,MAAM,GAAG,UAAU,MACtB,KAAI,MAAM,SAAS,EACjB,WAAU,KAAK;EACb,MAAM,YAAY,MAAM,GAAG;EAC3B,MAAM,MAAM,GAAG;EACf,OAAO,MAAM,KAAK,MAAM,EAAE,aAAa;EACxC,CAAC;AAIN,QAAO;;;;;;;AAYT,MAAa,aAAa;CAAC;CAAa;CAAa;CAAY;;;;;AAuBjE,eAAsB,cACpB,UACA,QACqB;CACrB,MAAM,QAAoB,EAAE;AAE5B,MAAK,MAAM,UAAU,OAAO,SAAS;EACnC,MAAM,UAAU,kBAAkB,UAAU,OAAO;AACnD,OAAK,MAAM,UAAU,OAAO,QAC1B,MAAK,MAAM,YAAY,YAAY;GACjC,MAAM,WAAW,KAAK,SAAS,QAAQ,SAAS;AAChD,OAAI,MAAM,WAAW,SAAS,CAC5B,OAAM,KAAK;IACT;IACA,QAAQ,OAAO;IACf;IACA,cAAc;IACf,CAAC;;;AAMV,QAAO;;;;;AAMT,SAAgB,yBACd,WACqB;CACrB,MAAM,yBAAS,IAAI,KAA+B;AAClD,MAAK,MAAM,MAAM,WAAW;EAC1B,MAAM,QAAQ,OAAO,IAAI,GAAG,SAAS,IAAI,EAAE;AAC3C,QAAM,KAAK,GAAG;AACd,SAAO,IAAI,GAAG,UAAU,MAAM;;CAGhC,MAAM,aAAkC,EAAE;AAC1C,MAAK,MAAM,CAAC,UAAU,UAAU,OAC9B,KAAI,MAAM,SAAS,EACjB,YAAW,KAAK;EACd;EACA,OAAO,MAAM,KAAK,OAAO,GAAG,aAAa;EAC1C,CAAC;AAIN,QAAO;;;;;;;AAkCT,eAAsB,oBACpB,UACA,QAC0B;CAC1B,MAAM,UAA2B,EAAE;CACnC,MAAM,YAAY,IAAI,IAAI,OAAO,MAAM,KAAK,MAAM,EAAE,KAAK,CAAC;AAE1D,MAAK,MAAM,UAAU,OAAO,SAAS;EACnC,MAAM,UAAU,kBAAkB,UAAU,OAAO;AACnD,OAAK,MAAM,UAAU,OAAO,SAAS;GACnC,MAAM,YAAY,KAAK,SAAS,OAAO;AACvC,OAAI,CAAE,MAAM,UAAU,UAAU,CAAG;GAEnC,MAAM,gBAAgB,MAAM,QAAQ,WAAW,EAAE,eAAe,MAAM,CAAC;AACvE,QAAK,MAAM,SAAS,eAAe;AACjC,QAAI,CAAC,MAAM,QAAQ,CAAE;IAErB,MAAM,EAAE,YAAY,aAAa,gBAAgB,MAAM,KAAK;AAC5D,QAAI,CAAC,cAAc,CAAC,UAAU,IAAI,WAAW,CAAE;AAE/C,YAAQ,KAAK;KACX,UAAU;KACV,MAAM;KACN,QAAQ,OAAO;KACf;KACA,cAAc,KAAK,WAAW,MAAM,KAAK;KAC1C,CAAC;;;;AAKR,QAAO;;;;;AAMT,SAAgB,yBACd,SACqB;CACrB,MAAM,wBAAQ,IAAI,KAA8B;AAChD,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,MAAM,GAAG,MAAM,SAAS,GAAG,MAAM;EACvC,MAAM,QAAQ,MAAM,IAAI,IAAI,IAAI,EAAE;AAClC,QAAM,KAAK,MAAM;AACjB,QAAM,IAAI,KAAK,MAAM;;CAGvB,MAAM,aAAkC,EAAE;AAC1C,MAAK,MAAM,GAAG,UAAU,MACtB,KAAI,MAAM,SAAS,EACjB,YAAW,KAAK;EACd,UAAU,MAAM,GAAG;EACnB,MAAM,MAAM,GAAG;EACf,OAAO,MAAM,KAAK,MAAM,EAAE,aAAa;EACxC,CAAC;AAIN,QAAO;;;;;;;;;ACpUT,SAAgB,gBACd,UACA,YACA,aACA,aACQ;AACR,QAAO,KAAK,UAAU,YAAY,aAAa,YAAY;;;;;;AAW7D,eAAsB,oBAAoB,gBAAwB,YAAsC;AAEtG,KAAI,CAAE,MAAM,UADK,KAAK,gBAAgB,WAAW,CAClB,CAAG,QAAO;AAGzC,QAAO,EADU,MAAM,aAAa,eAAe,EAClC,SAAS,aAAa,IAAI;;;;;;AAO7C,eAAsB,kBAAkB,gBAAwB,UAAoC;AAElG,KAAI,CAAE,MAAM,WADK,KAAK,gBAAgB,SAAS,CACf,CAAG,QAAO;AAG1C,QAAO,EADU,MAAM,aAAa,eAAe,EAClC,SAAS,SAAS;;;;;;AAOrC,eAAsB,kBACpB,gBACA,aACA,QACkB;AAClB,KAAI,OACF,QAAO,kBAAkB,gBAAgB,YAAY;KAErD,QAAO,oBAAoB,gBAAgB,YAAY;;;;;AAW3D,eAAsB,kBACpB,YACA,gBACA,YACgC;CAChC,MAAM,WAAW,KAAK,gBAAgB,WAAW;CACjD,MAAM,UAAU,MAAM,UAAU,SAAS;AAEzC,KAAI,QACF,OAAM,UAAU,SAAS;AAG3B,OAAM,MAAM,UAAU,EAAE,WAAW,MAAM,CAAC;AAC1C,OAAM,gBAAgB,YAAY,SAAS;AAC3C,OAAM,cAAc,gBAAgB,aAAa,IAAI;AAErD,QAAO,UAAU,YAAY;;;;;AAM/B,eAAsB,gBACpB,YACA,gBACA,UACgC;CAChC,MAAM,WAAW,KAAK,gBAAgB,SAAS;CAC/C,MAAM,UAAU,MAAM,WAAW,SAAS;AAE1C,OAAM,MAAM,gBAAgB,EAAE,WAAW,MAAM,CAAC;AAChD,OAAM,SAAS,YAAY,SAAS;AACpC,OAAM,cAAc,gBAAgB,SAAS;AAE7C,QAAO,UAAU,YAAY;;AAoB/B,eAAsB,mBACpB,SACA,QACe;CACf,IAAI,UAAU;AACd,QAAO,YAAY,UAAU,QAAQ,WAAW,OAAO,CACrD,KAAI;AAEF,OADgB,MAAM,QAAQ,QAAQ,EAC1B,SAAS,EAAG;AACxB,QAAM,MAAM,QAAQ;AACpB,YAAU,QAAQ,QAAQ;SACpB;AACN;;;;;;;AAwBN,eAAe,sBAAsB,KAAsC;AACzE,KAAI,CAAE,MAAM,UAAU,IAAI,CAAG,QAAO,EAAE;CAEtC,MAAM,SAAyB,EAAE;CACjC,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;CAG3D,MAAM,kBAAkB,MAAM,aAAa,IAAI;AAC/C,MAAK,MAAM,SAAS,iBAAiB;EACnC,MAAM,WAAW,iBAAiB,MAAM;EACxC,MAAM,OAAO,kBAAkB,MAAM;AACrC,SAAO,KAAK;GACV,MAAM,KAAK,KAAK,KAAK;GACrB,aAAa;GACb,eAAe;GACf;GACD,CAAC;;AAIJ,MAAK,MAAM,SAAS,SAAS;AAC3B,MAAI,CAAC,MAAM,aAAa,CAAE;AAC1B,MAAI,MAAM,SAAA,eAA0B;EAGpC,MAAM,MAAM,MAAM,sBADD,KAAK,KAAK,MAAM,KAAK,CACW;AACjD,SAAO,KAAK,GAAG,IAAI;;AAGrB,QAAO;;AAsBT,eAAsB,kBACpB,UACA,QACA,UAC0B;CAC1B,IAAI,QAAQ;CACZ,IAAI,UAAU;CACd,IAAI,UAAU;CACd,MAAM,SAAiD,EAAE;CAIzD,MAAM,mCAAmB,IAAI,KAA8B;AAE3D,MAAK,MAAM,QAAQ,OAAO,MACxB,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,CAAC,mBAAmB,SAAS,KAAK,KAAK,CAAE;EAE7C,MAAM,OAAO,YAAY,QAAQ;EACjC,MAAM,iBAAiB,KAAK,UAAU,KAAK,QAAQ,QAAQ,YAAY;EACvE,MAAM,WAAW,KAAK,gBAAgB,KAAK;EAC3C,MAAM,gBAAgB,QAAQ,SAAS,OAAO,OAAO;AAErD,mBAAiB,IAAI,UAAU;GAC7B,YAAY,QAAQ;GACpB;GACA;GACA;GACA,QAAQ,QAAQ;GACjB,CAAC;;AAKN,MAAK,MAAM,QAAQ,OAAO,OAAO;EAC/B,MAAM,UAAU,KAAK,UAAU,KAAK,OAAO;EAC3C,MAAM,iBAAiB,MAAM,sBAAsB,QAAQ;AAE3D,OAAK,MAAM,SAAS,gBAAgB;AAClC,OAAI,iBAAiB,IAAI,MAAM,KAAK,CAAE;AAEtC,OAAI;AACF,QAAI,MAAM,SACR,OAAM,UAAU,MAAM,KAAK;QAE3B,OAAM,WAAW,MAAM,KAAK;AAE9B,UAAM,mBAAmB,MAAM,aAAa,MAAM,cAAc;AAChE,UAAM,mBAAmB,MAAM,aAAa,QAAQ;AACpD;YACO,KAAK;AACZ,WAAO,KAAK;KACV,MAAM,MAAM;KACZ,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;KACxD,CAAC;;;;AAOR,MAAK,MAAM,CAAC,UAAU,aAAa,iBACjC,KAAI;EACF,MAAM,SAAS,SAAS,SACpB,MAAM,gBACJ,SAAS,YACT,SAAS,gBACT,SAAS,KACV,GACD,MAAM,kBACJ,SAAS,YACT,SAAS,gBACT,SAAS,KACV;AAEL,MAAI,WAAW,UAAW;WACjB,WAAW,UAAW;UACxB,KAAK;AACZ,SAAO,KAAK;GACV,MAAM;GACN,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GACxD,CAAC;;AAIN,QAAO;EAAE;EAAO;EAAS;EAAS;EAAQ;;AAO5C,MAAM,mBAAmB;;;;;AAMzB,eAAsB,kBAAkB,UAAoC;AAC1E,KAAI,CAAE,MAAM,WAAW,SAAS,CAAG,QAAO;AAE1C,SADgB,MAAM,SAAS,UAAU,QAAQ,EAClC,WAAW,iBAAiB;;;;;;AAa7C,eAAsB,cACpB,UACA,WAC6B;CAC7B,MAAM,SAAmB,EAAE;CAC3B,MAAM,UAAoB,EAAE;CAC5B,MAAM,SAAiD,EAAE;CAGzD,MAAM,2BAAW,IAAI,KAAuB;AAC5C,MAAK,MAAM,MAAM,UACf,UAAS,IAAI,GAAG,UAAU,GAAG;AAI/B,MAAK,MAAM,CAAC,UAAU,OAAO,UAAU;EACrC,MAAM,WAAW,KAAK,UAAU,SAAS;AACzC,MAAI;AAEF,OAAI,MAAM,WAAW,SAAS;QACxB,CAAE,MAAM,kBAAkB,SAAS,CACrC;;GAIJ,MAAM,gBAAgB,MAAM,SAAS,GAAG,cAAc,QAAQ;GAC9D,MAAM,iBAAiB,mBAAmB,OAAO;AACjD,SAAM,MAAM,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACnD,SAAM,UAAU,UAAU,gBAAgB,QAAQ;AAClD,UAAO,KAAK,SAAS;WACd,KAAK;AACZ,UAAO,KAAK;IACV,MAAM;IACN,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IACxD,CAAC;;;AAKN,MAAK,MAAM,YAAY,YAAY;AACjC,MAAI,SAAS,IAAI,SAAS,CAAE;EAC5B,MAAM,WAAW,KAAK,UAAU,SAAS;AACzC,MAAI;AACF,OAAI,MAAM,kBAAkB,SAAS,EAAE;AACrC,UAAM,WAAW,SAAS;AAC1B,YAAQ,KAAK,SAAS;;WAEjB,KAAK;AACZ,UAAO,KAAK;IACV,MAAM;IACN,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IACxD,CAAC;;;AAIN,QAAO;EAAE;EAAQ;EAAS;EAAQ;;;;;;;AAmBpC,eAAsB,yBACpB,UACA,QACA,SAC6B;CAC7B,IAAI,QAAQ;CACZ,IAAI,UAAU;CACd,IAAI,UAAU;CACd,MAAM,SAAiD,EAAE;CAGzD,MAAM,8BAAc,IAAI,KAAqB;AAC7C,MAAK,MAAM,QAAQ,OAAO,MACxB,aAAY,IAAI,KAAK,MAAM,KAAK,OAAO;CAIzC,MAAM,kCAAkB,IAAI,KAGzB;AAEH,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,SAAS,YAAY,IAAI,MAAM,SAAS;AAC9C,MAAI,CAAC,OAAQ;EAEb,MAAM,UAAU,KAAK,UAAU,OAAO;EACtC,MAAM,WAAW,KAAK,SAAS,MAAM,KAAK;AAE1C,kBAAgB,IAAI,UAAU;GAC5B,YAAY,MAAM;GAClB,MAAM,MAAM;GACZ;GACD,CAAC;;AAIJ,MAAK,MAAM,QAAQ,OAAO,OAAO;EAC/B,MAAM,UAAU,KAAK,UAAU,KAAK,OAAO;EAC3C,MAAM,WAAW,MAAM,aAAa,QAAQ;AAE5C,OAAK,MAAM,iBAAiB,UAAU;GACpC,MAAM,WAAW,iBAAiB,cAAc;GAEhD,MAAM,WAAW,KAAK,SADT,kBAAkB,cAAc,CACT;AAEpC,OAAI,gBAAgB,IAAI,SAAS,CAAE;AAEnC,OAAI;AACF,QAAI,SACF,OAAM,UAAU,SAAS;QAEzB,OAAM,WAAW,SAAS;AAE5B,UAAM,mBAAmB,SAAS,cAAc;AAChD;YACO,KAAK;AACZ,WAAO,KAAK;KACV,MAAM;KACN,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;KACxD,CAAC;;;;AAMR,MAAK,MAAM,GAAG,aAAa,gBACzB,KAAI;EACF,MAAM,SAAS,MAAM,gBACnB,SAAS,YACT,SAAS,SACT,SAAS,KACV;AAED,MAAI,WAAW,UAAW;WACjB,WAAW,UAAW;UACxB,KAAK;AACZ,SAAO,KAAK;GACV,MAAM,KAAK,SAAS,SAAS,SAAS,KAAK;GAC3C,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GACxD,CAAC;;AAIN,QAAO;EAAE;EAAO;EAAS;EAAS;EAAQ;;;;ACne5C,eAAsB,YAAY,KAAc,OAAgC;CAC9E,MAAM,WAAW,OAAO,cAAc;AAEtC,GAAE,MAAM,oBAAoB;AAG5B,KAAI,MAAM,WAAW,SAAS,EAAE;AAC9B,IAAE,IAAI,KAAK,GAAG,eAAe,sDAAsD;AACnF,IAAE,MAAM,uBAAuB;AAC/B;;CAGF,MAAM,IAAI,EAAE,SAAS;AAGrB,GAAE,MAAM,yBAAyB;CAEjC,MAAM,SAAS,MAAM,WAAW,SAAS;CAGzC,MAAM,kBAAkB,MAAM,cAAc,SAAS;AACrD,KAAI,gBACF,GAAE,IAAI,KACJ,mBAAmB,gBAAgB,YAAY,KAAK,gBAAgB,eACjE,gBAAgB,QAAQ,SAAS,IAC9B,KAAK,gBAAgB,QAAQ,OAAO,kBACpC,IACP;AAGH,GAAE,KAAK,sBAAsB;AAG7B,GAAE,MAAM,mBAAmB;CAE3B,MAAM,gBAAgB,MAAM,eAAe,UAAU,OAAO;CAC5D,MAAM,eAAe,cAAc,QAAQ,MAAM,EAAE,MAAM;AACzD,KAAI,aAAa,SAAS,GAAG;AAC3B,IAAE,KAAK,sBAAsB;AAC7B,OAAK,MAAM,OAAO,aAChB,GAAE,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,IAAI,QAAQ;AAE1C,UAAQ,KAAK,EAAE;;CAIjB,MAAM,eAAe,MAAM,sBAAsB,UAAU,OAAO;AAClE,KAAI,aAAa,SAAS,EACxB,MAAK,MAAM,QAAQ,aACjB,GAAE,IAAI,KAAK,yBAAyB,OAAO;AAI/C,MAAK,MAAM,KAAK,cACd,KAAI,EAAE,WAAW,QACf,GAAE,IAAI,KAAK,GAAG,EAAE,KAAK,IAAI,EAAE,SAAS;AAIxC,GAAE,KAAK,iBAAiB;AAGxB,GAAE,MAAM,wBAAwB;CAGhC,MAAM,WAAW,MAAM,aAAa,UAAU,QADzB,MAAM,qBAAqB,UAAU,OAAO,CACE;CACnE,MAAM,YAAY,MAAM,cAAc,UAAU,OAAO;CACvD,MAAM,kBAAkB,MAAM,oBAAoB,UAAU,OAAO;CAEnE,MAAM,aAAa,iBAAiB,SAAS;AAC7C,KAAI,WAAW,SAAS,GAAG;AACzB,IAAE,KAAK,8BAA8B;AACrC,OAAK,MAAM,OAAO,WAChB,GAAE,IAAI,MACJ,cAAc,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,GAC/D;AAEH,UAAQ,KAAK,EAAE;;CAGjB,MAAM,iBAAiB,yBAAyB,UAAU;AAC1D,KAAI,eAAe,SAAS,GAAG;AAC7B,IAAE,KAAK,gCAAgC;AACvC,OAAK,MAAM,OAAO,eAChB,GAAE,IAAI,MACJ,cAAc,IAAI,SAAS,KAAK,IAAI,MAAM,KAAK,KAAK,GACrD;AAEH,UAAQ,KAAK,EAAE;;CAGjB,MAAM,qBAAqB,yBAAyB,gBAAgB;AACpE,KAAI,mBAAmB,SAAS,GAAG;AACjC,IAAE,KAAK,uCAAuC;AAC9C,OAAK,MAAM,OAAO,mBAChB,GAAE,IAAI,MACJ,cAAc,IAAI,KAAK,cAAc,IAAI,SAAS,KAAK,IAAI,MAAM,KAAK,KAAK,GAC5E;AAEH,UAAQ,KAAK,EAAE;;AAGjB,GAAE,KAAK,GAAG,SAAS,OAAO,iBAAiB,UAAU,SAAS,IAAI,KAAK,UAAU,OAAO,iBAAiB,KAAK,gBAAgB,SAAS,IAAI,KAAK,gBAAgB,OAAO,iBAAiB,gBAAgB,WAAW,IAAI,MAAM,UAAU,KAAK;AAG5O,GAAE,MAAM,+BAA+B;CAEvC,MAAM,YAAsB,EAAE;AAC9B,MAAK,MAAM,QAAQ,OAAO,MACxB,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,CAAC,mBAAmB,SAAS,KAAK,KAAK,CAAE;EAE7C,MAAM,WAAW,YAAY,QAAQ;EACrC,MAAM,iBAAiB,KAAK,UAAU,KAAK,QAAQ,QAAQ,YAAY;EACvE,MAAM,OAAO,gBACX,UACA,KAAK,QACL,QAAQ,aACR,SACD;AACD,MAAI,MAAM,kBAAkB,gBAAgB,UAAU,QAAQ,OAAO,CACnE,WAAU,KAAK,KAAK;;AAK1B,KAAI,UAAU,SAAS,GAAG;AACxB,IAAE,KAAK,0BAA0B;AACjC,OAAK,MAAM,KAAK,UACd,GAAE,IAAI,MAAM,cAAc,EAAE,sCAAsC;AAEpE,IAAE,IAAI,KAAK,4DAA4D;AACvE,UAAQ,KAAK,EAAE;;AAGjB,GAAE,KAAK,oBAAoB;AAG3B,GAAE,MAAM,wBAAwB;CAEhC,MAAM,SAAS,MAAM,kBAAkB,UAAU,QAAQ,SAAS;AAElE,GAAE,KAAK,sBAAsB;AAE7B,GAAE,IAAI,KACJ,UAAU,OAAO,MAAM,aAAa,OAAO,QAAQ,aAAa,OAAO,UACxE;AAED,KAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,OAAK,MAAM,OAAO,OAAO,OACvB,GAAE,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,IAAI,QAAQ;AAE1C,IAAE,MAAM,uBAAuB,OAAO,OAAO,OAAO,YAAY;AAChE,UAAQ,KAAK,EAAE;;AAIjB,KAAI,UAAU,SAAS,GAAG;AACxB,IAAE,MAAM,sBAAsB;EAE9B,MAAM,aAAa,MAAM,cAAc,UAAU,UAAU;AAE3D,OAAK,MAAM,QAAQ,WAAW,OAC5B,GAAE,IAAI,KAAK,qBAAqB,OAAO;AAEzC,OAAK,MAAM,QAAQ,WAAW,QAC5B,GAAE,IAAI,KAAK,sBAAsB,OAAO;AAE1C,OAAK,MAAM,OAAO,WAAW,OAC3B,GAAE,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,IAAI,QAAQ;AAG1C,IAAE,KAAK,oBAAoB;QACtB;EAEL,MAAM,aAAa,MAAM,cAAc,UAAU,EAAE,CAAC;AACpD,OAAK,MAAM,QAAQ,WAAW,QAC5B,GAAE,IAAI,KAAK,sBAAsB,OAAO;;AAK5C,GAAE,MAAM,6BAA6B;CAErC,MAAM,iBAAiB,MAAM,yBAC3B,UACA,QACA,gBACD;AAED,KACE,eAAe,QAAQ,KACvB,eAAe,UAAU,KACzB,eAAe,UAAU,EAEzB,GAAE,IAAI,KACJ,qBAAqB,eAAe,MAAM,aAAa,eAAe,QAAQ,aAAa,eAAe,UAC3G;AAGH,KAAI,eAAe,OAAO,SAAS,GAAG;AACpC,OAAK,MAAM,OAAO,eAAe,OAC/B,GAAE,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,IAAI,QAAQ;AAE1C,IAAE,KAAK,uCAAuC;AAC9C,IAAE,MAAM,uBAAuB,eAAe,OAAO,OAAO,YAAY;AACxE,UAAQ,KAAK,EAAE;;AAGjB,GAAE,KAAK,2BAA2B;AAElC,GAAE,MAAM,iBAAiB;;;;ACtO3B,eAAsB,cAAc,KAAc,OAAgC;CAChF,MAAM,WAAW,OAAO,cAAc;AAEtC,GAAE,MAAM,gCAAgC;CAExC,MAAM,SAAS,MAAM,WAAW,SAAS;CAGzC,MAAM,kBAAkB,MAAM,cAAc,SAAS;AACrD,KAAI,gBACF,GAAE,IAAI,KACJ,mBAAmB,gBAAgB,YAAY,KAAK,gBAAgB,eACjE,gBAAgB,QAAQ,SAAS,IAC9B,KAAK,gBAAgB,QAAQ,OAAO,kBACpC,IACP;CAGH,MAAM,IAAI,EAAE,SAAS;AACrB,GAAE,MAAM,+BAA+B;CAEvC,MAAM,UAAU,MAAM,eAAe,UAAU,OAAO;AAEtD,GAAE,KAAK,kBAAkB;AAEzB,MAAK,MAAM,KAAK,QACd,KAAI,EAAE,MACJ,GAAE,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI,EAAE,QAAQ;KAEpC,GAAE,IAAI,KAAK,GAAG,EAAE,KAAK,IAAI,EAAE,SAAS;CAIxC,MAAM,SAAS,QAAQ,QAAQ,MAAM,EAAE,MAAM;AAC7C,KAAI,OAAO,SAAS,EAClB,GAAE,MAAM,aAAa,OAAO,OAAO,YAAY;KAE/C,GAAE,MAAM,0BAA0B;;;;AC9BtC,SAAS,eAAe,QAA8B;AACpD,QAAO,OAAO,MAAM,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK;;AAGnD,eAAsB,cACpB,KACA,OACe;CACf,MAAM,WAAW,OAAO,cAAc;AAEtC,GAAE,MAAM,uBAAuB;CAE/B,MAAM,YAAY,MAAM,aAAa,SAAS;CAC9C,IAAI;AAEJ,KAAI,UACF,UAAS,MAAM,WAAW,SAAS;CAGrC,MAAM,cAAc,SAAS,eAAe,OAAO,GAAG;AACtD,GAAE,IAAI,KACJ,2EAA2E,cAC5E;CAED,MAAM,IAAI,EAAE,SAAS;CAErB,IAAI,gBAAgB;CACpB,IAAI,iBAAiB;AAErB,KAAI,QAAQ;AACV,IAAE,MAAM,sCAAsC;EAE9C,MAAM,gBAAgB,MAAM,kBAAkB,UAAU,QAAQ,EAAE,CAAC;EACnE,MAAM,iBAAiB,MAAM,yBAAyB,UAAU,QAAQ,EAAE,CAAC;AAE3E,kBAAgB,cAAc,OAAO;AACrC,mBAAiB,eAAe,OAAO;AAEvC,IAAE,KAAK,uBAAuB;AAE9B,IAAE,IAAI,KACJ,qBAAqB,cAAc,QAAQ,YAAY,cAAc,GACtE;AACD,IAAE,IAAI,KACJ,4BAA4B,eAAe,QAAQ,YAAY,eAAe,GAC/E;AACD,IAAE,IAAI,KAAK,iEAAiE;AAE5E,OAAK,MAAM,OAAO,cAAc,OAC9B,GAAE,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,IAAI,QAAQ;AAE1C,OAAK,MAAM,OAAO,eAAe,OAC/B,GAAE,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,IAAI,QAAQ;OAG1C,GAAE,IAAI,KAAK,mEAAmE;AAGhF,GAAE,MAAM,mCAAmC;CAC3C,MAAM,eAAe,YAAY,SAAS,GAAG,MAAM,eAAe,SAAS,GAAG,EAAE;AAChF,GAAE,KAAK,yBAAyB;AAEhC,KAAI,aAAa,SAAS,EACxB,GAAE,IAAI,KAAK,kBAAkB,aAAa,KAAK,KAAK,GAAG;UAC9C,YAAY,SAAS,CAC9B,GAAE,IAAI,KAAK,+BAA+B;KAE1C,GAAE,IAAI,KAAK,8CAA8C;AAG3D,GAAE,MAAM,oCAAoC;CAC5C,MAAM,aAAa,UAAU,SAAS;AACtC,KAAI,MAAM,UAAU,WAAW,EAAE;AAC/B,QAAM,UAAU,WAAW;AAC3B,IAAE,KAAK,wBAAwB;OAE/B,GAAE,KAAK,0BAA0B;AAKnC,OAAM,kBAAkB,SAAS;AACjC,GAAE,IAAI,KACJ,SAAS,eAAe,8IAGzB;CAED,MAAM,cAAc,gBAAgB;AACpC,KAAI,cAAc,GAAG;AACnB,IAAE,MAAM,0BAA0B,YAAY,oBAAoB;AAClE,UAAQ,KAAK,EAAE;;AAGjB,GAAE,MAAM,kEAAkE;;;;ACtF5E,eAAe,gBAAgB,KAA4B;AACzD,KAAI;AAEF,MAAI,EADM,MAAM,KAAK,IAAI,EAClB,aAAa,CAClB,OAAM,IAAI,MAAM,kCAAkC,MAAM;UAEnD,KAAK;AACZ,MAAK,IAA8B,SAAS,SAC1C,OAAM,IAAI,MAAM,8BAA8B,MAAM;AAEtD,QAAM;;;AAIV,eAAe,kBACb,QAC8C;AAC9C,QAAO,OAAO,SAAqB;AACjC,MAAI,KAAK,KAAK;AACZ,QAAK,MAAM,QAAQ,KAAK,IAAI;AAC5B,SAAM,gBAAgB,KAAK,IAAI;;AAEjC,QAAM,OAAO,KAAK,KAAK,KAAK;;;AAIhC,SAAS,QAAQ,OAAe,UAA8B;AAC5D,UAAS,KAAK,MAAM;AACpB,QAAO;;AAGT,MAAM,UAAU,IAAI,SAAS,CAC1B,KAAK,eAAe,CACpB,YAAY,sDAAsD,CAClE,QAAQ,SAAS,gBAAgB;AAEpC,QACG,QAAQ,OAAO,CACf,YAAY,6DAA6D,CACzE,OAAO,gBAAgB,iCAAiC,CACxD,OAAO,WAAW,gDAAgD,CAClE,OAAO,oBAAoB,iEAAiE,CAC5F,OAAO,kBAAkB,yEAAyE,CAClG,OAAO,sBAAsB,8DAA8D,SAAS,EAAE,CAAC,CACvG,OAAO,WAAW,2CAA2C,CAC7D,OAAO,MAAM,kBAAkB,YAAY,CAAC;AAE/C,QACG,QAAQ,OAAO,CACf,YAAY,mDAAmD,CAC/D,OAAO,gBAAgB,iCAAiC,CACxD,OAAO,MAAM,kBAAkB,YAAY,CAAC;AAE/C,QACG,QAAQ,SAAS,CACjB,YAAY,8CAA8C,CAC1D,OAAO,gBAAgB,iCAAiC,CACxD,OAAO,MAAM,kBAAkB,cAAc,CAAC;AAEjD,QACG,QAAQ,UAAU,CAClB,YAAY,mEAAmE,CAC/E,OAAO,gBAAgB,iCAAiC,CACxD,OAAO,MAAM,kBAAkB,cAAc,CAAC;AAEjD,QAAQ,OAAO"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@sofatutor/agent-bridge",
3
+ "version": "0.13.1",
4
+ "description": "Sync AI agent configurations (skills, agents, prompts) from shared sources into your project's tool directories.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/sofatutor/agent-bridge.git"
9
+ },
10
+ "type": "module",
11
+ "main": "dist/index.mjs",
12
+ "types": "dist/index.d.mts",
13
+ "bin": {
14
+ "agent-bridge": "dist/index.mjs"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "scripts": {
23
+ "build": "vp pack",
24
+ "check": "vp run typecheck && vp test",
25
+ "check:viteplus": "vp check",
26
+ "dev": "vp pack --watch",
27
+ "prepack": "vp pack",
28
+ "version": "vp pack && git add dist",
29
+ "test": "vp test",
30
+ "test:watch": "vp test watch",
31
+ "typecheck": "tsc --noEmit"
32
+ },
33
+ "dependencies": {
34
+ "@clack/prompts": "^0.9.1",
35
+ "commander": "^14.0.3",
36
+ "fs-extra": "^11.3.4",
37
+ "js-yaml": "^4.1.0",
38
+ "zod": "^4.3.6"
39
+ },
40
+ "devDependencies": {
41
+ "@types/fs-extra": "^11.0.4",
42
+ "@types/js-yaml": "^4.0.9",
43
+ "@types/node": "^20.0.0",
44
+ "typescript": "^5.4.0",
45
+ "vite-plus": "^0.1.15"
46
+ },
47
+ "packageManager": "npm@11.12.1",
48
+ "engines": {
49
+ "node": ">=18.0.0"
50
+ }
51
+ }