@orquentra/codebuddy 2.9.0 → 2.14.0

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.
Files changed (70) hide show
  1. package/CLAUDE_CODEX.md +33 -0
  2. package/README.md +27 -3
  3. package/dist/{adapters-S4HHTWJW.js → adapters-OJRSVFV3.js} +2 -2
  4. package/dist/{chunk-GKNYMNFM.js → chunk-4DKYKDHI.js} +6 -1
  5. package/dist/chunk-4DKYKDHI.js.map +1 -0
  6. package/dist/{chunk-SRA4MDVG.js → chunk-7QMHIJBA.js} +30 -3
  7. package/dist/chunk-7QMHIJBA.js.map +1 -0
  8. package/dist/{chunk-6NZEZJYD.js → chunk-ESC4Y3L7.js} +3 -5
  9. package/dist/{chunk-6NZEZJYD.js.map → chunk-ESC4Y3L7.js.map} +1 -1
  10. package/dist/chunk-GQKWUHMA.js +20 -0
  11. package/dist/chunk-GQKWUHMA.js.map +1 -0
  12. package/dist/{chunk-J4T6HHI4.js → chunk-L6BRYZ6G.js} +537 -316
  13. package/dist/chunk-L6BRYZ6G.js.map +1 -0
  14. package/dist/{chunk-UK6XTS2Y.js → chunk-LRQNPYCK.js} +13 -19
  15. package/dist/chunk-LRQNPYCK.js.map +1 -0
  16. package/dist/{chunk-GXOML4UZ.js → chunk-LW75SKZR.js} +2 -2
  17. package/dist/{chunk-ENKDFLOO.js → chunk-UOTKHSQ5.js} +25 -8
  18. package/dist/chunk-UOTKHSQ5.js.map +1 -0
  19. package/dist/{chunk-W7GAXL6C.js → chunk-V7PT6ER5.js} +6 -5
  20. package/dist/chunk-V7PT6ER5.js.map +1 -0
  21. package/dist/{chunk-F4BJ3DXW.js → chunk-XSNW6Y3X.js} +1253 -315
  22. package/dist/chunk-XSNW6Y3X.js.map +1 -0
  23. package/dist/{chunk-2WH2Q4TA.js → chunk-YJPCS2GP.js} +256 -68
  24. package/dist/chunk-YJPCS2GP.js.map +1 -0
  25. package/dist/{chunk-TLWGZVPC.js → chunk-YNSTIMCF.js} +746 -66
  26. package/dist/chunk-YNSTIMCF.js.map +1 -0
  27. package/dist/{chunk-QN76UJP7.js → chunk-YWDH6DQ6.js} +70 -4
  28. package/dist/chunk-YWDH6DQ6.js.map +1 -0
  29. package/dist/cli/index.js +23 -9
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/{codebuddy-DO7ZhoWn.d.ts → codebuddy-CHqMptZB.d.ts} +35 -0
  32. package/dist/index.d.ts +6 -4
  33. package/dist/index.js +8 -8
  34. package/dist/install-EU35ERK5.js +30 -0
  35. package/dist/langgraph/index.d.ts +1 -1
  36. package/dist/lean-43ADYTBW.js +28 -0
  37. package/dist/{main-YJKSYFPT.js → main-SFC5LV4E.js} +704 -242
  38. package/dist/main-SFC5LV4E.js.map +1 -0
  39. package/dist/{operations-OG4ISWIM.js → operations-6Z6ZDVBP.js} +3 -3
  40. package/dist/{pretool-command-PRFOIHMW.js → pretool-command-B5GY5T6E.js} +2 -2
  41. package/dist/prompt-gate-TDTFFQW2.js +8 -0
  42. package/dist/{runtime-GCSEIAVD.js → runtime-W5AW32SD.js} +14 -6
  43. package/dist/{summaries-ML53R7EL.js → summaries-32PHOB6L.js} +3 -4
  44. package/dist/{zvec-XDZNZ2OL.js → zvec-24HLAFGN.js} +3 -4
  45. package/dist/zvec-24HLAFGN.js.map +1 -0
  46. package/package.json +11 -2
  47. package/dist/chunk-2WH2Q4TA.js.map +0 -1
  48. package/dist/chunk-ENKDFLOO.js.map +0 -1
  49. package/dist/chunk-F4BJ3DXW.js.map +0 -1
  50. package/dist/chunk-GKNYMNFM.js.map +0 -1
  51. package/dist/chunk-ITQO632W.js +0 -673
  52. package/dist/chunk-ITQO632W.js.map +0 -1
  53. package/dist/chunk-J4T6HHI4.js.map +0 -1
  54. package/dist/chunk-QN76UJP7.js.map +0 -1
  55. package/dist/chunk-SRA4MDVG.js.map +0 -1
  56. package/dist/chunk-TLWGZVPC.js.map +0 -1
  57. package/dist/chunk-UK6XTS2Y.js.map +0 -1
  58. package/dist/chunk-W7GAXL6C.js.map +0 -1
  59. package/dist/install-BXM56QCD.js +0 -19
  60. package/dist/lean-command-7DFW32MA.js +0 -19
  61. package/dist/main-YJKSYFPT.js.map +0 -1
  62. /package/dist/{adapters-S4HHTWJW.js.map → adapters-OJRSVFV3.js.map} +0 -0
  63. /package/dist/{chunk-GXOML4UZ.js.map → chunk-LW75SKZR.js.map} +0 -0
  64. /package/dist/{install-BXM56QCD.js.map → install-EU35ERK5.js.map} +0 -0
  65. /package/dist/{lean-command-7DFW32MA.js.map → lean-43ADYTBW.js.map} +0 -0
  66. /package/dist/{operations-OG4ISWIM.js.map → operations-6Z6ZDVBP.js.map} +0 -0
  67. /package/dist/{pretool-command-PRFOIHMW.js.map → pretool-command-B5GY5T6E.js.map} +0 -0
  68. /package/dist/{runtime-GCSEIAVD.js.map → prompt-gate-TDTFFQW2.js.map} +0 -0
  69. /package/dist/{summaries-ML53R7EL.js.map → runtime-W5AW32SD.js.map} +0 -0
  70. /package/dist/{zvec-XDZNZ2OL.js.map → summaries-32PHOB6L.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/map/indexer.ts","../src/indexer/engine.ts","../src/map/cache.ts","../src/indexer/store.ts","../src/indexer/types.ts","../src/indexer/scan.ts","../src/indexer/summary.ts"],"sourcesContent":["import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { dirname, extname, join, relative, resolve } from \"node:path\";\nimport { extractPythonImports } from \"../indexer/python.js\";\nimport type { ArchitectureMap, MapQuery, ModuleEdge, ModuleNode } from \"./types.js\";\n\nconst SUPPORTED_EXTENSIONS = new Set([\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\", \".py\"]);\nconst IGNORED_DIRECTORIES = new Set([\n \".git\",\n \"node_modules\",\n \"dist\",\n \"build\",\n \"coverage\",\n \".codebuddy\",\n // Python virtualenvs and caches would otherwise map site-packages.\n \".venv\",\n \"venv\",\n \".tox\",\n \"__pycache__\",\n]);\n\nexport async function buildArchitectureMap(\n repositoryRoot = process.cwd(),\n): Promise<ArchitectureMap> {\n const root = resolve(repositoryRoot);\n const files = await listSourceFiles(root);\n const fileSet = new Set(files.map((file) => normalize(relative(root, file))));\n const modules: ModuleNode[] = [];\n const edges: ModuleEdge[] = [];\n\n for (const absolutePath of files) {\n const source = await readFile(absolutePath, \"utf8\");\n const from = normalize(relative(root, absolutePath));\n const resolvedImports = moduleImports(root, from, source, fileSet);\n edges.push(...resolvedImports.map(({ to, kind }) => ({ from, to, kind })));\n modules.push({\n path: from,\n language: moduleLanguage(from),\n imports: [...new Set(resolvedImports.map(({ to }) => to))].sort(),\n });\n }\n\n return {\n modules: modules.sort((left, right) => left.path.localeCompare(right.path)),\n edges: edges.sort((left, right) =>\n `${left.from}:${left.to}`.localeCompare(`${right.from}:${right.to}`),\n ),\n };\n}\n\nexport type IncrementalMapStats = { readFiles: number; reusedFiles: number; rebuilt: boolean };\n\n/** Build or update a map from an already-known source file set. */\nexport async function updateArchitectureMap(\n repositoryRoot: string,\n sourcePaths: string[],\n previous: ArchitectureMap | null,\n changedPaths: string[],\n): Promise<{ map: ArchitectureMap; stats: IncrementalMapStats }> {\n const root = resolve(repositoryRoot);\n const normalizedPaths = sourcePaths.map(normalize).sort();\n const fileSet = new Set(normalizedPaths);\n const changed = new Set(changedPaths.map(normalize));\n const retainedModules = (previous?.modules ?? []).filter(\n (module) => fileSet.has(module.path) && !changed.has(module.path),\n );\n const retainedEdges = (previous?.edges ?? []).filter(\n (edge) => fileSet.has(edge.from) && fileSet.has(edge.to) && !changed.has(edge.from),\n );\n const modules = [...retainedModules];\n const edges = [...retainedEdges];\n\n for (const path of normalizedPaths) {\n if (!changed.has(path)) continue;\n const source = await readFile(join(root, path), \"utf8\");\n const resolvedImports = moduleImports(root, path, source, fileSet);\n edges.push(...resolvedImports.map(({ to, kind }) => ({ from: path, to, kind })));\n modules.push({\n path,\n language: moduleLanguage(path),\n imports: [...new Set(resolvedImports.map(({ to }) => to))].sort(),\n });\n }\n\n return {\n map: {\n modules: modules.sort((left, right) => left.path.localeCompare(right.path)),\n edges: edges.sort((left, right) =>\n `${left.from}:${left.to}`.localeCompare(`${right.from}:${right.to}`),\n ),\n },\n stats: {\n readFiles: changed.size,\n reusedFiles: retainedModules.length,\n rebuilt: previous === null || changed.size === normalizedPaths.length,\n },\n };\n}\n\nexport function queryMap(map: ArchitectureMap, query: MapQuery): ModuleEdge[] {\n const limit = query.limit ?? 50;\n return map.edges\n .filter((edge) => (query.from ? edge.from === normalize(query.from) : true))\n .filter((edge) => (query.to ? edge.to === normalize(query.to) : true))\n .slice(0, limit);\n}\n\nexport function neighbours(\n map: ArchitectureMap,\n modulePath: string,\n direction: \"in\" | \"out\" | \"both\" = \"both\",\n): { modules: ModuleNode[]; edges: ModuleEdge[] } {\n const target = normalize(modulePath);\n const edges = map.edges.filter((edge) => {\n if (direction === \"in\") return edge.to === target;\n if (direction === \"out\") return edge.from === target;\n return edge.from === target || edge.to === target;\n });\n const paths = new Set<string>([target]);\n for (const edge of edges) {\n paths.add(edge.from);\n paths.add(edge.to);\n }\n return {\n modules: map.modules.filter((module) => paths.has(module.path)),\n edges,\n };\n}\n\nasync function listSourceFiles(root: string): Promise<string[]> {\n const files: string[] = [];\n await walk(root, files);\n return files.sort();\n}\n\nasync function walk(directory: string, files: string[]): Promise<void> {\n const entries = await readdir(directory, { withFileTypes: true });\n for (const entry of entries) {\n const absolute = join(directory, entry.name);\n if (entry.isDirectory()) {\n if (IGNORED_DIRECTORIES.has(entry.name)) continue;\n await walk(absolute, files);\n continue;\n }\n if (!entry.isFile()) continue;\n if (!SUPPORTED_EXTENSIONS.has(extname(entry.name))) continue;\n const info = await stat(absolute);\n if (info.size > 500_000) continue;\n files.push(absolute);\n }\n}\n\nfunction moduleLanguage(path: string): ModuleNode[\"language\"] {\n const extension = extname(path);\n if (extension === \".py\") return \"py\";\n return extension.includes(\"ts\") ? \"ts\" : \"js\";\n}\n\n/** Resolved in-repo imports of one module; external specifiers yield nothing. */\nfunction moduleImports(\n root: string,\n from: string,\n source: string,\n fileSet: Set<string>,\n): Array<{ to: string; kind: ModuleEdge[\"kind\"] }> {\n if (extname(from) === \".py\") {\n return pythonImports(from, source, fileSet).map((to) => ({ to, kind: \"import\" as const }));\n }\n return extractImports(source).flatMap((specifier) => {\n const to = resolveImport(root, from, specifier.value, fileSet);\n return to ? [{ to, kind: specifier.kind }] : [];\n });\n}\n\n/** Where absolute Python imports are looked up: the repo root, then a `src/` layout. */\nconst PYTHON_ROOTS = [\"\", \"src/\"];\n\n/**\n * Python imports resolved to repo files. Absolute modules are looked up under\n * `PYTHON_ROOTS` (first root that has them wins), relative ones from the\n * importing file's package. In `from pkg import name`, a `name` that is a\n * submodule gets its own edge; other names point at the package. Anything not\n * found (stdlib, third-party) is external and yields no edge.\n */\nfunction pythonImports(from: string, source: string, fileSet: Set<string>): string[] {\n const pick = (...candidates: string[]) =>\n candidates.find((candidate) => fileSet.has(candidate)) ?? null;\n const moduleFile = (base: string) => pick(`${base}.py`, `${base}/__init__.py`);\n const packageDir = from.split(\"/\").slice(0, -1);\n const targets: string[] = [];\n for (const statement of extractPythonImports(source)) {\n const parts = statement.module ? statement.module.split(\".\") : [];\n let bases: string[];\n if (statement.level > 0) {\n const up = statement.level - 1;\n if (up > packageDir.length) continue;\n bases = [[...packageDir.slice(0, packageDir.length - up), ...parts].join(\"/\")];\n } else {\n bases = PYTHON_ROOTS.map((prefix) => `${prefix}${parts.join(\"/\")}`);\n }\n for (const base of bases) {\n const prefix = base ? `${base}/` : \"\";\n // `from . import x` names the package itself, which only an __init__ can be.\n const module = parts.length > 0 ? moduleFile(base) : pick(`${prefix}__init__.py`);\n const submodules = statement.names.flatMap((name) => moduleFile(`${prefix}${name}`) ?? []);\n if (!module && submodules.length === 0) continue;\n targets.push(...submodules);\n if (module && submodules.length < Math.max(1, statement.names.length)) targets.push(module);\n break;\n }\n }\n return [...new Set(targets)].filter((target) => target !== from);\n}\n\nfunction extractImports(source: string): Array<{ value: string; kind: ModuleEdge[\"kind\"] }> {\n const imports: Array<{ value: string; kind: ModuleEdge[\"kind\"] }> = [];\n // Strip line/block comments first so quoted specifiers inside comments can't\n // fabricate phantom edges in the architecture map.\n const stripped = source.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \" \").replace(/(^|[^:])\\/\\/[^\\n]*/g, \"$1 \");\n const staticImport = /\\bimport\\s+(?:type\\s+)?(?:[^'\"()+]+?\\s+from\\s+)?[\"']([^\"']+)[\"']/g;\n const exportFrom = /\\bexport\\s+[^'\"()+]+?\\s+from\\s+[\"']([^\"']+)[\"']/g;\n const dynamicImport = /\\bimport\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\n\n for (const match of stripped.matchAll(staticImport)) {\n if (match[1]) imports.push({ value: match[1], kind: \"import\" });\n }\n for (const match of stripped.matchAll(exportFrom)) {\n if (match[1]) imports.push({ value: match[1], kind: \"import\" });\n }\n for (const match of stripped.matchAll(dynamicImport)) {\n if (match[1]) imports.push({ value: match[1], kind: \"dynamic_import\" });\n }\n return imports;\n}\n\nfunction resolveImport(\n root: string,\n from: string,\n specifier: string,\n fileSet: Set<string>,\n): string | null {\n if (!specifier.startsWith(\".\")) return null;\n const base = normalize(relative(root, resolve(root, dirname(from), specifier)));\n // ESM specifiers point at emitted JS (`./x.js`), but the source on disk is\n // often `./x.ts`. Try the specifier EXACTLY as written first (so a real\n // `x.js` file wins when both `x.ts` and `x.js` exist), then fall back to the\n // extension-stripped stem for the TS-source case.\n const stem = base.replace(/\\.(js|jsx|mjs|cjs)$/, \"\");\n const bases = stem === base ? [base] : [base, stem];\n const candidates = bases.flatMap((b) => [\n b,\n `${b}.ts`,\n `${b}.tsx`,\n `${b}.js`,\n `${b}.jsx`,\n `${b}.mjs`,\n `${b}.cjs`,\n `${b}/index.ts`,\n `${b}/index.tsx`,\n `${b}/index.js`,\n `${b}/index.jsx`,\n ]);\n return candidates.find((candidate) => fileSet.has(candidate)) ?? null;\n}\n\nfunction normalize(path: string): string {\n return path.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n}\n","import { createHash } from \"node:crypto\";\nimport type { Stats } from \"node:fs\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { refreshArchitectureCache } from \"../map/cache.js\";\nimport { listIndexableFiles } from \"./scan.js\";\nimport { emptyManifest, IndexStore } from \"./store.js\";\nimport { summarizeSource } from \"./summary.js\";\nimport { extractSymbolTable, supportsSymbols } from \"./symbols.js\";\nimport {\n INDEX_MANIFEST_VERSION,\n type IndexEntry,\n type IndexError,\n type IndexManifest,\n type IndexResult,\n} from \"./types.js\";\n\n/**\n * Incremental repository indexing. A file is re-summarized only when its\n * content hash differs from the manifest, so repeated runs over an unchanged\n * tree are cheap and deterministic. A file whose size, mtime and ctime still\n * match the entry is not even re-read. Per-file failures (unreadable, too\n * large) are isolated and reported; they never abort the pass. `full` ignores\n * existing hashes and rebuilds every entry.\n *\n * The same pass doubles as use-time validation (`loadFreshIndex`): context\n * tools run it before trusting indexed symbols, summaries or imports, so a CLI\n * or hook process with no watcher still sees current source.\n */\n\n/** Skip files larger than this — summarizing them is neither cheap nor useful. */\nconst MAX_FILE_BYTES = 512_000;\n/**\n * Files changed this recently are always re-hashed: a second edit inside a\n * coarse mtime tick could otherwise keep the same size and timestamps.\n */\nconst RACY_WINDOW_MS = 3_000;\n\nexport type IndexOptions = {\n repositoryRoot?: string;\n full?: boolean;\n /** Injectable clock for deterministic tests. */\n now?: () => Date;\n};\n\nexport async function indexRepository(options: IndexOptions = {}): Promise<IndexResult> {\n return (await refreshIndex(options)).result;\n}\n\nexport type FreshIndex = {\n manifest: IndexManifest;\n /** One `index:` explain line: how the manifest compared with current source. */\n explain: string;\n};\n\n/**\n * The manifest, validated against current source right now. Changed files are\n * re-summarized (symbols, imports), deleted ones dropped, new ones added, and\n * the result is persisted so the next call takes the metadata fast path. Never\n * throws: if validation fails the stored manifest is returned with a reason.\n */\nexport async function loadFreshIndex(repositoryRoot: string): Promise<FreshIndex> {\n try {\n const { result, manifest } = await refreshIndex({ repositoryRoot });\n const delta = result.added + result.changed + result.removed;\n const failures =\n result.errors.length > 0\n ? `; ${result.errors.length} file(s) could not be validated and were omitted`\n : \"\";\n return {\n manifest,\n explain:\n delta === 0\n ? `index: ${result.totalIndexed} indexed file(s) match current source${failures}`\n : `index: refreshed against current source (${result.changed} changed, ${result.added} added, ${result.removed} removed; ${result.totalIndexed} indexed)${failures}`,\n };\n } catch (error) {\n return {\n manifest: emptyManifest(),\n explain: `index: freshness check failed (${(error as Error).message}); indexed evidence omitted`,\n };\n }\n}\n\n// ponytail: in-process serialization only; separate processes (hook + MCP\n// server) can still race, which the metadata check heals on the next pass.\nconst passes = new Map<string, Promise<unknown>>();\n\n/** One pass at a time per repository, so overlapping callers never interleave writes. */\nfunction refreshIndex(\n options: IndexOptions,\n): Promise<{ result: IndexResult; manifest: IndexManifest }> {\n const key = resolve(options.repositoryRoot ?? process.cwd());\n const next = (passes.get(key) ?? Promise.resolve())\n .catch(() => undefined)\n .then(() => runIndexPass(options));\n passes.set(key, next);\n void next\n .finally(() => {\n if (passes.get(key) === next) passes.delete(key);\n })\n .catch(() => undefined);\n return next;\n}\n\nasync function runIndexPass(\n options: IndexOptions,\n): Promise<{ result: IndexResult; manifest: IndexManifest }> {\n const start = Date.now();\n const now = options.now ?? (() => new Date());\n const repositoryRoot = resolve(options.repositoryRoot ?? process.cwd());\n const store = new IndexStore(repositoryRoot);\n\n const [files, previous] = await Promise.all([listIndexableFiles(repositoryRoot), store.read()]);\n\n const entries: Record<string, IndexEntry> = {};\n const errors: IndexError[] = [];\n let added = 0;\n let changed = 0;\n let unchanged = 0;\n let skipped = 0;\n // A never-written manifest is persisted even when the tree is empty.\n let dirty = previous.generatedAt === emptyManifest().generatedAt;\n\n for (const path of files) {\n try {\n const info = await stat(join(repositoryRoot, path));\n if (!info.isFile()) continue;\n if (info.size > MAX_FILE_BYTES) {\n skipped++;\n continue;\n }\n\n const existing = previous.entries[path];\n if (!options.full && existing && statUnchanged(existing, info)) {\n entries[path] = existing;\n unchanged++;\n continue;\n }\n\n const content = await readFile(join(repositoryRoot, path), \"utf8\");\n const hash = hashContent(content);\n const metadata = trustedMetadata(info);\n dirty = true;\n\n if (!options.full && existing && existing.hash === hash) {\n const { mtimeMs: _mtime, ctimeMs: _ctime, ...rest } = existing;\n entries[path] = { ...rest, size: info.size, ...metadata };\n unchanged++;\n continue;\n }\n\n const { language, lines, symbols, summary } = summarizeSource(path, content);\n const symbolTable = supportsSymbols(path) ? extractSymbolTable(content, path) : [];\n entries[path] = {\n path,\n hash,\n size: info.size,\n ...metadata,\n language,\n lines,\n symbols,\n ...(symbolTable.length > 0 ? { symbolTable } : {}),\n summary,\n indexedAt: now().toISOString(),\n };\n if (existing) changed++;\n else added++;\n } catch (error) {\n errors.push({ path, error: (error as Error).message });\n skipped++;\n }\n }\n\n const removed = Object.keys(previous.entries).filter((path) => !(path in entries)).length;\n\n const manifest: IndexManifest = {\n version: INDEX_MANIFEST_VERSION,\n generatedAt: now().toISOString(),\n entries: sortEntries(entries),\n };\n if (dirty || removed > 0) await store.write(manifest);\n const architecture = await refreshArchitectureCache(repositoryRoot, manifest);\n\n const result: IndexResult = {\n scanned: files.length,\n added,\n changed,\n unchanged,\n removed,\n skipped,\n errors,\n durationMs: Date.now() - start,\n totalIndexed: Object.keys(entries).length,\n architecture: {\n source: architecture.source,\n readFiles: architecture.stats.readFiles,\n reusedFiles: architecture.stats.reusedFiles,\n },\n };\n return { result, manifest };\n}\n\nfunction statUnchanged(entry: IndexEntry, info: Stats): boolean {\n return (\n entry.mtimeMs !== undefined &&\n entry.size === info.size &&\n entry.mtimeMs === info.mtimeMs &&\n entry.ctimeMs === info.ctimeMs\n );\n}\n\n/** Stat metadata worth trusting later, or nothing while the file is still racy. */\nfunction trustedMetadata(info: Stats): { mtimeMs?: number; ctimeMs?: number } {\n if (Date.now() - Math.max(info.mtimeMs, info.ctimeMs) < RACY_WINDOW_MS) return {};\n return { mtimeMs: info.mtimeMs, ctimeMs: info.ctimeMs };\n}\n\nexport function hashContent(content: string): string {\n return createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\");\n}\n\nfunction sortEntries(entries: Record<string, IndexEntry>): Record<string, IndexEntry> {\n return Object.fromEntries(\n Object.keys(entries)\n .sort()\n .map((key) => [key, entries[key] as IndexEntry]),\n );\n}\n","import { mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, extname, join, resolve } from \"node:path\";\nimport { assertNoSymlinkBetween, writeAtomic } from \"../core/markdown-store-fs.js\";\nimport { IndexStore } from \"../indexer/store.js\";\nimport type { IndexManifest } from \"../indexer/types.js\";\nimport {\n buildArchitectureMap,\n type IncrementalMapStats,\n updateArchitectureMap,\n} from \"./indexer.js\";\nimport type { ArchitectureMap } from \"./types.js\";\n\nconst CACHE_VERSION = 1;\nconst SOURCE_EXTENSIONS = new Set([\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\", \".py\"]);\n\ntype ArchitectureCache = {\n version: 1;\n generatedAt: string;\n sourceHashes: Record<string, string>;\n map: ArchitectureMap;\n};\n\nexport type CachedArchitectureResult = {\n map: ArchitectureMap;\n stats: IncrementalMapStats;\n source: \"cache\" | \"incremental\" | \"rebuild\" | \"fallback\";\n};\n\nexport async function loadArchitectureMap(\n repositoryRoot = process.cwd(),\n suppliedManifest?: IndexManifest,\n): Promise<CachedArchitectureResult> {\n const root = resolve(repositoryRoot);\n const manifest = suppliedManifest ?? (await new IndexStore(root).read());\n if (Object.keys(manifest.entries).length === 0) {\n const map = await buildArchitectureMap(root);\n return {\n map,\n stats: { readFiles: map.modules.length, reusedFiles: 0, rebuilt: true },\n source: \"fallback\",\n };\n }\n return refreshArchitectureCache(root, manifest);\n}\n\nexport async function refreshArchitectureCache(\n repositoryRoot: string,\n manifest: IndexManifest,\n): Promise<CachedArchitectureResult> {\n const root = resolve(repositoryRoot);\n const path = join(root, \".codebuddy\", \"cache\", \"architecture.json\");\n const currentHashes = sourceHashes(manifest);\n const currentPaths = Object.keys(currentHashes).sort();\n const cached = await readCache(path);\n const cachedPaths = cached ? Object.keys(cached.sourceHashes).sort() : [];\n const fileSetChanged = cachedPaths.join(\"\\n\") !== currentPaths.join(\"\\n\");\n const changedPaths =\n !cached || fileSetChanged\n ? currentPaths\n : currentPaths.filter(\n (sourcePath) => cached.sourceHashes[sourcePath] !== currentHashes[sourcePath],\n );\n\n if (cached && changedPaths.length === 0) {\n return {\n map: cached.map,\n stats: { readFiles: 0, reusedFiles: cached.map.modules.length, rebuilt: false },\n source: \"cache\",\n };\n }\n\n const updated = await updateArchitectureMap(\n root,\n currentPaths,\n fileSetChanged ? null : (cached?.map ?? null),\n changedPaths,\n );\n const next: ArchitectureCache = {\n version: CACHE_VERSION,\n generatedAt: manifest.generatedAt,\n sourceHashes: currentHashes,\n map: updated.map,\n };\n await assertNoSymlinkBetween(root, dirname(path));\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n await writeAtomic(path, `${JSON.stringify(next, null, 2)}\\n`);\n return { ...updated, source: fileSetChanged || !cached ? \"rebuild\" : \"incremental\" };\n}\n\nasync function readCache(path: string): Promise<ArchitectureCache | null> {\n try {\n const parsed = JSON.parse(await readFile(path, \"utf8\")) as ArchitectureCache;\n if (parsed.version !== CACHE_VERSION || !parsed.map || !parsed.sourceHashes) return null;\n if (!Array.isArray(parsed.map.modules) || !Array.isArray(parsed.map.edges)) return null;\n if (\n !parsed.map.modules.every(\n (module) =>\n module &&\n typeof module.path === \"string\" &&\n typeof module.language === \"string\" &&\n Array.isArray(module.imports),\n ) ||\n !parsed.map.edges.every(\n (edge) => edge && typeof edge.from === \"string\" && typeof edge.to === \"string\",\n )\n ) {\n return null;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction sourceHashes(manifest: IndexManifest): Record<string, string> {\n return Object.fromEntries(\n Object.values(manifest.entries)\n .filter((entry) => SOURCE_EXTENSIONS.has(extname(entry.path).toLowerCase()))\n .map((entry) => [entry.path, entry.hash]),\n );\n}\n","import { mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport { assertNoSymlinkBetween, writeAtomic } from \"../core/markdown-store-fs.js\";\nimport { INDEX_MANIFEST_VERSION, type IndexManifest } from \"./types.js\";\n\n/**\n * Persists the index manifest as a single JSON file under\n * `.codebuddy/cache/index.json`. `cache/` is gitignored by the project\n * scaffold, so the manifest is treated as a rebuildable derived artifact —\n * corrupt or version-mismatched manifests degrade to \"empty\" rather than\n * throwing, and a full `codebuddy index` regenerates them.\n */\n\nconst symbolSpanSchema = z.object({\n name: z.string(),\n kind: z.enum([\"function\", \"method\", \"class\", \"interface\", \"type\", \"enum\", \"const\", \"heading\"]),\n startLine: z.number().int().nonnegative(),\n endLine: z.number().int().nonnegative(),\n signature: z.string(),\n exported: z.boolean(),\n});\n\nconst entrySchema = z.object({\n path: z.string(),\n hash: z.string(),\n size: z.number().int().nonnegative(),\n mtimeMs: z.number().optional(),\n ctimeMs: z.number().optional(),\n language: z.string(),\n lines: z.number().int().nonnegative(),\n symbols: z.array(z.string()),\n symbolTable: z.array(symbolSpanSchema).optional(),\n summary: z.string(),\n indexedAt: z.string(),\n});\n\nconst manifestSchema = z.object({\n version: z.literal(INDEX_MANIFEST_VERSION),\n generatedAt: z.string(),\n entries: z.record(z.string(), entrySchema),\n});\n\nexport class IndexStore {\n readonly repositoryRoot: string;\n readonly cacheDirectory: string;\n readonly manifestPath: string;\n\n constructor(repositoryRoot = process.cwd()) {\n this.repositoryRoot = resolve(repositoryRoot);\n this.cacheDirectory = join(this.repositoryRoot, \".codebuddy\", \"cache\");\n this.manifestPath = join(this.cacheDirectory, \"index.json\");\n }\n\n /** Read the manifest, or an empty one if absent/corrupt/version-mismatched. */\n async read(): Promise<IndexManifest> {\n try {\n const raw = await readFile(this.manifestPath, \"utf8\");\n const parsed = manifestSchema.safeParse(JSON.parse(raw));\n if (!parsed.success) return emptyManifest();\n // Zod's optional() widens to `| undefined`; the parse guarantees the shape.\n return parsed.data as IndexManifest;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return emptyManifest();\n // Unreadable cache is non-fatal: rebuild from scratch.\n return emptyManifest();\n }\n }\n\n async write(manifest: IndexManifest): Promise<void> {\n await assertNoSymlinkBetween(this.repositoryRoot, dirname(this.manifestPath));\n await mkdir(this.cacheDirectory, { recursive: true, mode: 0o700 });\n await writeAtomic(this.manifestPath, `${JSON.stringify(manifest, null, 2)}\\n`);\n }\n}\n\nexport function emptyManifest(): IndexManifest {\n return { version: INDEX_MANIFEST_VERSION, generatedAt: new Date(0).toISOString(), entries: {} };\n}\n","/**\n * Background index model (Proposal 04 Phase 04.3). The indexer builds a\n * deterministic, local-first picture of the repository — one content hash and\n * one lightweight summary per indexable file — so CodeBuddy knows something\n * useful before any memory is manually written. It is a derived *cache* under\n * `.codebuddy/cache/`, never a source of truth: deleting it and re-running\n * `codebuddy index` reproduces it exactly. No LLM is involved; summaries are\n * extracted with simple, stable heuristics.\n */\n\nimport type { SymbolSpan } from \"./symbols.js\";\n\n/** Bumped when symbol tables change shape (2: class methods; 3: Python, doc headings) so old caches rebuild. */\nexport const INDEX_MANIFEST_VERSION = 3 as const;\n\n/** One indexed file. Everything here is reproducible from the file contents. */\nexport type IndexEntry = {\n /** Repo-relative POSIX path. */\n path: string;\n /** SHA-256 of the file contents; the authoritative change-detection signal. */\n hash: string;\n /** Byte size at index time. */\n size: number;\n /**\n * File mtime/ctime observed when `hash` was taken. Recorded only once the file\n * is older than the racy window, so a matching size+mtime+ctime is a reliable\n * \"unchanged\" shortcut; when absent the file is re-hashed.\n */\n mtimeMs?: number;\n ctimeMs?: number;\n /** Coarse language tag derived from the extension (ts, js, md, py, …). */\n language: string;\n /** Line count (excluding a single trailing newline). */\n lines: number;\n /** Best-effort top-level/exported symbol names. Empty for non-code files. */\n symbols: string[];\n /**\n * Optional richer symbol table (name/kind/range/signature) for code files —\n * the basis for symbol-level context (Roadmap N.3). Absent for non-code files\n * and older manifests, so consumers must treat it as optional.\n */\n symbolTable?: SymbolSpan[];\n /** Deterministic one-line summary. */\n summary: string;\n /** ISO timestamp of when this entry was (re)computed. */\n indexedAt: string;\n};\n\nexport type IndexManifest = {\n version: typeof INDEX_MANIFEST_VERSION;\n generatedAt: string;\n /** Keyed by repo-relative POSIX path. */\n entries: Record<string, IndexEntry>;\n};\n\nexport type IndexError = {\n path: string;\n error: string;\n};\n\n/** Outcome of one indexing pass. Counts are disjoint where noted. */\nexport type IndexResult = {\n scanned: number;\n added: number;\n changed: number;\n /** Unchanged files whose summary was reused (the incremental fast path). */\n unchanged: number;\n /** Entries dropped because the file no longer exists or is now ignored. */\n removed: number;\n /** Files skipped as too large or unreadable (isolated, non-fatal). */\n skipped: number;\n errors: IndexError[];\n durationMs: number;\n totalIndexed: number;\n architecture: {\n source: \"cache\" | \"incremental\" | \"rebuild\" | \"fallback\";\n readFiles: number;\n reusedFiles: number;\n };\n};\n","import { spawn } from \"node:child_process\";\nimport { readdir } from \"node:fs/promises\";\nimport { extname, join, relative, sep } from \"node:path\";\nimport { SUPPORTED_EXTENSIONS } from \"./summary.js\";\n\n/**\n * Enumerate the repo-relative POSIX paths the indexer should consider.\n *\n * `.gitignore` respect is delegated to git itself: `git ls-files` lists tracked\n * plus untracked-but-not-ignored files, which is exactly the set a developer\n * expects to be \"in the project\". When the directory is not a git repository we\n * fall back to a plain walk with a conservative ignore list so indexing still\n * works on partially configured repos.\n *\n * CodeBuddy's own generated artifacts (`.codebuddy/`, `graphify-out/`) are\n * always excluded — the index does not index itself.\n */\n\nconst FALLBACK_IGNORED_DIRECTORIES = new Set([\n \".git\",\n \"node_modules\",\n \"dist\",\n \"build\",\n \"coverage\",\n \".codebuddy\",\n \"graphify-out\",\n \".next\",\n \".turbo\",\n \"out\",\n \".venv\",\n \"venv\",\n \"__pycache__\",\n]);\n\nexport async function listIndexableFiles(repositoryRoot: string): Promise<string[]> {\n const tracked = await gitListFiles(repositoryRoot);\n const paths = tracked ?? (await walkFiles(repositoryRoot));\n return paths\n .map(normalize)\n .filter((path) => !path.startsWith(\".codebuddy/\") && !path.startsWith(\"graphify-out/\"))\n .filter((path) => SUPPORTED_EXTENSIONS.has(extname(path).toLowerCase()))\n .sort();\n}\n\n/** Returns null when the directory is not a git repository. */\nasync function gitListFiles(repositoryRoot: string): Promise<string[] | null> {\n // `-z`: NUL-separated and unquoted, so names with non-ASCII, quotes, tabs\n // or newlines arrive verbatim instead of C-quoted.\n const result = await runGit(repositoryRoot, [\n \"ls-files\",\n \"-z\",\n \"--cached\",\n \"--others\",\n \"--exclude-standard\",\n ]);\n if (result.code !== 0) return null;\n return result.stdout.split(\"\\0\").filter(Boolean);\n}\n\nasync function walkFiles(root: string): Promise<string[]> {\n const files: string[] = [];\n const walk = async (directory: string): Promise<void> => {\n const entries = await readdir(directory, { withFileTypes: true });\n for (const entry of entries) {\n const absolute = join(directory, entry.name);\n if (entry.isDirectory()) {\n if (FALLBACK_IGNORED_DIRECTORIES.has(entry.name)) continue;\n await walk(absolute);\n continue;\n }\n if (entry.isFile()) files.push(relative(root, absolute));\n }\n };\n await walk(root);\n return files;\n}\n\nfunction runGit(\n cwd: string,\n args: string[],\n): Promise<{ code: number; stdout: string; stderr: string }> {\n return new Promise((resolveResult) => {\n const child = spawn(\"git\", args, { cwd, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n let stdout = \"\";\n let stderr = \"\";\n // Decode as one stream so a multi-byte character split across chunks survives.\n child.stdout.setEncoding(\"utf8\");\n child.stdout.on(\"data\", (chunk) => {\n stdout += String(chunk);\n });\n child.stderr.on(\"data\", (chunk) => {\n stderr += String(chunk);\n });\n // A missing git binary should degrade to the walk fallback, not crash.\n child.on(\"error\", () => resolveResult({ code: -1, stdout: \"\", stderr: \"git unavailable\" }));\n child.on(\"close\", (code) => resolveResult({ code: code ?? 0, stdout, stderr }));\n });\n}\n\n/** POSIX form; a backslash is a separator only on Windows, elsewhere part of the name. */\nfunction normalize(path: string): string {\n return (sep === \"\\\\\" ? path.replace(/\\\\/g, \"/\") : path).replace(/^\\.\\//, \"\");\n}\n","import { extname } from \"node:path\";\nimport { extractSymbolTable } from \"./symbols.js\";\n\n/**\n * Deterministic file summarization. No LLM, no network — the same file always\n * produces the same summary, which keeps the index stable across runs and easy\n * to diff. Symbol extraction is intentionally best-effort regex over source\n * text: good enough to orient an agent, cheap enough to run on every save.\n */\n\nconst LANGUAGE_BY_EXTENSION: Record<string, string> = {\n \".ts\": \"ts\",\n \".tsx\": \"tsx\",\n \".js\": \"js\",\n \".jsx\": \"jsx\",\n \".mjs\": \"js\",\n \".cjs\": \"js\",\n \".json\": \"json\",\n \".md\": \"md\",\n \".mdx\": \"md\",\n \".rst\": \"rst\",\n \".yaml\": \"yaml\",\n \".yml\": \"yaml\",\n \".toml\": \"toml\",\n \".css\": \"css\",\n \".scss\": \"scss\",\n \".html\": \"html\",\n \".py\": \"py\",\n \".go\": \"go\",\n \".rs\": \"rust\",\n \".java\": \"java\",\n \".rb\": \"ruby\",\n \".php\": \"php\",\n \".sh\": \"shell\",\n \".sql\": \"sql\",\n \".txt\": \"text\",\n};\n\n/** Extensions the indexer will read and summarize. */\nexport const SUPPORTED_EXTENSIONS = new Set(Object.keys(LANGUAGE_BY_EXTENSION));\n\nconst CODE_LANGUAGES = new Set([\"ts\", \"tsx\", \"js\", \"jsx\"]);\nconst SYMBOL_LIMIT = 8;\nconst SUMMARY_COMMENT_MAX = 80;\n\nexport function languageFor(path: string): string {\n return LANGUAGE_BY_EXTENSION[extname(path).toLowerCase()] ?? \"text\";\n}\n\nexport type FileSummary = {\n language: string;\n lines: number;\n symbols: string[];\n summary: string;\n};\n\nexport function summarizeSource(path: string, content: string): FileSummary {\n const language = languageFor(path);\n const lines = countLines(content);\n const symbols = CODE_LANGUAGES.has(language)\n ? extractSymbols(content)\n : language === \"py\"\n ? pythonPublicNames(content)\n : [];\n const summary = composeSummary(language, lines, symbols, content);\n return { language, lines, symbols, summary };\n}\n\nfunction countLines(content: string): number {\n if (content.length === 0) return 0;\n return content.replace(/\\r/g, \"\").replace(/\\n$/, \"\").split(\"\\n\").length;\n}\n\n/**\n * Pull exported/top-level symbol names from JS/TS source. Covers the common\n * `export <kind> Name` forms, `export default function Name`, and named\n * re-export braces. Order-preserving and de-duplicated.\n */\nexport function extractSymbols(content: string): string[] {\n const names: string[] = [];\n const push = (name: string | undefined) => {\n const trimmed = name?.trim();\n if (trimmed && /^[A-Za-z_$][\\w$]*$/.test(trimmed) && !names.includes(trimmed)) {\n names.push(trimmed);\n }\n };\n\n const declaration =\n /^export\\s+(?:default\\s+)?(?:async\\s+)?(?:abstract\\s+)?(?:const|let|var|function|class|interface|type|enum)\\s+([A-Za-z_$][\\w$]*)/gm;\n for (const match of content.matchAll(declaration)) push(match[1]);\n\n const namedExports = /^export\\s*\\{([^}]*)\\}/gm;\n for (const match of content.matchAll(namedExports)) {\n for (const part of (match[1] ?? \"\").split(\",\")) {\n // Handle `foo as bar` — record the exported name (`bar`).\n const alias = part.includes(\" as \") ? part.split(\" as \").pop() : part;\n push(alias);\n }\n }\n\n return names.slice(0, SYMBOL_LIMIT);\n}\n\n/** Public (no leading underscore) top-level Python names, in file order. */\nfunction pythonPublicNames(content: string): string[] {\n return extractSymbolTable(content, \"x.py\")\n .filter((symbol) => symbol.exported && symbol.kind !== \"method\")\n .map((symbol) => symbol.name)\n .slice(0, SYMBOL_LIMIT);\n}\n\nfunction composeSummary(\n language: string,\n lines: number,\n symbols: string[],\n content: string,\n): string {\n const head = `${language} • ${lines} line${lines === 1 ? \"\" : \"s\"}`;\n if (symbols.length > 0) return `${head} • exports ${symbols.join(\", \")}`;\n const comment = leadingComment(content);\n if (comment) return `${head} • ${comment}`;\n return head;\n}\n\n/** First meaningful line of a leading comment block, if any. */\nfunction leadingComment(content: string): string | null {\n for (const raw of content.split(/\\r?\\n/)) {\n const line = raw.trim();\n if (line === \"\") continue;\n const stripped = line\n .replace(/^\\/\\/+/, \"\")\n .replace(/^\\/\\*+/, \"\")\n .replace(/\\*+\\/$/, \"\")\n .replace(/^[#*]+/, \"\")\n .trim();\n if (\n line.startsWith(\"//\") ||\n line.startsWith(\"/*\") ||\n line.startsWith(\"#\") ||\n line.startsWith(\"*\")\n ) {\n if (stripped) return truncate(stripped, SUMMARY_COMMENT_MAX);\n continue;\n }\n // First non-blank line is not a comment: nothing to summarize.\n return null;\n }\n return null;\n}\n\nfunction truncate(value: string, max: number): string {\n return value.length <= max ? value : `${value.slice(0, max - 1)}…`;\n}\n"],"mappings":";;;;;;;;;;;AAAA,SAAS,SAAS,UAAU,YAAY;AACxC,SAAS,SAAS,SAAS,MAAM,UAAU,eAAe;AAI1D,IAAM,uBAAuB,oBAAI,IAAI,CAAC,OAAO,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,KAAK,CAAC;AAC1F,IAAM,sBAAsB,oBAAI,IAAI;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,eAAsB,qBACpB,iBAAiB,QAAQ,IAAI,GACH;AAC1B,QAAM,OAAO,QAAQ,cAAc;AACnC,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,UAAU,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,UAAU,SAAS,MAAM,IAAI,CAAC,CAAC,CAAC;AAC5E,QAAM,UAAwB,CAAC;AAC/B,QAAM,QAAsB,CAAC;AAE7B,aAAW,gBAAgB,OAAO;AAChC,UAAM,SAAS,MAAM,SAAS,cAAc,MAAM;AAClD,UAAM,OAAO,UAAU,SAAS,MAAM,YAAY,CAAC;AACnD,UAAM,kBAAkB,cAAc,MAAM,MAAM,QAAQ,OAAO;AACjE,UAAM,KAAK,GAAG,gBAAgB,IAAI,CAAC,EAAE,IAAI,KAAK,OAAO,EAAE,MAAM,IAAI,KAAK,EAAE,CAAC;AACzE,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,UAAU,eAAe,IAAI;AAAA,MAC7B,SAAS,CAAC,GAAG,IAAI,IAAI,gBAAgB,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK;AAAA,IAClE,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,SAAS,QAAQ,KAAK,CAAC,MAAM,UAAU,KAAK,KAAK,cAAc,MAAM,IAAI,CAAC;AAAA,IAC1E,OAAO,MAAM;AAAA,MAAK,CAAC,MAAM,UACvB,GAAG,KAAK,IAAI,IAAI,KAAK,EAAE,GAAG,cAAc,GAAG,MAAM,IAAI,IAAI,MAAM,EAAE,EAAE;AAAA,IACrE;AAAA,EACF;AACF;AAKA,eAAsB,sBACpB,gBACA,aACA,UACA,cAC+D;AAC/D,QAAM,OAAO,QAAQ,cAAc;AACnC,QAAM,kBAAkB,YAAY,IAAI,SAAS,EAAE,KAAK;AACxD,QAAM,UAAU,IAAI,IAAI,eAAe;AACvC,QAAM,UAAU,IAAI,IAAI,aAAa,IAAI,SAAS,CAAC;AACnD,QAAM,mBAAmB,UAAU,WAAW,CAAC,GAAG;AAAA,IAChD,CAAC,WAAW,QAAQ,IAAI,OAAO,IAAI,KAAK,CAAC,QAAQ,IAAI,OAAO,IAAI;AAAA,EAClE;AACA,QAAM,iBAAiB,UAAU,SAAS,CAAC,GAAG;AAAA,IAC5C,CAAC,SAAS,QAAQ,IAAI,KAAK,IAAI,KAAK,QAAQ,IAAI,KAAK,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK,IAAI;AAAA,EACpF;AACA,QAAM,UAAU,CAAC,GAAG,eAAe;AACnC,QAAM,QAAQ,CAAC,GAAG,aAAa;AAE/B,aAAW,QAAQ,iBAAiB;AAClC,QAAI,CAAC,QAAQ,IAAI,IAAI,EAAG;AACxB,UAAM,SAAS,MAAM,SAAS,KAAK,MAAM,IAAI,GAAG,MAAM;AACtD,UAAM,kBAAkB,cAAc,MAAM,MAAM,QAAQ,OAAO;AACjE,UAAM,KAAK,GAAG,gBAAgB,IAAI,CAAC,EAAE,IAAI,KAAK,OAAO,EAAE,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;AAC/E,YAAQ,KAAK;AAAA,MACX;AAAA,MACA,UAAU,eAAe,IAAI;AAAA,MAC7B,SAAS,CAAC,GAAG,IAAI,IAAI,gBAAgB,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK;AAAA,IAClE,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,KAAK;AAAA,MACH,SAAS,QAAQ,KAAK,CAAC,MAAM,UAAU,KAAK,KAAK,cAAc,MAAM,IAAI,CAAC;AAAA,MAC1E,OAAO,MAAM;AAAA,QAAK,CAAC,MAAM,UACvB,GAAG,KAAK,IAAI,IAAI,KAAK,EAAE,GAAG,cAAc,GAAG,MAAM,IAAI,IAAI,MAAM,EAAE,EAAE;AAAA,MACrE;AAAA,IACF;AAAA,IACA,OAAO;AAAA,MACL,WAAW,QAAQ;AAAA,MACnB,aAAa,gBAAgB;AAAA,MAC7B,SAAS,aAAa,QAAQ,QAAQ,SAAS,gBAAgB;AAAA,IACjE;AAAA,EACF;AACF;AAEO,SAAS,SAAS,KAAsB,OAA+B;AAC5E,QAAM,QAAQ,MAAM,SAAS;AAC7B,SAAO,IAAI,MACR,OAAO,CAAC,SAAU,MAAM,OAAO,KAAK,SAAS,UAAU,MAAM,IAAI,IAAI,IAAK,EAC1E,OAAO,CAAC,SAAU,MAAM,KAAK,KAAK,OAAO,UAAU,MAAM,EAAE,IAAI,IAAK,EACpE,MAAM,GAAG,KAAK;AACnB;AAEO,SAAS,WACd,KACA,YACA,YAAmC,QACa;AAChD,QAAM,SAAS,UAAU,UAAU;AACnC,QAAM,QAAQ,IAAI,MAAM,OAAO,CAAC,SAAS;AACvC,QAAI,cAAc,KAAM,QAAO,KAAK,OAAO;AAC3C,QAAI,cAAc,MAAO,QAAO,KAAK,SAAS;AAC9C,WAAO,KAAK,SAAS,UAAU,KAAK,OAAO;AAAA,EAC7C,CAAC;AACD,QAAM,QAAQ,oBAAI,IAAY,CAAC,MAAM,CAAC;AACtC,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,KAAK,IAAI;AACnB,UAAM,IAAI,KAAK,EAAE;AAAA,EACnB;AACA,SAAO;AAAA,IACL,SAAS,IAAI,QAAQ,OAAO,CAAC,WAAW,MAAM,IAAI,OAAO,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AACF;AAEA,eAAe,gBAAgB,MAAiC;AAC9D,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,MAAM,KAAK;AACtB,SAAO,MAAM,KAAK;AACpB;AAEA,eAAe,KAAK,WAAmB,OAAgC;AACrE,QAAM,UAAU,MAAM,QAAQ,WAAW,EAAE,eAAe,KAAK,CAAC;AAChE,aAAW,SAAS,SAAS;AAC3B,UAAM,WAAW,KAAK,WAAW,MAAM,IAAI;AAC3C,QAAI,MAAM,YAAY,GAAG;AACvB,UAAI,oBAAoB,IAAI,MAAM,IAAI,EAAG;AACzC,YAAM,KAAK,UAAU,KAAK;AAC1B;AAAA,IACF;AACA,QAAI,CAAC,MAAM,OAAO,EAAG;AACrB,QAAI,CAAC,qBAAqB,IAAI,QAAQ,MAAM,IAAI,CAAC,EAAG;AACpD,UAAM,OAAO,MAAM,KAAK,QAAQ;AAChC,QAAI,KAAK,OAAO,IAAS;AACzB,UAAM,KAAK,QAAQ;AAAA,EACrB;AACF;AAEA,SAAS,eAAe,MAAsC;AAC5D,QAAM,YAAY,QAAQ,IAAI;AAC9B,MAAI,cAAc,MAAO,QAAO;AAChC,SAAO,UAAU,SAAS,IAAI,IAAI,OAAO;AAC3C;AAGA,SAAS,cACP,MACA,MACA,QACA,SACiD;AACjD,MAAI,QAAQ,IAAI,MAAM,OAAO;AAC3B,WAAO,cAAc,MAAM,QAAQ,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,MAAM,SAAkB,EAAE;AAAA,EAC3F;AACA,SAAO,eAAe,MAAM,EAAE,QAAQ,CAAC,cAAc;AACnD,UAAM,KAAK,cAAc,MAAM,MAAM,UAAU,OAAO,OAAO;AAC7D,WAAO,KAAK,CAAC,EAAE,IAAI,MAAM,UAAU,KAAK,CAAC,IAAI,CAAC;AAAA,EAChD,CAAC;AACH;AAGA,IAAM,eAAe,CAAC,IAAI,MAAM;AAShC,SAAS,cAAc,MAAc,QAAgB,SAAgC;AACnF,QAAM,OAAO,IAAI,eACf,WAAW,KAAK,CAAC,cAAc,QAAQ,IAAI,SAAS,CAAC,KAAK;AAC5D,QAAM,aAAa,CAAC,SAAiB,KAAK,GAAG,IAAI,OAAO,GAAG,IAAI,cAAc;AAC7E,QAAM,aAAa,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE;AAC9C,QAAM,UAAoB,CAAC;AAC3B,aAAW,aAAa,qBAAqB,MAAM,GAAG;AACpD,UAAM,QAAQ,UAAU,SAAS,UAAU,OAAO,MAAM,GAAG,IAAI,CAAC;AAChE,QAAI;AACJ,QAAI,UAAU,QAAQ,GAAG;AACvB,YAAM,KAAK,UAAU,QAAQ;AAC7B,UAAI,KAAK,WAAW,OAAQ;AAC5B,cAAQ,CAAC,CAAC,GAAG,WAAW,MAAM,GAAG,WAAW,SAAS,EAAE,GAAG,GAAG,KAAK,EAAE,KAAK,GAAG,CAAC;AAAA,IAC/E,OAAO;AACL,cAAQ,aAAa,IAAI,CAAC,WAAW,GAAG,MAAM,GAAG,MAAM,KAAK,GAAG,CAAC,EAAE;AAAA,IACpE;AACA,eAAW,QAAQ,OAAO;AACxB,YAAM,SAAS,OAAO,GAAG,IAAI,MAAM;AAEnC,YAAM,SAAS,MAAM,SAAS,IAAI,WAAW,IAAI,IAAI,KAAK,GAAG,MAAM,aAAa;AAChF,YAAM,aAAa,UAAU,MAAM,QAAQ,CAAC,SAAS,WAAW,GAAG,MAAM,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC;AACzF,UAAI,CAAC,UAAU,WAAW,WAAW,EAAG;AACxC,cAAQ,KAAK,GAAG,UAAU;AAC1B,UAAI,UAAU,WAAW,SAAS,KAAK,IAAI,GAAG,UAAU,MAAM,MAAM,EAAG,SAAQ,KAAK,MAAM;AAC1F;AAAA,IACF;AAAA,EACF;AACA,SAAO,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,WAAW,IAAI;AACjE;AAEA,SAAS,eAAe,QAAoE;AAC1F,QAAM,UAA8D,CAAC;AAGrE,QAAM,WAAW,OAAO,QAAQ,qBAAqB,GAAG,EAAE,QAAQ,uBAAuB,KAAK;AAC9F,QAAM,eAAe;AACrB,QAAM,aAAa;AACnB,QAAM,gBAAgB;AAEtB,aAAW,SAAS,SAAS,SAAS,YAAY,GAAG;AACnD,QAAI,MAAM,CAAC,EAAG,SAAQ,KAAK,EAAE,OAAO,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC;AAAA,EAChE;AACA,aAAW,SAAS,SAAS,SAAS,UAAU,GAAG;AACjD,QAAI,MAAM,CAAC,EAAG,SAAQ,KAAK,EAAE,OAAO,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC;AAAA,EAChE;AACA,aAAW,SAAS,SAAS,SAAS,aAAa,GAAG;AACpD,QAAI,MAAM,CAAC,EAAG,SAAQ,KAAK,EAAE,OAAO,MAAM,CAAC,GAAG,MAAM,iBAAiB,CAAC;AAAA,EACxE;AACA,SAAO;AACT;AAEA,SAAS,cACP,MACA,MACA,WACA,SACe;AACf,MAAI,CAAC,UAAU,WAAW,GAAG,EAAG,QAAO;AACvC,QAAM,OAAO,UAAU,SAAS,MAAM,QAAQ,MAAM,QAAQ,IAAI,GAAG,SAAS,CAAC,CAAC;AAK9E,QAAM,OAAO,KAAK,QAAQ,uBAAuB,EAAE;AACnD,QAAM,QAAQ,SAAS,OAAO,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI;AAClD,QAAM,aAAa,MAAM,QAAQ,CAAC,MAAM;AAAA,IACtC;AAAA,IACA,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,IACJ,GAAG,CAAC;AAAA,EACN,CAAC;AACD,SAAO,WAAW,KAAK,CAAC,cAAc,QAAQ,IAAI,SAAS,CAAC,KAAK;AACnE;AAEA,SAAS,UAAU,MAAsB;AACvC,SAAO,KAAK,QAAQ,OAAO,GAAG,EAAE,QAAQ,SAAS,EAAE;AACrD;;;AC1QA,SAAS,kBAAkB;AAE3B,SAAS,YAAAA,WAAU,QAAAC,aAAY;AAC/B,SAAS,QAAAC,OAAM,WAAAC,gBAAe;;;ACH9B,SAAS,SAAAC,QAAO,YAAAC,iBAAgB;AAChC,SAAS,WAAAC,UAAS,WAAAC,UAAS,QAAAC,OAAM,WAAAC,gBAAe;;;ACDhD,SAAS,OAAO,YAAAC,iBAAgB;AAChC,SAAS,WAAAC,UAAS,QAAAC,OAAM,WAAAC,gBAAe;AACvC,SAAS,SAAS;;;ACWX,IAAM,yBAAyB;;;ADCtC,IAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,MAAM,EAAE,OAAO;AAAA,EACf,MAAM,EAAE,KAAK,CAAC,YAAY,UAAU,SAAS,aAAa,QAAQ,QAAQ,SAAS,SAAS,CAAC;AAAA,EAC7F,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACxC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACtC,WAAW,EAAE,OAAO;AAAA,EACpB,UAAU,EAAE,QAAQ;AACtB,CAAC;AAED,IAAM,cAAc,EAAE,OAAO;AAAA,EAC3B,MAAM,EAAE,OAAO;AAAA,EACf,MAAM,EAAE,OAAO;AAAA,EACf,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACnC,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,UAAU,EAAE,OAAO;AAAA,EACnB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACpC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAC3B,aAAa,EAAE,MAAM,gBAAgB,EAAE,SAAS;AAAA,EAChD,SAAS,EAAE,OAAO;AAAA,EAClB,WAAW,EAAE,OAAO;AACtB,CAAC;AAED,IAAM,iBAAiB,EAAE,OAAO;AAAA,EAC9B,SAAS,EAAE,QAAQ,sBAAsB;AAAA,EACzC,aAAa,EAAE,OAAO;AAAA,EACtB,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,WAAW;AAC3C,CAAC;AAEM,IAAM,aAAN,MAAiB;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,iBAAiB,QAAQ,IAAI,GAAG;AAC1C,SAAK,iBAAiBC,SAAQ,cAAc;AAC5C,SAAK,iBAAiBC,MAAK,KAAK,gBAAgB,cAAc,OAAO;AACrE,SAAK,eAAeA,MAAK,KAAK,gBAAgB,YAAY;AAAA,EAC5D;AAAA;AAAA,EAGA,MAAM,OAA+B;AACnC,QAAI;AACF,YAAM,MAAM,MAAMC,UAAS,KAAK,cAAc,MAAM;AACpD,YAAM,SAAS,eAAe,UAAU,KAAK,MAAM,GAAG,CAAC;AACvD,UAAI,CAAC,OAAO,QAAS,QAAO,cAAc;AAE1C,aAAO,OAAO;AAAA,IAChB,SAAS,OAAO;AACd,UAAK,MAAgC,SAAS,SAAU,QAAO,cAAc;AAE7E,aAAO,cAAc;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,UAAwC;AAClD,UAAM,uBAAuB,KAAK,gBAAgBC,SAAQ,KAAK,YAAY,CAAC;AAC5E,UAAM,MAAM,KAAK,gBAAgB,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACjE,UAAM,YAAY,KAAK,cAAc,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,CAAI;AAAA,EAC/E;AACF;AAEO,SAAS,gBAA+B;AAC7C,SAAO,EAAE,SAAS,wBAAwB,cAAa,oBAAI,KAAK,CAAC,GAAE,YAAY,GAAG,SAAS,CAAC,EAAE;AAChG;;;ADlEA,IAAM,gBAAgB;AACtB,IAAM,oBAAoB,oBAAI,IAAI,CAAC,OAAO,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,KAAK,CAAC;AAevF,eAAsB,oBACpB,iBAAiB,QAAQ,IAAI,GAC7B,kBACmC;AACnC,QAAM,OAAOC,SAAQ,cAAc;AACnC,QAAM,WAAW,oBAAqB,MAAM,IAAI,WAAW,IAAI,EAAE,KAAK;AACtE,MAAI,OAAO,KAAK,SAAS,OAAO,EAAE,WAAW,GAAG;AAC9C,UAAM,MAAM,MAAM,qBAAqB,IAAI;AAC3C,WAAO;AAAA,MACL;AAAA,MACA,OAAO,EAAE,WAAW,IAAI,QAAQ,QAAQ,aAAa,GAAG,SAAS,KAAK;AAAA,MACtE,QAAQ;AAAA,IACV;AAAA,EACF;AACA,SAAO,yBAAyB,MAAM,QAAQ;AAChD;AAEA,eAAsB,yBACpB,gBACA,UACmC;AACnC,QAAM,OAAOA,SAAQ,cAAc;AACnC,QAAM,OAAOC,MAAK,MAAM,cAAc,SAAS,mBAAmB;AAClE,QAAM,gBAAgB,aAAa,QAAQ;AAC3C,QAAM,eAAe,OAAO,KAAK,aAAa,EAAE,KAAK;AACrD,QAAM,SAAS,MAAM,UAAU,IAAI;AACnC,QAAM,cAAc,SAAS,OAAO,KAAK,OAAO,YAAY,EAAE,KAAK,IAAI,CAAC;AACxE,QAAM,iBAAiB,YAAY,KAAK,IAAI,MAAM,aAAa,KAAK,IAAI;AACxE,QAAM,eACJ,CAAC,UAAU,iBACP,eACA,aAAa;AAAA,IACX,CAAC,eAAe,OAAO,aAAa,UAAU,MAAM,cAAc,UAAU;AAAA,EAC9E;AAEN,MAAI,UAAU,aAAa,WAAW,GAAG;AACvC,WAAO;AAAA,MACL,KAAK,OAAO;AAAA,MACZ,OAAO,EAAE,WAAW,GAAG,aAAa,OAAO,IAAI,QAAQ,QAAQ,SAAS,MAAM;AAAA,MAC9E,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA,iBAAiB,OAAQ,QAAQ,OAAO;AAAA,IACxC;AAAA,EACF;AACA,QAAM,OAA0B;AAAA,IAC9B,SAAS;AAAA,IACT,aAAa,SAAS;AAAA,IACtB,cAAc;AAAA,IACd,KAAK,QAAQ;AAAA,EACf;AACA,QAAM,uBAAuB,MAAMC,SAAQ,IAAI,CAAC;AAChD,QAAMC,OAAMD,SAAQ,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,QAAM,YAAY,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,CAAI;AAC5D,SAAO,EAAE,GAAG,SAAS,QAAQ,kBAAkB,CAAC,SAAS,YAAY,cAAc;AACrF;AAEA,eAAe,UAAU,MAAiD;AACxE,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,MAAME,UAAS,MAAM,MAAM,CAAC;AACtD,QAAI,OAAO,YAAY,iBAAiB,CAAC,OAAO,OAAO,CAAC,OAAO,aAAc,QAAO;AACpF,QAAI,CAAC,MAAM,QAAQ,OAAO,IAAI,OAAO,KAAK,CAAC,MAAM,QAAQ,OAAO,IAAI,KAAK,EAAG,QAAO;AACnF,QACE,CAAC,OAAO,IAAI,QAAQ;AAAA,MAClB,CAAC,WACC,UACA,OAAO,OAAO,SAAS,YACvB,OAAO,OAAO,aAAa,YAC3B,MAAM,QAAQ,OAAO,OAAO;AAAA,IAChC,KACA,CAAC,OAAO,IAAI,MAAM;AAAA,MAChB,CAAC,SAAS,QAAQ,OAAO,KAAK,SAAS,YAAY,OAAO,KAAK,OAAO;AAAA,IACxE,GACA;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,UAAiD;AACrE,SAAO,OAAO;AAAA,IACZ,OAAO,OAAO,SAAS,OAAO,EAC3B,OAAO,CAAC,UAAU,kBAAkB,IAAIC,SAAQ,MAAM,IAAI,EAAE,YAAY,CAAC,CAAC,EAC1E,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,MAAM,IAAI,CAAC;AAAA,EAC5C;AACF;;;AGxHA,SAAS,aAAa;AACtB,SAAS,WAAAC,gBAAe;AACxB,SAAS,WAAAC,UAAS,QAAAC,OAAM,YAAAC,WAAU,WAAW;;;ACF7C,SAAS,WAAAC,gBAAe;AAUxB,IAAM,wBAAgD;AAAA,EACpD,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AACV;AAGO,IAAMC,wBAAuB,IAAI,IAAI,OAAO,KAAK,qBAAqB,CAAC;AAE9E,IAAM,iBAAiB,oBAAI,IAAI,CAAC,MAAM,OAAO,MAAM,KAAK,CAAC;AACzD,IAAM,eAAe;AACrB,IAAM,sBAAsB;AAErB,SAAS,YAAY,MAAsB;AAChD,SAAO,sBAAsBC,SAAQ,IAAI,EAAE,YAAY,CAAC,KAAK;AAC/D;AASO,SAAS,gBAAgB,MAAc,SAA8B;AAC1E,QAAM,WAAW,YAAY,IAAI;AACjC,QAAM,QAAQ,WAAW,OAAO;AAChC,QAAM,UAAU,eAAe,IAAI,QAAQ,IACvC,eAAe,OAAO,IACtB,aAAa,OACX,kBAAkB,OAAO,IACzB,CAAC;AACP,QAAM,UAAU,eAAe,UAAU,OAAO,SAAS,OAAO;AAChE,SAAO,EAAE,UAAU,OAAO,SAAS,QAAQ;AAC7C;AAEA,SAAS,WAAW,SAAyB;AAC3C,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO,QAAQ,QAAQ,OAAO,EAAE,EAAE,QAAQ,OAAO,EAAE,EAAE,MAAM,IAAI,EAAE;AACnE;AAOO,SAAS,eAAe,SAA2B;AACxD,QAAM,QAAkB,CAAC;AACzB,QAAM,OAAO,CAAC,SAA6B;AACzC,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,WAAW,qBAAqB,KAAK,OAAO,KAAK,CAAC,MAAM,SAAS,OAAO,GAAG;AAC7E,YAAM,KAAK,OAAO;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,cACJ;AACF,aAAW,SAAS,QAAQ,SAAS,WAAW,EAAG,MAAK,MAAM,CAAC,CAAC;AAEhE,QAAM,eAAe;AACrB,aAAW,SAAS,QAAQ,SAAS,YAAY,GAAG;AAClD,eAAW,SAAS,MAAM,CAAC,KAAK,IAAI,MAAM,GAAG,GAAG;AAE9C,YAAM,QAAQ,KAAK,SAAS,MAAM,IAAI,KAAK,MAAM,MAAM,EAAE,IAAI,IAAI;AACjE,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAEA,SAAO,MAAM,MAAM,GAAG,YAAY;AACpC;AAGA,SAAS,kBAAkB,SAA2B;AACpD,SAAO,mBAAmB,SAAS,MAAM,EACtC,OAAO,CAAC,WAAW,OAAO,YAAY,OAAO,SAAS,QAAQ,EAC9D,IAAI,CAAC,WAAW,OAAO,IAAI,EAC3B,MAAM,GAAG,YAAY;AAC1B;AAEA,SAAS,eACP,UACA,OACA,SACA,SACQ;AACR,QAAM,OAAO,GAAG,QAAQ,WAAM,KAAK,QAAQ,UAAU,IAAI,KAAK,GAAG;AACjE,MAAI,QAAQ,SAAS,EAAG,QAAO,GAAG,IAAI,mBAAc,QAAQ,KAAK,IAAI,CAAC;AACtE,QAAM,UAAU,eAAe,OAAO;AACtC,MAAI,QAAS,QAAO,GAAG,IAAI,WAAM,OAAO;AACxC,SAAO;AACT;AAGA,SAAS,eAAe,SAAgC;AACtD,aAAW,OAAO,QAAQ,MAAM,OAAO,GAAG;AACxC,UAAM,OAAO,IAAI,KAAK;AACtB,QAAI,SAAS,GAAI;AACjB,UAAM,WAAW,KACd,QAAQ,UAAU,EAAE,EACpB,QAAQ,UAAU,EAAE,EACpB,QAAQ,UAAU,EAAE,EACpB,QAAQ,UAAU,EAAE,EACpB,KAAK;AACR,QACE,KAAK,WAAW,IAAI,KACpB,KAAK,WAAW,IAAI,KACpB,KAAK,WAAW,GAAG,KACnB,KAAK,WAAW,GAAG,GACnB;AACA,UAAI,SAAU,QAAO,SAAS,UAAU,mBAAmB;AAC3D;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAAe,KAAqB;AACpD,SAAO,MAAM,UAAU,MAAM,QAAQ,GAAG,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC;AACjE;;;ADtIA,IAAM,+BAA+B,oBAAI,IAAI;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,eAAsB,mBAAmB,gBAA2C;AAClF,QAAM,UAAU,MAAM,aAAa,cAAc;AACjD,QAAM,QAAQ,WAAY,MAAM,UAAU,cAAc;AACxD,SAAO,MACJ,IAAIC,UAAS,EACb,OAAO,CAAC,SAAS,CAAC,KAAK,WAAW,aAAa,KAAK,CAAC,KAAK,WAAW,eAAe,CAAC,EACrF,OAAO,CAAC,SAASC,sBAAqB,IAAIC,SAAQ,IAAI,EAAE,YAAY,CAAC,CAAC,EACtE,KAAK;AACV;AAGA,eAAe,aAAa,gBAAkD;AAG5E,QAAM,SAAS,MAAM,OAAO,gBAAgB;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,MAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,SAAO,OAAO,OAAO,MAAM,IAAI,EAAE,OAAO,OAAO;AACjD;AAEA,eAAe,UAAU,MAAiC;AACxD,QAAM,QAAkB,CAAC;AACzB,QAAMC,QAAO,OAAO,cAAqC;AACvD,UAAM,UAAU,MAAMC,SAAQ,WAAW,EAAE,eAAe,KAAK,CAAC;AAChE,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAWC,MAAK,WAAW,MAAM,IAAI;AAC3C,UAAI,MAAM,YAAY,GAAG;AACvB,YAAI,6BAA6B,IAAI,MAAM,IAAI,EAAG;AAClD,cAAMF,MAAK,QAAQ;AACnB;AAAA,MACF;AACA,UAAI,MAAM,OAAO,EAAG,OAAM,KAAKG,UAAS,MAAM,QAAQ,CAAC;AAAA,IACzD;AAAA,EACF;AACA,QAAMH,MAAK,IAAI;AACf,SAAO;AACT;AAEA,SAAS,OACP,KACA,MAC2D;AAC3D,SAAO,IAAI,QAAQ,CAAC,kBAAkB;AACpC,UAAM,QAAQ,MAAM,OAAO,MAAM,EAAE,KAAK,OAAO,CAAC,UAAU,QAAQ,MAAM,EAAE,CAAC;AAC3E,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,UAAM,OAAO,YAAY,MAAM;AAC/B,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAU;AACjC,gBAAU,OAAO,KAAK;AAAA,IACxB,CAAC;AACD,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAU;AACjC,gBAAU,OAAO,KAAK;AAAA,IACxB,CAAC;AAED,UAAM,GAAG,SAAS,MAAM,cAAc,EAAE,MAAM,IAAI,QAAQ,IAAI,QAAQ,kBAAkB,CAAC,CAAC;AAC1F,UAAM,GAAG,SAAS,CAAC,SAAS,cAAc,EAAE,MAAM,QAAQ,GAAG,QAAQ,OAAO,CAAC,CAAC;AAAA,EAChF,CAAC;AACH;AAGA,SAASH,WAAU,MAAsB;AACvC,UAAQ,QAAQ,OAAO,KAAK,QAAQ,OAAO,GAAG,IAAI,MAAM,QAAQ,SAAS,EAAE;AAC7E;;;AJvEA,IAAM,iBAAiB;AAKvB,IAAM,iBAAiB;AASvB,eAAsB,gBAAgB,UAAwB,CAAC,GAAyB;AACtF,UAAQ,MAAM,aAAa,OAAO,GAAG;AACvC;AAcA,eAAsB,eAAe,gBAA6C;AAChF,MAAI;AACF,UAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,aAAa,EAAE,eAAe,CAAC;AAClE,UAAM,QAAQ,OAAO,QAAQ,OAAO,UAAU,OAAO;AACrD,UAAM,WACJ,OAAO,OAAO,SAAS,IACnB,KAAK,OAAO,OAAO,MAAM,qDACzB;AACN,WAAO;AAAA,MACL;AAAA,MACA,SACE,UAAU,IACN,UAAU,OAAO,YAAY,wCAAwC,QAAQ,KAC7E,4CAA4C,OAAO,OAAO,aAAa,OAAO,KAAK,WAAW,OAAO,OAAO,aAAa,OAAO,YAAY,YAAY,QAAQ;AAAA,IACxK;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,UAAU,cAAc;AAAA,MACxB,SAAS,kCAAmC,MAAgB,OAAO;AAAA,IACrE;AAAA,EACF;AACF;AAIA,IAAM,SAAS,oBAAI,IAA8B;AAGjD,SAAS,aACP,SAC2D;AAC3D,QAAM,MAAMO,SAAQ,QAAQ,kBAAkB,QAAQ,IAAI,CAAC;AAC3D,QAAM,QAAQ,OAAO,IAAI,GAAG,KAAK,QAAQ,QAAQ,GAC9C,MAAM,MAAM,MAAS,EACrB,KAAK,MAAM,aAAa,OAAO,CAAC;AACnC,SAAO,IAAI,KAAK,IAAI;AACpB,OAAK,KACF,QAAQ,MAAM;AACb,QAAI,OAAO,IAAI,GAAG,MAAM,KAAM,QAAO,OAAO,GAAG;AAAA,EACjD,CAAC,EACA,MAAM,MAAM,MAAS;AACxB,SAAO;AACT;AAEA,eAAe,aACb,SAC2D;AAC3D,QAAM,QAAQ,KAAK,IAAI;AACvB,QAAM,MAAM,QAAQ,QAAQ,MAAM,oBAAI,KAAK;AAC3C,QAAM,iBAAiBA,SAAQ,QAAQ,kBAAkB,QAAQ,IAAI,CAAC;AACtE,QAAM,QAAQ,IAAI,WAAW,cAAc;AAE3C,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,QAAQ,IAAI,CAAC,mBAAmB,cAAc,GAAG,MAAM,KAAK,CAAC,CAAC;AAE9F,QAAM,UAAsC,CAAC;AAC7C,QAAM,SAAuB,CAAC;AAC9B,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,MAAI,YAAY;AAChB,MAAI,UAAU;AAEd,MAAI,QAAQ,SAAS,gBAAgB,cAAc,EAAE;AAErD,aAAW,QAAQ,OAAO;AACxB,QAAI;AACF,YAAM,OAAO,MAAMC,MAAKC,MAAK,gBAAgB,IAAI,CAAC;AAClD,UAAI,CAAC,KAAK,OAAO,EAAG;AACpB,UAAI,KAAK,OAAO,gBAAgB;AAC9B;AACA;AAAA,MACF;AAEA,YAAM,WAAW,SAAS,QAAQ,IAAI;AACtC,UAAI,CAAC,QAAQ,QAAQ,YAAY,cAAc,UAAU,IAAI,GAAG;AAC9D,gBAAQ,IAAI,IAAI;AAChB;AACA;AAAA,MACF;AAEA,YAAM,UAAU,MAAMC,UAASD,MAAK,gBAAgB,IAAI,GAAG,MAAM;AACjE,YAAM,OAAO,YAAY,OAAO;AAChC,YAAM,WAAW,gBAAgB,IAAI;AACrC,cAAQ;AAER,UAAI,CAAC,QAAQ,QAAQ,YAAY,SAAS,SAAS,MAAM;AACvD,cAAM,EAAE,SAAS,QAAQ,SAAS,QAAQ,GAAG,KAAK,IAAI;AACtD,gBAAQ,IAAI,IAAI,EAAE,GAAG,MAAM,MAAM,KAAK,MAAM,GAAG,SAAS;AACxD;AACA;AAAA,MACF;AAEA,YAAM,EAAE,UAAU,OAAO,SAAS,QAAQ,IAAI,gBAAgB,MAAM,OAAO;AAC3E,YAAM,cAAc,gBAAgB,IAAI,IAAI,mBAAmB,SAAS,IAAI,IAAI,CAAC;AACjF,cAAQ,IAAI,IAAI;AAAA,QACd;AAAA,QACA;AAAA,QACA,MAAM,KAAK;AAAA,QACX,GAAG;AAAA,QACH;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,YAAY,SAAS,IAAI,EAAE,YAAY,IAAI,CAAC;AAAA,QAChD;AAAA,QACA,WAAW,IAAI,EAAE,YAAY;AAAA,MAC/B;AACA,UAAI,SAAU;AAAA,UACT;AAAA,IACP,SAAS,OAAO;AACd,aAAO,KAAK,EAAE,MAAM,OAAQ,MAAgB,QAAQ,CAAC;AACrD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,KAAK,SAAS,OAAO,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,QAAQ,EAAE;AAEnF,QAAM,WAA0B;AAAA,IAC9B,SAAS;AAAA,IACT,aAAa,IAAI,EAAE,YAAY;AAAA,IAC/B,SAAS,YAAY,OAAO;AAAA,EAC9B;AACA,MAAI,SAAS,UAAU,EAAG,OAAM,MAAM,MAAM,QAAQ;AACpD,QAAM,eAAe,MAAM,yBAAyB,gBAAgB,QAAQ;AAE5E,QAAM,SAAsB;AAAA,IAC1B,SAAS,MAAM;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,KAAK,IAAI,IAAI;AAAA,IACzB,cAAc,OAAO,KAAK,OAAO,EAAE;AAAA,IACnC,cAAc;AAAA,MACZ,QAAQ,aAAa;AAAA,MACrB,WAAW,aAAa,MAAM;AAAA,MAC9B,aAAa,aAAa,MAAM;AAAA,IAClC;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,SAAS;AAC5B;AAEA,SAAS,cAAc,OAAmB,MAAsB;AAC9D,SACE,MAAM,YAAY,UAClB,MAAM,SAAS,KAAK,QACpB,MAAM,YAAY,KAAK,WACvB,MAAM,YAAY,KAAK;AAE3B;AAGA,SAAS,gBAAgB,MAAqD;AAC5E,MAAI,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,SAAS,KAAK,OAAO,IAAI,eAAgB,QAAO,CAAC;AAChF,SAAO,EAAE,SAAS,KAAK,SAAS,SAAS,KAAK,QAAQ;AACxD;AAEO,SAAS,YAAY,SAAyB;AACnD,SAAO,WAAW,QAAQ,EAAE,OAAO,SAAS,MAAM,EAAE,OAAO,KAAK;AAClE;AAEA,SAAS,YAAY,SAAiE;AACpF,SAAO,OAAO;AAAA,IACZ,OAAO,KAAK,OAAO,EAChB,KAAK,EACL,IAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ,GAAG,CAAe,CAAC;AAAA,EACnD;AACF;","names":["readFile","stat","join","resolve","mkdir","readFile","dirname","extname","join","resolve","readFile","dirname","join","resolve","resolve","join","readFile","dirname","resolve","join","dirname","mkdir","readFile","extname","readdir","extname","join","relative","extname","SUPPORTED_EXTENSIONS","extname","normalize","SUPPORTED_EXTENSIONS","extname","walk","readdir","join","relative","resolve","stat","join","readFile"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/clients/install.ts","../src/hooks/hermes-plugin.ts","../src/hooks/managed-plugin.ts","../src/hooks/opencode-plugin.ts","../src/hooks/settings.ts","../src/hooks/install.ts","../src/integrations/project-mcp.ts","../src/templates/install.ts","../src/templates/workflow.ts","../src/cli/commands/codex.ts","../src/cli/commands/claude-desktop.ts","../src/integrations/hermes-config.ts","../src/clients/global-mcp.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { resolveNamespaceFrom } from \"../core/config-file.js\";\nimport {\n clientHooksInstalled,\n clientHooksVariant,\n hookedRulesSiblings,\n setClientHooks,\n} from \"../hooks/install.js\";\nimport type { HookVariant } from \"../hooks/settings.js\";\nimport { indexRepository } from \"../indexer/engine.js\";\nimport {\n codebuddyMcpServer,\n formatEntry,\n hasMcpServer,\n projectMcpPath,\n removeMcpServer,\n upsertMcpServer,\n} from \"../integrations/project-mcp.js\";\nimport { installWorkflowRules, readManagedMode } from \"../templates/install.js\";\nimport {\n type AutomationMode,\n CLIENT_ADAPTERS,\n type ClientId,\n MODE_LABELS,\n sharedRulesReaders,\n supportedModes,\n} from \"./adapters.js\";\nimport { globalMcpPath, hasGlobalMcp, setGlobalMcp } from \"./global-mcp.js\";\n\n/**\n * Applies one explicit automation mode to one client, across every surface its\n * adapter declares (rules block, lifecycle hooks, project MCP config). Each step\n * reuses the existing idempotent installer and reports what it did, so a re-run\n * is all `unchanged` and `uninstallClient` reverses it without touching user config.\n */\n\nexport type ClientStep = {\n surface: \"rules\" | \"hooks\" | \"mcp\" | \"index\";\n target: string;\n action: string;\n};\nexport type ClientReport = { client: ClientId; steps: ClientStep[]; notes: string[] };\n\ntype ClientOptions = {\n client: ClientId;\n projectRoot?: string;\n namespace?: string;\n /** Also write/remove the entry in the client's global MCP config (Codex, Hermes). */\n global?: boolean;\n};\n\nexport async function installClient(\n options: ClientOptions & { mode: AutomationMode },\n): Promise<ClientReport> {\n const adapter = CLIENT_ADAPTERS[options.client];\n if (!supportedModes(adapter).includes(options.mode)) {\n throw new Error(\n `${adapter.name} does not support ${MODE_LABELS[options.mode]} mode: ${adapter.gap ?? \"no lifecycle hooks\"}.`,\n );\n }\n return apply(options, options.mode, options.mode !== \"lean\");\n}\n\n/** Lean has no MCP server to index the repo, so install builds the index the hook reads. */\nasync function ensureIndex(root: string): Promise<ClientStep> {\n const target = join(root, \".codebuddy\", \"cache\", \"index.json\");\n const existed = await access(target).then(\n () => true,\n () => false,\n );\n if (!existed) await indexRepository({ repositoryRoot: root });\n return { surface: \"index\", target, action: existed ? \"unchanged\" : \"built\" };\n}\n\n/** Remove every CodeBuddy surface for a client; user rules, hooks and servers stay. */\nexport async function uninstallClient(options: ClientOptions): Promise<ClientReport> {\n return apply(options, \"mcp-only\", false);\n}\n\nasync function apply(\n options: ClientOptions,\n mode: AutomationMode,\n keepMcp: boolean,\n): Promise<ClientReport> {\n const root = resolve(options.projectRoot ?? process.cwd());\n const adapter = CLIENT_ADAPTERS[options.client];\n const steps: ClientStep[] = [];\n const notes: string[] = [];\n\n const namespace = options.namespace ?? (await resolveNamespaceFrom(root));\n const name = `codebuddy-${namespace}`;\n const shared = sharedRulesReaders(options.client);\n // Removing the block would strip the workflow from a sibling client that still\n // has its MCP entry for this namespace (project or global config).\n const liveSiblings =\n mode === \"mcp-only\" || mode === \"lean\" ? await installedWithMcp(root, shared, namespace) : [];\n if (liveSiblings.length > 0) {\n const path = join(root, adapter.rulesFile);\n steps.push({ surface: \"rules\", target: path, action: \"kept\" });\n notes.push(\n `Kept the CodeBuddy block in ${adapter.rulesFile}: still used by ${liveSiblings.map((id) => CLIENT_ADAPTERS[id].name).join(\", \")}.`,\n );\n } else {\n // An agent-driven block would make a hooked sibling retrieve and capture twice.\n const hooked = mode === \"agent\" ? await hookedRulesSiblings(root, options.client) : [];\n const rules = await installWorkflowRules({\n client: options.client,\n projectRoot: root,\n // Lean sends no rules: the block would cost tokens every turn and shadow AGENTS.md.\n mode: mode === \"lean\" ? \"mcp-only\" : hooked.length > 0 ? \"hooks\" : mode,\n });\n steps.push({ surface: \"rules\", target: rules.path, action: rules.action });\n if (hooked.length > 0) {\n notes.push(\n `Kept ${adapter.rulesFile} hook-driven: ${hooked.map((id) => CLIENT_ADAPTERS[id].name).join(\", \")} hooks are installed; agents without hooks follow its agent-driven fallback.`,\n );\n }\n }\n if (shared.length > 0) {\n notes.push(\n `${adapter.rulesFile} is also read by ${shared.map((id) => CLIENT_ADAPTERS[id].name).join(\", \")}.`,\n );\n }\n\n const hooks = await setHooks(\n root,\n mode === \"hooks\" || mode === \"lean\",\n options.client,\n mode === \"lean\" ? \"lean\" : \"full\",\n );\n if (hooks) steps.push(hooks);\n if (mode === \"lean\") steps.push(await ensureIndex(root));\n\n if (\"file\" in adapter.mcp) {\n const path = await projectMcpPath(root, adapter.mcp.file, adapter.mcp.format);\n const action = keepMcp\n ? await upsertMcpServer(path, adapter.mcp.format, name, codebuddyMcpServer(root, namespace))\n : await removeMcpServer(path, adapter.mcp.format, name);\n steps.push({ surface: \"mcp\", target: path, action });\n if (action === \"manual\") {\n notes.push(\n `${path} has comments CodeBuddy will not rewrite; ${keepMcp ? \"add\" : \"remove\"} the \"${name}\" entry under \"mcp\" by hand${keepMcp ? `: ${JSON.stringify(formatEntry(adapter.mcp.format, codebuddyMcpServer(root, namespace)))}` : \"\"}.`,\n );\n }\n } else if (options.global) {\n const target = globalMcpPath(adapter.mcp.global);\n const action = await setGlobalMcp(adapter.mcp.global, {\n name,\n root,\n namespace,\n enabled: keepMcp,\n });\n steps.push({ surface: \"mcp\", target, action });\n if (action === \"manual\") {\n notes.push(\n `${target} is not a YAML map CodeBuddy can edit safely; ${keepMcp ? \"add\" : \"remove\"} the \"${name}\" entry under mcp_servers by hand.`,\n );\n }\n } else {\n notes.push(\n `MCP config is global; ${keepMcp ? \"add\" : \"remove\"} it with --global, or: ${adapter.mcp.manual}.`,\n );\n }\n if (adapter.note && mode !== \"mcp-only\") notes.push(`${adapter.name}: ${adapter.note}.`);\n return { client: options.client, steps, notes };\n}\n\nasync function installedWithMcp(\n root: string,\n clients: ClientId[],\n namespace: string,\n): Promise<ClientId[]> {\n const live: ClientId[] = [];\n for (const id of clients) {\n if ((await detectClient(id, root, namespace)).mcp) live.push(id);\n }\n return live;\n}\n\n/** Install or remove a client's CodeBuddy lifecycle hooks; null when it has none. */\nexport async function setHooks(\n root: string,\n enabled: boolean,\n client: ClientId = \"claude\",\n variant: HookVariant = \"full\",\n): Promise<ClientStep | null> {\n const result = await setClientHooks(root, client, enabled, variant);\n if (!result) return null;\n const action = result.changed ? (enabled ? \"installed\" : \"removed\") : \"unchanged\";\n return { surface: \"hooks\", target: result.target, action };\n}\n\nexport type ClientStatus = {\n client: ClientId;\n /** The effective mode in this repo; \"none\" when nothing project-local is installed. */\n mode: AutomationMode | \"none\";\n rulesMode: AutomationMode | null;\n /** null = this client has no CodeBuddy hook adapter. */\n hooks: boolean | null;\n /** Project MCP entry, or for Codex/Hermes the entry in their global config. */\n mcp: boolean;\n warnings: string[];\n};\n\nexport async function detectClient(\n client: ClientId,\n projectRoot = process.cwd(),\n namespace?: string,\n): Promise<ClientStatus> {\n const root = resolve(projectRoot);\n const adapter = CLIENT_ADAPTERS[client];\n const rulesMode = await readManagedMode(join(root, adapter.rulesFile));\n const hooks = await clientHooksInstalled(root, client);\n const name = `codebuddy-${namespace ?? (await resolveNamespaceFrom(root))}`;\n const mcp =\n \"file\" in adapter.mcp\n ? await hasMcpServer(\n await projectMcpPath(root, adapter.mcp.file, adapter.mcp.format),\n adapter.mcp.format,\n name,\n )\n : await hasGlobalMcp(adapter.mcp.global, name).catch(() => false);\n\n const warnings: string[] = [];\n if (hooks && rulesMode === \"agent\") {\n warnings.push(\n `hooks are installed but ${adapter.rulesFile} is agent-driven: retrieval and capture run twice. Run \\`codebuddy clients install ${client} --mode hooks\\`.`,\n );\n }\n if (rulesMode === \"hooks\" && hooks === false) {\n warnings.push(\n `${adapter.rulesFile} is hook-driven but hooks are not installed: turns are not captured.`,\n );\n }\n // A hookless client reading a hook-driven block follows its agent-driven fallback.\n const effectiveRules = hooks === null && rulesMode === \"hooks\" ? \"agent\" : rulesMode;\n const mode: ClientStatus[\"mode\"] = hooks\n ? (await clientHooksVariant(root, client)) === \"lean\"\n ? \"lean\"\n : \"hooks\"\n : (effectiveRules ?? (mcp ? \"mcp-only\" : \"none\"));\n return { client, mode, rulesMode, hooks, mcp, warnings };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { type ManagedPlugin, managedPluginInstalled, setManagedPlugin } from \"./managed-plugin.js\";\nimport type { HookVariant } from \"./settings.js\";\n\n/**\n * Hermes Agent lifecycle bridge. Hermes loads project plugins from\n * `./.hermes/plugins/<name>/` (plugin.yaml + `__init__.py` with `register(ctx)`),\n * so CodeBuddy writes one managed Python plugin there. Like the OpenCode\n * bridge it only forwards: each hook runs the fail-soft `codebuddy hooks\n * <command>` runtime with a normalized JSON payload on stdin.\n *\n * Verified against Hermes Agent v0.17.0 (hermes_cli/plugins.py, agent/turn_context.py,\n * agent/turn_finalizer.py, model_tools.py, agent/conversation_compression.py):\n * - Project plugins load only with HERMES_ENABLE_PROJECT_PLUGINS=1 and the\n * plugin key in `plugins.enabled` of ~/.hermes/config.yaml. `hermes plugins\n * enable` only knows bundled and user plugins, so that entry is a manual\n * edit (or `clients install --global`).\n * - pre_llm_call: a returned {\"context\"} is appended to this turn's user\n * message at API-call time and never persisted, so the bridge asks for a\n * full block every turn (`ephemeral_context`).\n * - post_tool_call: write_file / patch (`path`, or V4A `patch` text) are\n * staged; terminal commands keep the JSON result's `exit_code`.\n * - post_llm_call: the final assistant text, used as the capture summary.\n * - on_session_end: fires at the end of every turn → capture.\n * - Compaction has no plugin hook and can rotate the session id mid-turn, so\n * a turn keeps the id it started with, and the per-turn full resend (which\n * also resets the session ledger) stands in for a compaction reset.\n */\n\nexport const HERMES_PLUGIN_MARKER = \"# codebuddy-managed: hermes-plugin\";\n\nexport const HERMES_PLUGIN_MANIFEST = `${HERMES_PLUGIN_MARKER} v1\nname: codebuddy\nversion: 1.0.0\nkind: standalone\ndescription: CodeBuddy recall and capture bridge (codebuddy clients install hermes --mode hooks).\nprovides_hooks:\n - pre_llm_call\n - post_tool_call\n - post_llm_call\n - on_session_end\n`;\n\nconst HERMES_LEAN_MANIFEST = `${HERMES_PLUGIN_MARKER} v1 lean\nname: codebuddy\nversion: 1.0.0\nkind: standalone\ndescription: CodeBuddy lean bridge (codebuddy clients install hermes --mode lean).\nprovides_hooks:\n - pre_llm_call\n`;\n\nconst HERMES_HOOK_FN = `def _hook(command, payload):\n \"\"\"Run one fail-soft CodeBuddy hook; its stdout, or \"\" on any failure.\"\"\"\n try:\n done = subprocess.run(\n [\"codebuddy\", \"hooks\", command],\n input=json.dumps({\"cwd\": ROOT, **payload}),\n capture_output=True,\n text=True,\n timeout=TIMEOUT_S[command],\n cwd=ROOT,\n )\n except Exception:\n return \"\"\n return done.stdout if done.returncode == 0 else \"\"\n\n\n`;\n\n/** The generated plugin: `full` bridges every lifecycle event, `lean` only prompts. */\nexport function renderHermesPlugin(variant: HookVariant): string {\n return variant === \"lean\"\n ? `${HERMES_PLUGIN_MARKER} v1 lean\n# Generated by \"codebuddy clients install hermes --mode lean\"; removed by\n# \"codebuddy clients uninstall hermes\". Local edits are overwritten.\n\"\"\"CodeBuddy lean bridge: forwards each prompt to \"codebuddy hooks lean\".\"\"\"\n\nimport json\nimport subprocess\nfrom pathlib import Path\n\n# <root>/.hermes/plugins/codebuddy/__init__.py\nROOT = str(Path(__file__).absolute().parents[3])\nTIMEOUT_S = {\"lean\": 2}\n\n\n${HERMES_HOOK_FN}def _pre_llm_call(session_id=\"\", user_message=\"\", **_):\n if not isinstance(user_message, str) or not user_message.strip():\n return None\n payload = {\n \"session_id\": session_id,\n \"hook_event_name\": \"pre_llm_call\",\n \"ephemeral_context\": True,\n \"prompt\": user_message,\n }\n block = _hook(\"lean\", payload).strip()\n return {\"context\": block} if block else None\n\n\ndef register(ctx):\n ctx.register_hook(\"pre_llm_call\", _pre_llm_call)\n`\n : `${HERMES_PLUGIN_MARKER} v1\n# Generated by \"codebuddy clients install hermes --mode hooks\"; removed by\n# \"codebuddy clients uninstall hermes\". Local edits are overwritten.\n\"\"\"CodeBuddy lifecycle bridge: forwards Hermes hooks to \"codebuddy hooks <command>\".\"\"\"\n\nimport json\nimport subprocess\nimport threading\nfrom pathlib import Path\n\n# <root>/.hermes/plugins/codebuddy/__init__.py\nROOT = str(Path(__file__).absolute().parents[3])\nEDIT_TOOLS = {\"write_file\", \"patch\"}\nTIMEOUT_S = {\"context\": 20, \"stage\": 10, \"capture\": 60}\n_lock = threading.Lock()\n_turns = {}\n\n\n${HERMES_HOOK_FN}def _turn(turn_id, session_id):\n # Compression can rotate the session id mid-turn; a turn keeps its first id.\n with _lock:\n return _turns.setdefault(\n turn_id or session_id, {\"session\": session_id, \"text\": \"\", \"commands\": []}\n )\n\n\ndef _pre_llm_call(session_id=\"\", turn_id=\"\", user_message=\"\", **_):\n _turn(turn_id, session_id)\n payload = {\"session_id\": session_id, \"hook_event_name\": \"pre_llm_call\", \"ephemeral_context\": True}\n if isinstance(user_message, str) and user_message.strip():\n payload[\"prompt\"] = user_message\n block = _hook(\"context\", payload).strip()\n return {\"context\": block} if block else None\n\n\ndef _post_tool_call(tool_name=\"\", args=None, result=None, session_id=\"\", turn_id=\"\", **_):\n turn = _turn(turn_id, session_id)\n args = args if isinstance(args, dict) else {}\n if tool_name in EDIT_TOOLS:\n # Paths only: write_file content never needs to cross the pipe.\n tool_input = {k: args[k] for k in (\"path\", \"mode\", \"patch\") if k in args}\n _hook(\"stage\", {\n \"session_id\": turn[\"session\"],\n \"hook_event_name\": \"post_tool_call\",\n \"tool_name\": tool_name,\n \"tool_input\": tool_input,\n })\n elif tool_name == \"terminal\" and isinstance(args.get(\"command\"), str):\n exit_code = None\n if not args.get(\"background\"): # a background process has not finished\n try:\n exit_code = json.loads(result).get(\"exit_code\")\n except Exception:\n pass\n turn[\"commands\"].append({\n \"command\": args[\"command\"],\n \"exit_code\": exit_code if type(exit_code) is int else None,\n })\n\n\ndef _post_llm_call(session_id=\"\", turn_id=\"\", assistant_response=\"\", **_):\n if isinstance(assistant_response, str) and assistant_response.strip():\n _turn(turn_id, session_id)[\"text\"] = assistant_response\n\n\ndef _on_session_end(session_id=\"\", turn_id=\"\", **_):\n with _lock:\n turn = _turns.pop(turn_id or session_id, None)\n turn = turn or {\"session\": session_id, \"text\": \"\", \"commands\": []}\n payload = {\"session_id\": turn[\"session\"], \"hook_event_name\": \"on_session_end\"}\n if turn[\"text\"]:\n payload[\"last_assistant_message\"] = turn[\"text\"]\n if turn[\"commands\"]:\n payload[\"shell_commands\"] = turn[\"commands\"][-20:]\n _hook(\"capture\", payload)\n\n\ndef register(ctx):\n ctx.register_hook(\"pre_llm_call\", _pre_llm_call)\n ctx.register_hook(\"post_tool_call\", _post_tool_call)\n ctx.register_hook(\"post_llm_call\", _post_llm_call)\n ctx.register_hook(\"on_session_end\", _on_session_end)\n`;\n}\n\nexport const HERMES_PLUGIN_SOURCE = renderHermesPlugin(\"full\");\n\nexport function hermesPluginDir(repositoryRoot: string): string {\n return join(resolve(repositoryRoot), \".hermes\", \"plugins\", \"codebuddy\");\n}\n\nfunction plugin(repositoryRoot: string, variant: HookVariant = \"full\"): ManagedPlugin {\n const dir = hermesPluginDir(repositoryRoot);\n return {\n marker: HERMES_PLUGIN_MARKER,\n files: [\n {\n path: join(dir, \"plugin.yaml\"),\n content: variant === \"lean\" ? HERMES_LEAN_MANIFEST : HERMES_PLUGIN_MANIFEST,\n },\n { path: join(dir, \"__init__.py\"), content: renderHermesPlugin(variant) },\n ],\n dirs: [dir, dirname(dir), dirname(dirname(dir))],\n };\n}\n\nexport function hermesPluginInstalled(repositoryRoot: string): Promise<boolean> {\n return managedPluginInstalled(plugin(repositoryRoot));\n}\n\nexport function setHermesPlugin(\n repositoryRoot: string,\n enabled: boolean,\n variant: HookVariant = \"full\",\n): Promise<boolean> {\n return setManagedPlugin(plugin(repositoryRoot, variant), enabled);\n}\n\n/** Which plugin variant is installed, or null when CodeBuddy's plugin is absent. */\nexport async function hermesPluginVariant(repositoryRoot: string): Promise<HookVariant | null> {\n if (!(await hermesPluginInstalled(repositoryRoot))) return null;\n const source = await readFile(join(hermesPluginDir(repositoryRoot), \"__init__.py\"), \"utf8\");\n return source.startsWith(`${HERMES_PLUGIN_MARKER} v1 lean`) ? \"lean\" : \"full\";\n}\n","import { mkdir, readFile, rm, rmdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\n\n/**\n * A host plugin CodeBuddy owns: files that each start with `marker`. Install\n * and uninstall are idempotent, and a file at one of these paths without the\n * marker is never overwritten or removed.\n */\nexport type ManagedPlugin = {\n marker: string;\n files: Array<{ path: string; content: string }>;\n /** Directories install may create, innermost first; uninstall removes them only while empty. */\n dirs: string[];\n};\n\nasync function read(path: string): Promise<string | null> {\n try {\n return await readFile(path, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n}\n\nexport async function managedPluginInstalled(plugin: ManagedPlugin): Promise<boolean> {\n for (const file of plugin.files) {\n if (!(await read(file.path))?.startsWith(plugin.marker)) return false;\n }\n return true;\n}\n\n/** Write or remove the plugin; returns whether anything changed. */\nexport async function setManagedPlugin(plugin: ManagedPlugin, enabled: boolean): Promise<boolean> {\n const current = await Promise.all(plugin.files.map((file) => read(file.path)));\n const foreign = plugin.files.find((_, i) => {\n const text = current[i];\n return text != null && !text.startsWith(plugin.marker);\n });\n if (foreign) {\n if (!enabled) return false;\n throw new Error(\n `${foreign.path} exists and is not managed by CodeBuddy; move it aside and re-run.`,\n );\n }\n let changed = false;\n for (const [i, file] of plugin.files.entries()) {\n if (enabled && current[i] !== file.content) {\n await mkdir(dirname(file.path), { recursive: true });\n await writeAtomic(file.path, file.content, { mode: 0o644 });\n changed = true;\n } else if (!enabled && current[i] != null) {\n await rm(file.path, { force: true });\n changed = true;\n }\n }\n if (!enabled && changed) {\n for (const dir of plugin.dirs) await rmdir(dir).catch(() => {});\n }\n return changed;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { type ManagedPlugin, managedPluginInstalled, setManagedPlugin } from \"./managed-plugin.js\";\nimport type { HookVariant } from \"./settings.js\";\n\n/**\n * OpenCode lifecycle bridge. OpenCode loads project plugins from\n * `.opencode/{plugin,plugins}/*.{ts,js}`, so CodeBuddy writes one managed JS\n * plugin there. It only forwards events: each one spawns the same fail-soft\n * `codebuddy hooks <command>` runtime Claude Code uses, with a normalized\n * Claude-shaped JSON payload on stdin. All logic stays in the shared services.\n *\n * Verified against OpenCode 1.18.32 (binary) and @opencode-ai/plugin 1.17.9 types:\n * - chat.message: mutated `output.parts` are saved with the user message and\n * non-ignored text parts reach the model, so the context block is appended\n * as a synthetic text part (prompt-time injection).\n * - tool.execute.after: edit/write (`filePath`), apply_patch/patch (`patchText`)\n * are staged; bash commands and `metadata.exit` feed turn verification.\n * - experimental.text.complete: the latest assistant text, used as the\n * capture summary (OpenCode has no transcript file).\n * - event session.idle → capture; session.compacted → reset.\n * The module must export only plugin functions: OpenCode calls every export.\n */\n\nexport const OPENCODE_PLUGIN_MARKER = \"// codebuddy-managed: opencode-plugin\";\n\nconst OPENCODE_RUNTIME = `const ID_CHARS = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\nlet lastMs = 0;\nlet counter = 0;\n\n/** Run one fail-soft CodeBuddy hook; resolves to its stdout, \"\" on any failure. */\nfunction hook(command, payload) {\n return new Promise((done) => {\n let out = \"\";\n let child;\n try {\n child = spawn(\"codebuddy\", [\"hooks\", command], {\n cwd: payload.cwd,\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n // npm installs a codebuddy.cmd shim on Windows, which spawn only runs through a shell.\n shell: process.platform === \"win32\",\n });\n } catch {\n done(\"\");\n return;\n }\n const timer = setTimeout(() => child.kill(), TIMEOUT_MS[command]);\n child.stdout.setEncoding(\"utf8\");\n child.stdout.on(\"data\", (chunk) => {\n out += chunk;\n });\n child.on(\"error\", () => {\n clearTimeout(timer);\n done(\"\");\n });\n child.on(\"close\", (code) => {\n clearTimeout(timer);\n done(code === 0 ? out : \"\");\n });\n child.stdin.on(\"error\", () => {});\n child.stdin.end(JSON.stringify(payload));\n });\n}\n\n/** An ascending OpenCode part id (prt_ + 48-bit time/counter hex + 14 base62). */\nfunction partId() {\n const now = Date.now();\n if (now !== lastMs) {\n lastMs = now;\n counter = 0;\n }\n counter += 1;\n const time = (BigInt(now) * 4096n + BigInt(counter)) & 0xffffffffffffn;\n let random = \"\";\n for (const byte of crypto.getRandomValues(new Uint8Array(14))) random += ID_CHARS[byte % 62];\n return \"prt_\" + time.toString(16).padStart(12, \"0\") + random;\n}\n\n`;\n\n/** The generated plugin: `full` bridges every lifecycle event, `lean` only prompts and compaction. */\nexport function renderOpencodePlugin(variant: HookVariant): string {\n return variant === \"lean\"\n ? `${OPENCODE_PLUGIN_MARKER} v1 lean\n// Generated by \\`codebuddy clients install opencode --mode lean\\`; removed by\n// \\`codebuddy clients uninstall opencode\\`. Local edits are overwritten.\nimport { spawn } from \"node:child_process\";\n\nconst TIMEOUT_MS = { lean: 2000, reset: 10000 };\n${OPENCODE_RUNTIME}/** @type {import(\"@opencode-ai/plugin\").Plugin} */\nexport const CodeBuddyPlugin = async ({ directory }) => {\n const base = (sessionID, event) => ({ session_id: sessionID, cwd: directory, hook_event_name: event });\n\n return {\n \"chat.message\": async (input, output) => {\n const prompt = output.parts\n .flatMap((part) => (part.type === \"text\" && !part.synthetic ? [part.text] : []))\n .join(\"\\\\n\")\n .trim();\n if (!prompt) return;\n const block = (await hook(\"lean\", { ...base(input.sessionID, \"chat.message\"), prompt })).trim();\n if (!block) return;\n output.parts.push({\n id: partId(),\n sessionID: input.sessionID,\n messageID: output.message.id,\n type: \"text\",\n text: block,\n synthetic: true,\n });\n },\n event: async ({ event }) => {\n if (event.type === \"session.compacted\") {\n await hook(\"reset\", { ...base(event.properties.sessionID, \"session.compacted\"), source: \"compact\" });\n }\n },\n };\n};\n`\n : `${OPENCODE_PLUGIN_MARKER} v1\n// Generated by \\`codebuddy clients install opencode --mode hooks\\`; removed by\n// \\`codebuddy clients uninstall opencode\\`. Local edits are overwritten.\nimport { spawn } from \"node:child_process\";\n\nconst EDIT_TOOLS = new Set([\"edit\", \"write\", \"patch\", \"apply_patch\"]);\nconst TIMEOUT_MS = { context: 20000, stage: 10000, capture: 60000, reset: 10000 };\n${OPENCODE_RUNTIME}/** @type {import(\"@opencode-ai/plugin\").Plugin} */\nexport const CodeBuddyPlugin = async ({ directory }) => {\n /** @type {Map<string, { text: string, commands: Array<{ command: string, exit_code: number | null }> }>} */\n const turns = new Map();\n const turn = (sessionID) => {\n if (!turns.has(sessionID)) turns.set(sessionID, { text: \"\", commands: [] });\n return turns.get(sessionID);\n };\n const base = (sessionID, event) => ({ session_id: sessionID, cwd: directory, hook_event_name: event });\n\n return {\n \"chat.message\": async (input, output) => {\n turns.delete(input.sessionID);\n const prompt = output.parts\n .flatMap((part) => (part.type === \"text\" && !part.synthetic ? [part.text] : []))\n .join(\"\\\\n\")\n .trim();\n const block = (\n await hook(\"context\", { ...base(input.sessionID, \"chat.message\"), ...(prompt ? { prompt } : {}) })\n ).trim();\n if (!block) return;\n output.parts.push({\n id: partId(),\n sessionID: input.sessionID,\n messageID: output.message.id,\n type: \"text\",\n text: block,\n synthetic: true,\n });\n },\n \"tool.execute.after\": async (input, output) => {\n if (EDIT_TOOLS.has(input.tool)) {\n await hook(\"stage\", {\n ...base(input.sessionID, \"tool.execute.after\"),\n tool_name: input.tool,\n tool_input: input.args,\n });\n } else if (input.tool === \"bash\" && typeof input.args?.command === \"string\") {\n const exit = output.metadata?.exit;\n turn(input.sessionID).commands.push({\n command: input.args.command,\n exit_code: typeof exit === \"number\" ? exit : null,\n });\n }\n },\n \"experimental.text.complete\": async (input, output) => {\n if (output.text.trim()) turn(input.sessionID).text = output.text;\n },\n event: async ({ event }) => {\n if (event.type === \"session.idle\") {\n const sessionID = event.properties.sessionID;\n const current = turns.get(sessionID);\n turns.delete(sessionID);\n await hook(\"capture\", {\n ...base(sessionID, \"session.idle\"),\n ...(current?.text ? { last_assistant_message: current.text } : {}),\n ...(current?.commands.length ? { shell_commands: current.commands.slice(-20) } : {}),\n });\n } else if (event.type === \"session.compacted\") {\n await hook(\"reset\", { ...base(event.properties.sessionID, \"session.compacted\"), source: \"compact\" });\n }\n },\n };\n};\n`;\n}\n\nexport const OPENCODE_PLUGIN_SOURCE = renderOpencodePlugin(\"full\");\n\nexport function opencodePluginPath(repositoryRoot: string): string {\n return join(resolve(repositoryRoot), \".opencode\", \"plugins\", \"codebuddy.js\");\n}\n\nfunction plugin(repositoryRoot: string, variant: HookVariant = \"full\"): ManagedPlugin {\n const path = opencodePluginPath(repositoryRoot);\n return {\n marker: OPENCODE_PLUGIN_MARKER,\n files: [{ path, content: renderOpencodePlugin(variant) }],\n dirs: [dirname(path), dirname(dirname(path))],\n };\n}\n\nexport function opencodePluginInstalled(repositoryRoot: string): Promise<boolean> {\n return managedPluginInstalled(plugin(repositoryRoot));\n}\n\n/** Write or remove CodeBuddy's plugin; a same-named file without the marker is never touched. */\nexport function setOpencodePlugin(\n repositoryRoot: string,\n enabled: boolean,\n variant: HookVariant = \"full\",\n): Promise<boolean> {\n return setManagedPlugin(plugin(repositoryRoot, variant), enabled);\n}\n\n/** Which plugin variant is installed, or null when CodeBuddy's plugin is absent. */\nexport async function opencodePluginVariant(repositoryRoot: string): Promise<HookVariant | null> {\n if (!(await opencodePluginInstalled(repositoryRoot))) return null;\n const source = await readFile(opencodePluginPath(repositoryRoot), \"utf8\");\n return source.startsWith(`${OPENCODE_PLUGIN_MARKER} v1 lean`) ? \"lean\" : \"full\";\n}\n","import { mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\n\n/**\n * Installs CodeBuddy's lifecycle hooks into a client's settings JSON. Unlike the\n * Markdown workflow templates, settings files are JSON, so we can't use comment\n * markers — instead every CodeBuddy hook command starts with `codebuddy hooks`,\n * which is how we recognise (and cleanly remove) our own entries. Install is\n * idempotent and never touches hooks the user added themselves.\n *\n * Claude Code hooks fire deterministically, independent of the model's choices:\n * - UserPromptSubmit → `codebuddy hooks context` (recall, injected into prompt)\n * - PostToolUse(Edit|Write|MultiEdit) → `codebuddy hooks stage` (track edits)\n * - Stop → `codebuddy hooks capture` (capture durable memory at turn end)\n * - SessionStart(compact|clear) → `codebuddy hooks reset` (forget what was sent)\n * - PreToolUse(Read|Grep) → `codebuddy hooks pretool` (outline big Reads, cap Greps;\n * both variants, never grants permission)\n */\n\nexport const HOOK_COMMAND_PREFIX = \"codebuddy hooks\";\n\nexport type HookCommand = { type: \"command\"; command: string };\nexport type HookGroup = { matcher?: string; hooks: HookCommand[] };\nexport type HookSettings = { hooks?: Record<string, HookGroup[]> } & Record<string, unknown>;\n\ntype HookDef = { event: string; command: string; matcher?: string };\n\n/** Token guard for tool output; independent of memory, so lean installs it too. */\nconst TOOL_GUARD_DEF: HookDef = {\n event: \"PreToolUse\",\n matcher: \"Read|Grep|Bash\",\n command: `${HOOK_COMMAND_PREFIX} pretool`,\n};\n\nconst HOOK_DEFS: HookDef[] = [\n { event: \"UserPromptSubmit\", command: `${HOOK_COMMAND_PREFIX} context` },\n {\n event: \"PostToolUse\",\n matcher: \"Edit|Write|MultiEdit\",\n command: `${HOOK_COMMAND_PREFIX} stage`,\n },\n { event: \"Stop\", command: `${HOOK_COMMAND_PREFIX} capture` },\n // After compaction or /clear the model no longer holds earlier context, so\n // capsules the session ledger marked as sent must be sent again.\n { event: \"SessionStart\", matcher: \"compact|clear\", command: `${HOOK_COMMAND_PREFIX} reset` },\n TOOL_GUARD_DEF,\n];\n\n/** Lean mode: prompt pointers only, plus the reset that keeps their dedupe correct. */\nconst LEAN_HOOK_DEFS: HookDef[] = [\n { event: \"UserPromptSubmit\", command: `${HOOK_COMMAND_PREFIX} lean` },\n { event: \"SessionStart\", matcher: \"compact|clear\", command: `${HOOK_COMMAND_PREFIX} reset` },\n TOOL_GUARD_DEF,\n];\n\nexport type HookVariant = \"full\" | \"lean\";\n\nexport function claudeSettingsPath(repositoryRoot = process.cwd()): string {\n return join(resolve(repositoryRoot), \".claude\", \"settings.json\");\n}\n\n/** Merge CodeBuddy's hook entries into a settings object. Idempotent. */\nexport function upsertHooks(\n settings: HookSettings,\n variant: HookVariant = \"full\",\n): { settings: HookSettings; changed: boolean } {\n const defs = variant === \"lean\" ? LEAN_HOOK_DEFS : HOOK_DEFS;\n const wanted = new Set(defs.map((def) => def.command));\n const next: HookSettings = { ...settings, hooks: { ...(settings.hooks ?? {}) } };\n const hooks = next.hooks as Record<string, HookGroup[]>;\n let changed = false;\n\n // Switching variants: drop the other variant's CodeBuddy commands, keep the user's.\n for (const event of Object.keys(hooks)) {\n const groups: HookGroup[] = [];\n for (const group of hooks[event] ?? []) {\n const kept = (group.hooks ?? []).filter(\n (hook) => !hook.command?.startsWith(HOOK_COMMAND_PREFIX) || wanted.has(hook.command),\n );\n if (kept.length !== (group.hooks ?? []).length) changed = true;\n if (kept.length > 0) groups.push({ ...group, hooks: kept });\n }\n if (groups.length > 0) hooks[event] = groups;\n else delete hooks[event];\n }\n\n for (const def of defs) {\n const groups = [...(hooks[def.event] ?? [])];\n const existingIndex = groups.findIndex((group) =>\n group.hooks?.some((hook) => hook.command === def.command),\n );\n if (existingIndex === -1) {\n groups.push({\n ...(def.matcher ? { matcher: def.matcher } : {}),\n hooks: [{ type: \"command\", command: def.command }],\n });\n changed = true;\n } else {\n // The command exists, but possibly under a stale matcher (e.g. an older\n // install with matcher:\"Edit\" before MultiEdit coverage). Reconcile the\n // group's matcher so coverage matches the current contract.\n const existing = groups[existingIndex];\n if (existing && (def.matcher ?? undefined) !== (existing.matcher ?? undefined)) {\n const { matcher: _oldMatcher, ...rest } = existing;\n groups[existingIndex] = def.matcher ? { ...rest, matcher: def.matcher } : rest;\n changed = true;\n }\n }\n hooks[def.event] = groups;\n }\n\n return { settings: next, changed };\n}\n\n/** Remove every CodeBuddy hook entry, leaving user hooks intact. */\nexport function removeHooks(settings: HookSettings): { settings: HookSettings; changed: boolean } {\n if (!settings.hooks) return { settings, changed: false };\n const next: HookSettings = { ...settings, hooks: { ...settings.hooks } };\n const hooks = next.hooks as Record<string, HookGroup[]>;\n let changed = false;\n\n for (const event of Object.keys(hooks)) {\n const keptGroups: HookGroup[] = [];\n for (const group of hooks[event] ?? []) {\n // Filter INSIDE groups: a user group mixing our hook with their own\n // commands must keep theirs. Dropping whole groups used to silently\n // delete user hooks that happened to share a group with ours.\n const keptHooks = (group.hooks ?? []).filter(\n (hook) => !hook.command?.startsWith(HOOK_COMMAND_PREFIX),\n );\n if (keptHooks.length !== (group.hooks ?? []).length) changed = true;\n if (keptHooks.length === 0) continue; // group was entirely ours\n keptGroups.push({ ...group, hooks: keptHooks });\n }\n if (keptGroups.length === 0) delete hooks[event];\n else hooks[event] = keptGroups;\n }\n\n return { settings: next, changed };\n}\n\nexport function hooksInstalled(settings: HookSettings): boolean {\n const hooks = settings.hooks ?? {};\n return Object.values(hooks).some((groups) =>\n groups.some((group) => group.hooks?.some((h) => h.command?.startsWith(HOOK_COMMAND_PREFIX))),\n );\n}\n\n/** Which CodeBuddy hook set is installed, or null when none is. */\nexport function hooksVariant(settings: HookSettings): HookVariant | null {\n if (!hooksInstalled(settings)) return null;\n const leanCommand = `${HOOK_COMMAND_PREFIX} lean`;\n const lean = Object.values(settings.hooks ?? {}).some((groups) =>\n groups.some((group) => group.hooks?.some((hook) => hook.command === leanCommand)),\n );\n return lean ? \"lean\" : \"full\";\n}\n\nexport async function readSettings(path: string): Promise<HookSettings> {\n try {\n return JSON.parse(await readFile(path, \"utf8\")) as HookSettings;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n throw error;\n }\n}\n\nexport async function writeSettings(path: string, settings: HookSettings): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n // Atomic so a crash mid-write can never truncate the user's settings.json.\n await writeAtomic(path, `${JSON.stringify(settings, null, 2)}\\n`, { mode: 0o644 });\n}\n","import {\n CLIENT_ADAPTERS,\n CLIENT_IDS,\n type ClientId,\n type HookSurface,\n} from \"../clients/adapters.js\";\nimport {\n hermesPluginDir,\n hermesPluginInstalled,\n hermesPluginVariant,\n setHermesPlugin,\n} from \"./hermes-plugin.js\";\nimport {\n opencodePluginInstalled,\n opencodePluginPath,\n opencodePluginVariant,\n setOpencodePlugin,\n} from \"./opencode-plugin.js\";\nimport {\n claudeSettingsPath,\n type HookVariant,\n hooksInstalled,\n hooksVariant,\n readSettings,\n removeHooks,\n upsertHooks,\n writeSettings,\n} from \"./settings.js\";\n\n/**\n * One install/detect contract per hook surface in the adapter table, so callers\n * (clients install, hooks install, rules mode) never branch on client names.\n */\nconst SURFACES: Record<\n HookSurface,\n {\n path: (root: string) => string;\n installed: (root: string) => Promise<boolean>;\n variant: (root: string) => Promise<HookVariant | null>;\n /** Returns whether anything changed. */\n set: (root: string, enabled: boolean, variant: HookVariant) => Promise<boolean>;\n }\n> = {\n \"claude-settings\": {\n path: claudeSettingsPath,\n installed: async (root) => hooksInstalled(await readSettings(claudeSettingsPath(root))),\n variant: async (root) => hooksVariant(await readSettings(claudeSettingsPath(root))),\n set: async (root, enabled, variant) => {\n const path = claudeSettingsPath(root);\n const current = await readSettings(path);\n const { settings, changed } = enabled ? upsertHooks(current, variant) : removeHooks(current);\n if (changed) await writeSettings(path, settings);\n return changed;\n },\n },\n \"opencode-plugin\": {\n path: opencodePluginPath,\n installed: opencodePluginInstalled,\n variant: opencodePluginVariant,\n set: setOpencodePlugin,\n },\n \"hermes-plugin\": {\n path: hermesPluginDir,\n installed: hermesPluginInstalled,\n variant: hermesPluginVariant,\n set: setHermesPlugin,\n },\n};\n\n/** Install or remove a client's CodeBuddy hooks; null when the client has none. */\nexport async function setClientHooks(\n root: string,\n client: ClientId,\n enabled: boolean,\n variant: HookVariant = \"full\",\n): Promise<{ target: string; changed: boolean } | null> {\n const surface = CLIENT_ADAPTERS[client].hooks;\n if (!surface) return null;\n const { path, set } = SURFACES[surface];\n return { target: path(root), changed: await set(root, enabled, variant) };\n}\n\n/** Which CodeBuddy hook variant a client has; null when the client has no hook surface. */\nexport async function clientHooksVariant(\n root: string,\n client: ClientId,\n): Promise<HookVariant | null | undefined> {\n const surface = CLIENT_ADAPTERS[client].hooks;\n if (!surface) return undefined;\n return SURFACES[surface].variant(root);\n}\n\n/** Whether a client's CodeBuddy hooks are installed; null when the client has none. */\nexport async function clientHooksInstalled(\n root: string,\n client: ClientId,\n): Promise<boolean | null> {\n const surface = CLIENT_ADAPTERS[client].hooks;\n return surface ? SURFACES[surface].installed(root) : null;\n}\n\n/**\n * Clients other than `client` that read the same rules file and have hooks\n * installed. Their rules block must stay hook-driven, or they retrieve and\n * capture twice; the hook-driven block already tells hookless agents to fall\n * back to the agent-driven flow.\n */\nexport async function hookedRulesSiblings(root: string, client: ClientId): Promise<ClientId[]> {\n const file = CLIENT_ADAPTERS[client].rulesFile;\n const hooked: ClientId[] = [];\n for (const id of CLIENT_IDS) {\n if (\n id !== client &&\n CLIENT_ADAPTERS[id].rulesFile === file &&\n (await clientHooksInstalled(root, id))\n )\n hooked.push(id);\n }\n return hooked;\n}\n\n/** Whether any client reading `client`'s rules file (itself included) has hooks installed. */\nexport async function rulesFileHooked(root: string, client: ClientId): Promise<boolean> {\n return (\n (await clientHooksInstalled(root, client)) === true ||\n (await hookedRulesSiblings(root, client)).length > 0\n );\n}\n","import { access, mkdir, readFile, rm } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\n\nexport type ProjectMcpServer = {\n command: string;\n args: string[];\n env?: Record<string, string>;\n};\n\nexport type ProjectMcpConfig = {\n mcpServers?: Record<string, ProjectMcpServer>;\n};\n\nexport type WriteProjectMcpConfigOptions = {\n repositoryRoot: string;\n namespace: string;\n graphify: boolean;\n graphPath?: string;\n};\n\n/** Add CodeBuddy and, optionally, Graphify to the project's MCP config. */\nexport async function writeProjectMcpConfig(\n options: WriteProjectMcpConfigOptions,\n): Promise<{ path: string; servers: string[] }> {\n const path = join(options.repositoryRoot, \".mcp.json\");\n const current = await readProjectMcpConfig(path);\n current.mcpServers ??= {};\n current.mcpServers[`codebuddy-${options.namespace}`] = codebuddyMcpServer(\n options.repositoryRoot,\n options.namespace,\n );\n if (options.graphify) {\n current.mcpServers.graphify = {\n command: \"python\",\n args: [\n \"-m\",\n \"graphify.serve\",\n resolve(options.repositoryRoot, options.graphPath ?? \"graphify-out/graph.json\"),\n ],\n env: { CODEBUDDY_PROJECT_ROOT: options.repositoryRoot },\n };\n } else if (current.mcpServers.graphify) {\n delete current.mcpServers.graphify;\n }\n await writeAtomic(path, `${JSON.stringify(current, null, 2)}\\n`, { mode: 0o644 });\n return { path, servers: Object.keys(current.mcpServers).sort() };\n}\n\n/**\n * Project-local MCP config shapes: the common `mcpServers` map (Claude Code\n * `.mcp.json`, Freebuff `.agents/mcp.json`) and OpenCode's `mcp` map of\n * `{ type: \"local\", command: [...], environment }`.\n */\nexport type McpConfigFormat = \"mcpServers\" | \"opencode\";\n\nconst OPENCODE_SCHEMA = \"https://opencode.ai/config.json\";\n\nexport function codebuddyMcpServer(repositoryRoot: string, namespace: string): ProjectMcpServer {\n return {\n command: \"codebuddy\",\n args: [\"serve\"],\n env: {\n CODEBUDDY_PROJECT_ROOT: repositoryRoot,\n CODEBUDDY_NAMESPACE: namespace,\n CODEBUDDY_TOOL_PROFILE: \"core\",\n },\n };\n}\n\n/**\n * The config file to use: OpenCode reads `opencode.jsonc` too, so when only\n * that one exists it is edited instead of creating a second `opencode.json`.\n */\nexport async function projectMcpPath(\n root: string,\n file: string,\n format: McpConfigFormat,\n): Promise<string> {\n const path = join(root, file);\n if (format !== \"opencode\" || (await exists(path))) return path;\n return (await exists(`${path}c`)) ? `${path}c` : path;\n}\n\nasync function exists(path: string): Promise<boolean> {\n return access(path).then(\n () => true,\n () => false,\n );\n}\n\nexport function formatEntry(format: McpConfigFormat, server: ProjectMcpServer): unknown {\n if (format === \"mcpServers\") return server;\n return {\n type: \"local\",\n command: [server.command, ...server.args],\n ...(server.env ? { environment: server.env } : {}),\n enabled: true,\n };\n}\n\nfunction serversKey(format: McpConfigFormat): \"mcpServers\" | \"mcp\" {\n return format === \"opencode\" ? \"mcp\" : \"mcpServers\";\n}\n\n/**\n * Add or update one named server, keeping every other key the user has. Idempotent.\n * \"manual\" = the file has comments a rewrite would destroy, so nothing was written.\n */\nexport async function upsertMcpServer(\n path: string,\n format: McpConfigFormat,\n name: string,\n server: ProjectMcpServer,\n): Promise<\"created\" | \"updated\" | \"unchanged\" | \"manual\"> {\n const existed = await exists(path);\n const { config, comments } = await readConfig(path);\n const key = serversKey(format);\n const servers = { ...((config[key] as Record<string, unknown> | undefined) ?? {}) };\n const entry = formatEntry(format, server);\n if (JSON.stringify(servers[name]) === JSON.stringify(entry)) return \"unchanged\";\n if (comments) return \"manual\";\n servers[name] = entry;\n const next =\n format === \"opencode\" && !existed\n ? { $schema: OPENCODE_SCHEMA, ...config, [key]: servers }\n : { ...config, [key]: servers };\n await mkdir(dirname(path), { recursive: true });\n await writeAtomic(path, `${JSON.stringify(next, null, 2)}\\n`, { mode: 0o644 });\n return existed ? \"updated\" : \"created\";\n}\n\n/** Remove one named server; delete the file if CodeBuddy's entry was all it held. */\nexport async function removeMcpServer(\n path: string,\n format: McpConfigFormat,\n name: string,\n): Promise<\"removed\" | \"unchanged\" | \"manual\"> {\n const { config, comments } = await readConfig(path);\n const key = serversKey(format);\n const servers = { ...((config[key] as Record<string, unknown> | undefined) ?? {}) };\n if (!(name in servers)) return \"unchanged\";\n if (comments) return \"manual\";\n delete servers[name];\n const next = { ...config, [key]: servers };\n const leftover = Object.keys(next).filter((k) => k !== key && k !== \"$schema\");\n if (Object.keys(servers).length === 0 && leftover.length === 0) await rm(path, { force: true });\n else await writeAtomic(path, `${JSON.stringify(next, null, 2)}\\n`, { mode: 0o644 });\n return \"removed\";\n}\n\nexport async function hasMcpServer(\n path: string,\n format: McpConfigFormat,\n name: string,\n): Promise<boolean> {\n const { config } = await readConfig(path);\n return name in ((config[serversKey(format)] as Record<string, unknown> | undefined) ?? {});\n}\n\n/**\n * Parse JSON with comments and trailing commas (OpenCode accepts both in\n * opencode.json and opencode.jsonc). Strings match first, so `//` or `,}`\n * inside a string value are left alone.\n */\nexport function parseJsonc(raw: string): { value: unknown; comments: boolean } {\n let comments = false;\n const json = raw\n .replace(/\"(?:[^\"\\\\]|\\\\.)*\"|\\/\\/[^\\n]*|\\/\\*[\\s\\S]*?\\*\\//g, (match) => {\n if (match.startsWith('\"')) return match;\n comments = true;\n return \" \";\n })\n .replace(/\"(?:[^\"\\\\]|\\\\.)*\"|,(\\s*[}\\]])/g, (match, tail: string | undefined) => tail ?? match);\n return { value: JSON.parse(json), comments };\n}\n\nasync function readConfig(\n path: string,\n): Promise<{ config: Record<string, unknown>; comments: boolean }> {\n try {\n const { value, comments } = parseJsonc(await readFile(path, \"utf8\"));\n const config = value && typeof value === \"object\" ? (value as Record<string, unknown>) : {};\n return { config, comments };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { config: {}, comments: false };\n throw new Error(`Could not read ${path}: ${(error as Error).message}`);\n }\n}\n\nasync function readProjectMcpConfig(path: string): Promise<ProjectMcpConfig> {\n return (await readConfig(path)).config as ProjectMcpConfig;\n}\n","import { readFile, rm } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport type { AutomationMode } from \"../clients/adapters.js\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\nimport { rulesFileHooked } from \"../hooks/install.js\";\nimport {\n defaultRulesFile,\n parseWorkflowMode,\n renderWorkflowTemplate,\n type WorkflowClient,\n} from \"./workflow.js\";\n\n/**\n * Safe, visible install of the workflow template into a client's Markdown rules\n * file. The template is written inside HTML-comment markers so:\n * - updates are idempotent (the block is replaced, not appended again);\n * - the user's own content around the block is preserved untouched;\n * - the markers make it obvious exactly what CodeBuddy manages.\n * No hidden mutation: the caller sees the resolved path and the action taken.\n */\n\nconst START_MARKER = \"<!-- codebuddy:workflow:start -->\";\nconst END_MARKER = \"<!-- codebuddy:workflow:end -->\";\n\nexport type InstallAction = \"created\" | \"added\" | \"updated\" | \"removed\" | \"unchanged\";\n\nexport type InstallResult = {\n path: string;\n client: WorkflowClient;\n action: InstallAction;\n};\n\nexport type InstallRulesOptions = {\n client: WorkflowClient;\n projectRoot?: string;\n /** Override the target file (defaults to CLAUDE.md / AGENTS.md). */\n targetPath?: string;\n /** `mcp-only` removes the managed block. Defaults to `defaultRulesMode`. */\n mode?: AutomationMode;\n /** Only rewrite an existing managed block; never add one (mode reconciliation). */\n onlyIfManaged?: boolean;\n};\n\nexport function renderManagedBlock(\n client: WorkflowClient,\n mode: Exclude<AutomationMode, \"mcp-only\"> = \"agent\",\n): string {\n return `${START_MARKER}\\n${renderWorkflowTemplate(client, mode)}\\n${END_MARKER}\\n`;\n}\n\nexport async function installWorkflowRules(options: InstallRulesOptions): Promise<InstallResult> {\n const projectRoot = resolve(options.projectRoot ?? process.cwd());\n const path = options.targetPath ?? join(projectRoot, defaultRulesFile(options.client));\n\n const existing = await readIfPresent(path);\n const mode = options.mode ?? (await defaultRulesMode(options.client, projectRoot));\n if (options.onlyIfManaged && (existing === null || !hasManagedBlock(existing))) {\n return { path, client: options.client, action: \"unchanged\" };\n }\n if (mode === \"mcp-only\") {\n const content = existing === null ? null : removeBlock(existing);\n if (content === null || content === existing) {\n return { path, client: options.client, action: \"unchanged\" };\n }\n // Leave no empty file behind when CodeBuddy's block was all it held.\n if (content.trim() === \"\") await rm(path, { force: true });\n else await writeAtomic(path, content, { mode: 0o644 });\n return { path, client: options.client, action: \"removed\" };\n }\n const { content, action } = upsertBlock(existing, renderManagedBlock(options.client, mode));\n\n if (action === \"unchanged\") return { path, client: options.client, action };\n // Atomic: a crash mid-write must never truncate the user's CLAUDE.md/AGENTS.md.\n await writeAtomic(path, content, { mode: 0o644 });\n return { path, client: options.client, action };\n}\n\n/** Compute the new file content and what changed, without touching disk. */\nexport function upsertBlock(\n existing: string | null,\n block: string,\n): { content: string; action: InstallAction } {\n if (existing === null) return { content: block, action: \"created\" };\n\n const start = existing.indexOf(START_MARKER);\n const end = existing.indexOf(END_MARKER);\n if (start !== -1 && end !== -1 && end > start) {\n const before = existing.slice(0, start);\n const after = existing.slice(end + END_MARKER.length).replace(/^\\n/, \"\");\n const replaced = `${before}${block}${after}`;\n return { content: replaced, action: replaced === existing ? \"unchanged\" : \"updated\" };\n }\n\n // No managed block yet — append, keeping the user's content intact.\n const separator =\n existing.length === 0 || existing.endsWith(\"\\n\\n\")\n ? \"\"\n : existing.endsWith(\"\\n\")\n ? \"\\n\"\n : \"\\n\\n\";\n return { content: `${existing}${separator}${block}`, action: \"added\" };\n}\n\n/**\n * Rules follow the installed hooks: hook-driven when CodeBuddy hooks are present\n * for any client reading this rules file, agent-driven otherwise (the hook-driven\n * block tells hookless agents to use the agent flow). Every caller that installs rules\n * without an explicit mode (rules install, `codebuddy use`) gets this, so a\n * re-run never re-adds agent-driven calls the hooks already make.\n */\nexport async function defaultRulesMode(\n client: WorkflowClient,\n projectRoot = process.cwd(),\n): Promise<\"agent\" | \"hooks\"> {\n return (await rulesFileHooked(projectRoot, client)) ? \"hooks\" : \"agent\";\n}\n\n/** The mode of the managed block in a rules file, or null when there is none. */\nexport async function readManagedMode(path: string): Promise<AutomationMode | null> {\n const existing = await readIfPresent(path);\n if (existing === null || !hasManagedBlock(existing)) return null;\n return parseWorkflowMode(existing.slice(existing.indexOf(START_MARKER)));\n}\n\nfunction hasManagedBlock(content: string): boolean {\n const start = content.indexOf(START_MARKER);\n return start !== -1 && content.indexOf(END_MARKER) > start;\n}\n\n/** Drop the managed block (and the blank line that separated it), keeping user text. */\nexport function removeBlock(existing: string): string {\n if (!hasManagedBlock(existing)) return existing;\n const start = existing.indexOf(START_MARKER);\n const end = existing.indexOf(END_MARKER) + END_MARKER.length;\n const before = existing.slice(0, start).replace(/\\n\\n$/, \"\\n\");\n const after = existing.slice(end).replace(/^\\n/, \"\");\n return `${before}${after}`;\n}\n\nasync function readIfPresent(path: string): Promise<string | null> {\n try {\n return await readFile(path, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n}\n","/**\n * Client workflow templates (Proposal 04 Phase 04.7). These are the concrete\n * rules that make CodeBuddy's automatic context flow reliable in Claude Code and\n * Codex: they say exactly which MCP tool to call, when, and what to do when a\n * tool or service is unavailable. The text references only tools that actually\n * ship (04.2–04.6): context_pack, context_bootstrap, context_before_edit,\n * context_after_turn, code_suggestions.\n *\n * Templates are installed into a client's Markdown rules file inside a clearly\n * delimited managed block (see `install.ts`) so updates are idempotent and the\n * user can always see exactly what CodeBuddy added.\n *\n * The automation mode decides who drives retrieval and capture. Agent-driven\n * rules ask the model to call the tools; hook-driven rules defer to the\n * lifecycle hooks so a turn is not retrieved or captured twice.\n */\n\nimport {\n type AutomationMode,\n CLIENT_ADAPTERS,\n CLIENT_IDS,\n type ClientId,\n MODE_LABELS,\n} from \"../clients/adapters.js\";\n\nexport const WORKFLOW_CLIENTS = CLIENT_IDS;\nexport type WorkflowClient = ClientId;\n\n/** The shared body, split where the automation mode changes who acts. */\nconst INTRO = [\n \"You have CodeBuddy available as a local, MCP-backed project-context engine.\",\n \"Use it to work like a careful senior engineer, not to dump broad context.\",\n \"\",\n];\n\nconst START_AGENT = [\n \"## At the start of a substantive coding turn\",\n \"\",\n \"1. When project context is needed, call `context_pack` with the user's task and a token budget (start\",\n \" with 4000). It automatically finds relevant files and returns bounded symbols,\",\n \" plan, policy, incidents, risks, architecture neighbours, and team facts.\",\n \" Reuse evidence already present. For a clearly identified small edit, read the\",\n \" needed source directly; fetch context for unknown dependencies or project constraints.\",\n \" Read missing function bodies before editing. If safety evidence was omitted,\",\n \" narrow the targets or increase the budget before proceeding.\",\n \"2. Use `context_bootstrap` only when you need broad project orientation beyond\",\n \" the task pack. Use `context_before_edit` when you already have exact paths\",\n \" or a plan and need a narrower follow-up.\",\n \"3. Prior work from earlier chats arrives as `handoffs` with freshness\",\n \" (`changedSince`, `missing`); re-check those files before relying on it.\",\n \"4. On repeated context calls in one conversation, pass a stable `sessionId` and\",\n \" the previous response's `delivery.deliveryId` as `ack`. Unchanged sections\",\n \" are then omitted and listed in `delivery.unchanged`. If earlier output is no\",\n \" longer in your context (e.g. after compaction), pass `full: true`.\",\n \"\",\n];\n\nconst START_HOOKS = [\n \"## At the start of a substantive coding turn\",\n \"\",\n \"CodeBuddy hooks inject a `CodeBuddy project context (auto-recalled)` block into\",\n \"each prompt: project, plan, policies, facts, incidents, handoffs, current\",\n \"changes, and risk. Agents without CodeBuddy hooks (no such block in the prompt)\",\n \"follow the agent-driven flow: `context_pack` first, `context_after_turn` last.\",\n \"\",\n \"1. Start from the injected block; do not call `context_bootstrap` for orientation\",\n \" it already covers. Its `task files` are CodeBuddy's best match; when it names\",\n \" none, call `context_pack` with the task before searching the repository yourself.\",\n \"2. Read ranges, not whole files: `Read` with offset/limit, or `sed -n 'A,Bp'`.\",\n \" Large files read whole (`Read` without a range, `cat`, `less`) are denied with an\",\n \" outline of their symbols and line ranges; read the range you need from it.\",\n \"3. Call `context_pack` (or `context_before_edit` with exact paths) when the\",\n \" task needs symbols, neighbours, or file-level evidence the block lacks. Pass the\",\n \" block's `session` value as `sessionId` and the previous `delivery.deliveryId`\",\n \" as `ack` so unchanged sections are not resent.\",\n \"4. Prior work from earlier chats arrives as handoffs with freshness; re-check\",\n \" those files before relying on it.\",\n \"\",\n];\n\nconst MIDDLE = [\n \"## While implementing\",\n \"\",\n \"- Prefer the smallest sufficient set of edits; preserve existing behavior.\",\n \"- Honor risk warnings and policy rules for the files you touch; call out risk\",\n \" before editing sensitive areas.\",\n \"- Do not re-request broad context if the task pack already suffices.\",\n \"\",\n \"## Before finalizing\",\n \"\",\n \"- Consider calling `code_suggestions` (with `paths`, `planId`, or `useGit`) for\",\n \" read-only, evidence-backed quality findings; resolve high-severity ones.\",\n \"- Run the smallest relevant verification (tests, lint, typecheck) and fix\",\n \" failures before presenting completion.\",\n \"\",\n];\n\nconst AFTER_AGENT = [\n \"## After a meaningful turn\",\n \"\",\n \"- Call `context_after_turn` with a concise `summary`, the `changedFiles`, and a\",\n \" `planId` if one is active, plus `verification` (commands run and results)\",\n \" so the turn's worklog handoff records it. CodeBuddy auto-saves confident facts, decisions,\",\n \" and incidents, queues uncertain or sensitive items for review, and drops\",\n \" chatter. Never paste secrets, tokens, or credentials into the summary.\",\n \"\",\n];\n\nconst AFTER_HOOKS = [\n \"## After a meaningful turn\",\n \"\",\n \"- Do not call `context_after_turn`: the turn-end hook captures the turn once, from\",\n \" the real change set and the verification commands you ran. A second capture\",\n \" would record a divergent worklog entry.\",\n \"- Never paste secrets, tokens, or credentials into your final message; it is the\",\n \" capture summary.\",\n \"\",\n];\n\nconst TAIL = [\n \"## If a tool or service is unavailable (degraded mode)\",\n \"\",\n \"- A CodeBuddy MCP tool errors or is missing: proceed using the repository\",\n \" itself as the source of truth; do not block on it.\",\n \"- Postgres is unreachable: the file-based tools (context, plan, risk, map,\",\n \" suggest) still work; recall and embeddings may be degraded — say so instead\",\n \" of inventing remembered facts.\",\n \"- Retrieval returns no context: read the relevant files directly rather than\",\n \" guessing; the working tree is authoritative.\",\n \"\",\n \"## Never\",\n \"\",\n \"- Invent behavior the codebase does not support.\",\n \"- Ignore failing tests or lint.\",\n \"- Make speculative, wide changes without evidence.\",\n \"- Save low-confidence project memory as a durable fact.\",\n];\n\nfunction renderBody(mode: Exclude<AutomationMode, \"mcp-only\">): string {\n return [\n ...INTRO,\n ...(mode === \"hooks\" ? START_HOOKS : START_AGENT),\n ...MIDDLE,\n ...(mode === \"hooks\" ? AFTER_HOOKS : AFTER_AGENT),\n ...TAIL,\n ].join(\"\\n\");\n}\n\n/** Clients whose rules live in the same file share one managed block. */\nfunction rulesReaders(file: string): string {\n return CLIENT_IDS.filter((id) => CLIENT_ADAPTERS[id].rulesFile === file)\n .map((id) => CLIENT_ADAPTERS[id].name)\n .join(\", \");\n}\n\n/**\n * Render the full template (heading + body). The text depends only on the\n * rules file and mode, so clients sharing AGENTS.md install an identical block.\n */\nexport function renderWorkflowTemplate(\n client: WorkflowClient,\n mode: Exclude<AutomationMode, \"mcp-only\"> = \"agent\",\n): string {\n const file = CLIENT_ADAPTERS[client].rulesFile;\n return [\n `# CodeBuddy workflow (${rulesReaders(file)})`,\n \"\",\n \"This section is managed by CodeBuddy (`codebuddy clients install <client> --mode <mode>`)\",\n `and lives in \\`${file}\\`. Edit outside the managed markers to customize.`,\n `Automation mode: \\`${mode}\\` (${MODE_LABELS[mode]}).`,\n \"\",\n renderBody(mode),\n ].join(\"\\n\");\n}\n\n/** The mode recorded in a rendered block; blocks from before modes existed are agent-driven. */\nexport function parseWorkflowMode(block: string): AutomationMode {\n return block.match(/Automation mode: `(hooks|agent)`/)?.[1] === \"hooks\" ? \"hooks\" : \"agent\";\n}\n\nexport function defaultRulesFile(client: WorkflowClient): string {\n return CLIENT_ADAPTERS[client].rulesFile;\n}\n\nexport function clientDisplayName(client: WorkflowClient): string {\n return CLIENT_ADAPTERS[client].name;\n}\n","import { existsSync } from \"node:fs\";\nimport { mkdir, readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { writeAtomic } from \"../../core/markdown-store-fs.js\";\nimport { buildEntry, type ClaudeDesktopEntry } from \"./claude-desktop.js\";\n\nexport type CodexConfigEntry = ClaudeDesktopEntry;\n\nexport function codexConfigPath(home = homedir()): string {\n return join(home, \".codex\", \"config.toml\");\n}\n\nexport async function readCodexConfig(path: string): Promise<string> {\n if (!existsSync(path)) return \"\";\n return readFile(path, \"utf8\");\n}\n\nexport async function writeCodexConfig(path: string, content: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n // Atomic + 0600: the TOML carries env secrets (HF token, DATABASE_URL).\n await writeAtomic(path, content, { mode: 0o600 });\n}\n\nexport type InstallCodexEntryOptions = {\n namespace: string;\n projectRoot?: string;\n hfToken?: string;\n databaseUrl?: string;\n serverName?: string;\n toolProfile?: \"core\" | \"full\";\n};\n\nexport type InstallCodexResult = {\n path: string;\n serverKey: string;\n created: boolean;\n changed: boolean;\n};\n\nexport async function installCodexEntry(\n options: InstallCodexEntryOptions,\n): Promise<InstallCodexResult> {\n const path = codexConfigPath();\n const serverKey = options.serverName ?? `codebuddy-${options.namespace}`;\n const current = await readCodexConfig(path);\n const existed = hasServerBlock(current, serverKey);\n const entry = buildEntry(options);\n if (options.toolProfile)\n entry.env = { ...entry.env, CODEBUDDY_TOOL_PROFILE: options.toolProfile };\n const next = upsertServerBlock(current, serverKey, entry);\n if (next !== current) await writeCodexConfig(path, next);\n return { path, serverKey, created: !existed, changed: next !== current };\n}\n\nexport async function removeCodexEntry(\n serverKey: string,\n): Promise<{ removed: boolean; path: string }> {\n const path = codexConfigPath();\n const current = await readCodexConfig(path);\n const next = removeServerBlock(current, serverKey);\n const removed = next !== current;\n // Drop the blank separator install added, so install + remove is a no-op.\n const trimmed = next.trimEnd();\n if (removed) await writeCodexConfig(path, trimmed ? `${trimmed}\\n` : \"\");\n return { removed, path };\n}\n\nexport async function listCodexEntries(): Promise<{\n path: string;\n entries: Array<{ key: string; namespace: string | null; databaseUrl: string | null }>;\n}> {\n const path = codexConfigPath();\n const config = await readCodexConfig(path);\n const entries = Array.from(config.matchAll(/^\\[mcp_servers\\.\"([^\"]+)\"\\]\\s*$/gm))\n .flatMap((match) => (match[1] ? [match[1]] : []))\n .filter((key) => key === \"codebuddy\" || key.startsWith(\"codebuddy-\"))\n .map((key) => {\n const block = getServerBlock(config, key);\n return {\n key,\n namespace: readEnvValue(block, \"CODEBUDDY_NAMESPACE\"),\n databaseUrl: readEnvValue(block, \"DATABASE_URL\"),\n };\n });\n return { path, entries };\n}\n\nfunction upsertServerBlock(config: string, serverKey: string, entry: CodexConfigEntry): string {\n const trimmed = removeServerBlock(config, serverKey).trimEnd();\n const block = formatServerBlock(serverKey, entry);\n return `${trimmed}${trimmed ? \"\\n\\n\" : \"\"}${block}\\n`;\n}\n\nfunction removeServerBlock(config: string, serverKey: string): string {\n const escaped = escapeRegExp(serverKey);\n const end = String.raw`(?![\\s\\S])`;\n const sectionPattern = String.raw`^\\[mcp_servers\\.\"${escaped}\"\\][\\s\\S]*?(?=^\\[[^\\]]+\\]\\s*$|${end})`;\n const envPattern = String.raw`^\\[mcp_servers\\.\"${escaped}\"\\.env\\][\\s\\S]*?(?=^\\[[^\\]]+\\]\\s*$|${end})`;\n return config\n .replace(new RegExp(sectionPattern, \"gm\"), \"\")\n .replace(new RegExp(envPattern, \"gm\"), \"\");\n}\n\nfunction getServerBlock(config: string, serverKey: string): string {\n const escaped = escapeRegExp(serverKey);\n const end = String.raw`(?![\\s\\S])`;\n const pattern = String.raw`^\\[mcp_servers\\.\"${escaped}\"\\][\\s\\S]*?(?=^\\[mcp_servers\\.\"${escaped}\"\\.env\\][\\s\\S]*?(?=^\\[[^\\]]+\\]\\s*$|${end})|^\\[[^\\]]+\\]\\s*$|${end})`;\n const server = config.match(new RegExp(pattern, \"m\"))?.[0] ?? \"\";\n const env =\n config.match(\n new RegExp(\n String.raw`^\\[mcp_servers\\.\"${escaped}\"\\.env\\][\\s\\S]*?(?=^\\[[^\\]]+\\]\\s*$|${end})`,\n \"m\",\n ),\n )?.[0] ?? \"\";\n return `${server}\\n${env}`;\n}\n\nfunction hasServerBlock(config: string, serverKey: string): boolean {\n return new RegExp(String.raw`^\\[mcp_servers\\.\"${escapeRegExp(serverKey)}\"\\]\\s*$`, \"m\").test(\n config,\n );\n}\n\nfunction formatServerBlock(serverKey: string, entry: CodexConfigEntry): string {\n const lines = [\n `[mcp_servers.${tomlKey(serverKey)}]`,\n `command = ${tomlString(entry.command)}`,\n `args = ${tomlArray(entry.args)}`,\n ];\n if (entry.env && Object.keys(entry.env).length > 0) {\n lines.push(\"\", `[mcp_servers.${tomlKey(serverKey)}.env]`);\n for (const [key, value] of Object.entries(entry.env).sort(([left], [right]) =>\n left.localeCompare(right),\n )) {\n lines.push(`${key} = ${tomlString(value)}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\nfunction readEnvValue(block: string, key: string): string | null {\n return block.match(new RegExp(`^${escapeRegExp(key)}\\\\s*=\\\\s*\"([^\"]*)\"`, \"m\"))?.[1] ?? null;\n}\n\nfunction tomlKey(key: string): string {\n return `\"${key.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll('\"', '\\\\\"')}\"`;\n}\n\nfunction tomlString(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction tomlArray(values: string[]): string {\n return `[${values.map(tomlString).join(\", \")}]`;\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { existsSync } from \"node:fs\";\nimport { mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { writeAtomic } from \"../../core/markdown-store-fs.js\";\n\nexport type ClaudeDesktopEntry = {\n command: string;\n args: string[];\n env?: Record<string, string>;\n};\n\nexport type ClaudeDesktopConfig = {\n mcpServers?: Record<string, ClaudeDesktopEntry>;\n};\n\nexport function claudeDesktopConfigPath(home = process.env.HOME ?? \"\"): string {\n if (process.platform === \"darwin\") {\n return join(home, \"Library\", \"Application Support\", \"Claude\", \"claude_desktop_config.json\");\n }\n if (process.platform === \"win32\") {\n return join(process.env.APPDATA ?? home, \"Claude\", \"claude_desktop_config.json\");\n }\n return join(home, \".config\", \"Claude\", \"claude_desktop_config.json\");\n}\n\nexport async function readClaudeConfig(path: string): Promise<ClaudeDesktopConfig> {\n if (!existsSync(path)) return {};\n try {\n return JSON.parse(await readFile(path, \"utf8\")) as ClaudeDesktopConfig;\n } catch (error) {\n throw new Error(\n `Failed to parse Claude Desktop config at ${path}: ${(error as Error).message}`,\n );\n }\n}\n\nexport async function writeClaudeConfig(path: string, config: ClaudeDesktopConfig): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n // Atomic + 0600: this config carries env secrets (HF token, DATABASE_URL).\n // A crash mid-write must never truncate the user's other MCP entries.\n await writeAtomic(path, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n}\n\n/**\n * Resolve the absolute path to this codebuddy install's CLI entrypoint.\n *\n * Has to cope with three layouts:\n * - dev: `tsx src/cli/index.ts` — argv[1] is the .ts file\n * - built locally: `node dist/cli/index.js serve` — argv[1] is dist/cli/index.js\n * - npm bin: `/usr/local/bin/codebuddy ...` — argv[1] is the resolved bin\n *\n * `process.argv[1]` is the file Node was invoked with, which IS the CLI entry\n * in every supported case. We use it as the source of truth. import.meta.url\n * is unreliable here because tsup bundles src/cli/* into a single dist file,\n * so the source-relative path math (`..`) lands on the SDK barrel\n * (dist/index.js) instead of the CLI (dist/cli/index.js).\n */\nexport function resolveCliEntrypoint(): string {\n const fromArgv = process.argv[1];\n if (fromArgv && existsSync(fromArgv)) return fromArgv;\n\n // Fallback for the unlikely case where argv[1] is missing (eval'd, embedded\n // host, etc): walk the source layout.\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n resolve(here, \"index.js\"), // bundled: here = dist/cli/\n resolve(here, \"..\", \"cli\", \"index.js\"), // source: here = src/cli/commands/\n resolve(here, \"..\", \"index.js\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) return candidate;\n }\n // Last resort — return argv[1] even if it doesn't exist, so the error\n // surfaces with the real path the caller saw rather than a stale guess.\n return fromArgv ?? candidates[0] ?? \"\";\n}\n\nexport type InstallEntryOptions = {\n namespace: string;\n projectRoot?: string;\n hfToken?: string;\n groqApiKey?: string;\n databaseUrl?: string;\n serverName?: string;\n};\n\nexport function buildEntry(options: InstallEntryOptions): ClaudeDesktopEntry {\n const env: Record<string, string> = {\n CODEBUDDY_NAMESPACE: options.namespace,\n CODEBUDDY_PROJECT_ROOT: resolve(options.projectRoot ?? process.cwd()),\n };\n if (options.hfToken) env.HF_TOKEN = options.hfToken;\n if (options.groqApiKey) env.GROQ_API_KEY = options.groqApiKey;\n if (options.databaseUrl) env.DATABASE_URL = options.databaseUrl;\n return {\n // Absolute: Claude Desktop is a GUI app and does not inherit the shell's\n // PATH (nvm, Homebrew), so a bare \"node\" may not resolve or may be another\n // version. Re-run `codebuddy claude install` after switching Node.\n command: process.execPath,\n args: [resolveCliEntrypoint(), \"serve\"],\n env,\n };\n}\n\nexport type InstallResult = {\n path: string;\n serverKey: string;\n created: boolean;\n};\n\nexport async function installClaudeEntry(options: InstallEntryOptions): Promise<InstallResult> {\n const path = claudeDesktopConfigPath();\n const config = await readClaudeConfig(path);\n config.mcpServers ??= {};\n const serverKey = options.serverName ?? `codebuddy-${options.namespace}`;\n const existed = config.mcpServers[serverKey] !== undefined;\n config.mcpServers[serverKey] = buildEntry(options);\n await writeClaudeConfig(path, config);\n return { path, serverKey, created: !existed };\n}\n\nexport async function removeClaudeEntry(\n serverKey: string,\n): Promise<{ removed: boolean; path: string }> {\n const path = claudeDesktopConfigPath();\n const config = await readClaudeConfig(path);\n if (!config.mcpServers || !(serverKey in config.mcpServers)) {\n return { removed: false, path };\n }\n delete config.mcpServers[serverKey];\n await writeClaudeConfig(path, config);\n return { removed: true, path };\n}\n\nexport async function listClaudeEntries(): Promise<{\n path: string;\n entries: Array<{ key: string; namespace: string | null; databaseUrl: string | null }>;\n}> {\n const path = claudeDesktopConfigPath();\n const config = await readClaudeConfig(path);\n const entries = Object.entries(config.mcpServers ?? {})\n .filter(([key]) => key === \"codebuddy\" || key.startsWith(\"codebuddy-\"))\n .map(([key, entry]) => ({\n key,\n namespace: entry.env?.CODEBUDDY_NAMESPACE ?? null,\n databaseUrl: entry.env?.DATABASE_URL ?? null,\n }));\n return { path, entries };\n}\n","import { mkdir, readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\nimport type { ProjectMcpServer } from \"./project-mcp.js\";\n\n/**\n * Key-scoped edits of Hermes' global `config.yaml`. A YAML round-trip would\n * refold the user's long strings and drop formatting, so only the lines of\n * one `mcp_servers.<name>` entry are rewritten; every other line and comment\n * stays byte-for-byte. Each edit is re-parsed and must equal the old config\n * with exactly that entry changed, otherwise nothing is written (\"manual\").\n */\n\n/** Hermes honours HERMES_HOME, like `hermes` itself. */\nexport function hermesConfigPath(home = homedir()): string {\n return join(process.env.HERMES_HOME ?? join(home, \".hermes\"), \"config.yaml\");\n}\n\ntype Config = Record<string, unknown>;\n\nasync function readRaw(path: string): Promise<string> {\n try {\n return await readFile(path, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return \"\";\n throw error;\n }\n}\n\n/** The parsed top-level map, or null when the file is not a YAML map CodeBuddy can reason about. */\nfunction parseConfig(raw: string): Config | null {\n try {\n const value = parseYaml(raw) ?? {};\n return value && typeof value === \"object\" && !Array.isArray(value) ? (value as Config) : null;\n } catch {\n return null;\n }\n}\n\nfunction servers(config: Config): Record<string, unknown> {\n const value = config.mcp_servers;\n return value && typeof value === \"object\" ? (value as Record<string, unknown>) : {};\n}\n\nconst isItem = (line: string) => line.trim() !== \"\" && !line.trim().startsWith(\"#\");\nconst indentOf = (line: string) => line.length - line.trimStart().length;\n\n/** Text edit of `mcp_servers.<name>`; `entry` null removes it. */\nexport function editMcpServers(raw: string, name: string, entry: unknown | null): string {\n const lines = raw.split(\"\\n\");\n const header = lines.findIndex((line) => /^mcp_servers:\\s*(#.*)?$/.test(line));\n const render = (indent: number) =>\n stringifyYaml({ [name]: entry })\n .trimEnd()\n .split(\"\\n\")\n .map((line) => `${\" \".repeat(indent)}${line}`);\n\n if (header === -1) {\n if (entry === null) return raw;\n // No separator line, so removing the entry restores the file byte-for-byte.\n const body = raw === \"\" || raw.endsWith(\"\\n\") ? raw : `${raw}\\n`;\n return `${body}mcp_servers:\\n${render(2).join(\"\\n\")}\\n`;\n }\n // The block runs until the next line that starts at column 0.\n let end = header + 1;\n while (end < lines.length && !/^\\S/.test(lines[end] ?? \"\")) end++;\n const block = Array.from({ length: end - header - 1 }, (_, i) => header + 1 + i);\n const items = block.filter((i) => isItem(lines[i] ?? \"\"));\n const indent = items.length > 0 ? indentOf(lines[items[0] as number] ?? \"\") : 2;\n const quoted = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const keyLine = new RegExp(`^ {${indent}}(?:${quoted}|\"${quoted}\"|'${quoted}'):\\\\s*(#.*)?$`);\n const start = items.find((i) => keyLine.test(lines[i] ?? \"\"));\n\n if (start === undefined) {\n if (entry === null) return raw;\n const at = (items.at(-1) ?? header) + 1;\n lines.splice(at, 0, ...render(indent));\n return lines.join(\"\\n\");\n }\n // Our entry ends at the next sibling key or sibling-level comment (the user's).\n const next = block.find(\n (i) => i > start && (lines[i] ?? \"\").trim() !== \"\" && indentOf(lines[i] ?? \"\") <= indent,\n );\n let stop = next ?? end;\n while (stop > start + 1 && (lines[stop - 1] ?? \"\").trim() === \"\") stop--;\n lines.splice(start, stop - start, ...(entry === null ? [] : render(indent)));\n // Removing the only entry leaves `mcp_servers:` null; drop the header as well.\n if (entry === null && items.length > 0 && items.every((i) => i >= start && i < stop)) {\n lines.splice(header, 1);\n }\n return lines.join(\"\\n\");\n}\n\nexport type HermesMcpAction = \"created\" | \"updated\" | \"unchanged\" | \"removed\" | \"manual\";\n\n/** Add, update (entry) or remove (null) CodeBuddy's server; never touches anything else. */\nexport async function setHermesMcpServer(\n path: string,\n name: string,\n entry: ProjectMcpServer | null,\n): Promise<HermesMcpAction> {\n const raw = await readRaw(path);\n const before = parseConfig(raw);\n if (!before) return \"manual\";\n const current = servers(before)[name];\n if (entry === null ? current === undefined : isDeepStrictEqual(current, entry)) {\n return \"unchanged\";\n }\n const nextServers = { ...servers(before) };\n if (entry === null) delete nextServers[name];\n else nextServers[name] = entry;\n const { mcp_servers: _, ...rest } = before;\n const expected =\n Object.keys(nextServers).length > 0 ? { ...rest, mcp_servers: nextServers } : rest;\n\n const next = editMcpServers(raw, name, entry);\n if (!isDeepStrictEqual(parseConfig(next), expected)) return \"manual\";\n await mkdir(dirname(path), { recursive: true });\n // Atomic + 0600: Hermes keeps provider keys in this file.\n await writeAtomic(path, next, { mode: 0o600 });\n return entry === null ? \"removed\" : current === undefined ? \"created\" : \"updated\";\n}\n\nexport async function readHermesConfig(path: string): Promise<Config | null> {\n return parseConfig(await readRaw(path));\n}\n\nexport async function hasHermesMcpServer(path: string, name: string): Promise<boolean> {\n const config = await readHermesConfig(path);\n return config !== null && name in servers(config);\n}\n","import {\n codexConfigPath,\n installCodexEntry,\n listCodexEntries,\n removeCodexEntry,\n} from \"../cli/commands/codex.js\";\nimport {\n hasHermesMcpServer,\n hermesConfigPath,\n setHermesMcpServer,\n} from \"../integrations/hermes-config.js\";\nimport { codebuddyMcpServer } from \"../integrations/project-mcp.js\";\n\n/**\n * The CodeBuddy server entry in a client's global MCP config, written only on\n * explicit opt-in (`clients install|uninstall --global`). Codex reuses the\n * `codebuddy codex install` TOML editor; Hermes gets a key-scoped YAML edit.\n */\nexport type GlobalMcp = \"codex\" | \"hermes\";\n\nexport function globalMcpPath(target: GlobalMcp): string {\n return target === \"codex\" ? codexConfigPath() : hermesConfigPath();\n}\n\nexport async function setGlobalMcp(\n target: GlobalMcp,\n options: { name: string; root: string; namespace: string; enabled: boolean },\n): Promise<string> {\n const { name, root, namespace, enabled } = options;\n if (target === \"hermes\") {\n return setHermesMcpServer(\n hermesConfigPath(),\n name,\n enabled ? codebuddyMcpServer(root, namespace) : null,\n );\n }\n if (!enabled) return (await removeCodexEntry(name)).removed ? \"removed\" : \"unchanged\";\n const result = await installCodexEntry({\n namespace,\n projectRoot: root,\n serverName: name,\n toolProfile: \"core\",\n });\n return result.created ? \"created\" : result.changed ? \"updated\" : \"unchanged\";\n}\n\nexport async function hasGlobalMcp(target: GlobalMcp, name: string): Promise<boolean> {\n if (target === \"hermes\") return hasHermesMcpServer(hermesConfigPath(), name);\n return (await listCodexEntries()).entries.some((entry) => entry.key === name);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA,SAAS,UAAAA,eAAc;AACvB,SAAS,QAAAC,OAAM,WAAAC,gBAAe;;;ACD9B,SAAS,YAAAC,iBAAgB;AACzB,SAAS,WAAAC,UAAS,MAAM,eAAe;;;ACDvC,SAAS,OAAO,UAAU,IAAI,aAAa;AAC3C,SAAS,eAAe;AAexB,eAAe,KAAK,MAAsC;AACxD,MAAI;AACF,WAAO,MAAM,SAAS,MAAM,MAAM;AAAA,EACpC,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU,QAAO;AAC/D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,uBAAuBC,SAAyC;AACpF,aAAW,QAAQA,QAAO,OAAO;AAC/B,QAAI,EAAE,MAAM,KAAK,KAAK,IAAI,IAAI,WAAWA,QAAO,MAAM,EAAG,QAAO;AAAA,EAClE;AACA,SAAO;AACT;AAGA,eAAsB,iBAAiBA,SAAuB,SAAoC;AAChG,QAAM,UAAU,MAAM,QAAQ,IAAIA,QAAO,MAAM,IAAI,CAAC,SAAS,KAAK,KAAK,IAAI,CAAC,CAAC;AAC7E,QAAM,UAAUA,QAAO,MAAM,KAAK,CAAC,GAAG,MAAM;AAC1C,UAAM,OAAO,QAAQ,CAAC;AACtB,WAAO,QAAQ,QAAQ,CAAC,KAAK,WAAWA,QAAO,MAAM;AAAA,EACvD,CAAC;AACD,MAAI,SAAS;AACX,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,IAAI;AAAA,MACR,GAAG,QAAQ,IAAI;AAAA,IACjB;AAAA,EACF;AACA,MAAI,UAAU;AACd,aAAW,CAAC,GAAG,IAAI,KAAKA,QAAO,MAAM,QAAQ,GAAG;AAC9C,QAAI,WAAW,QAAQ,CAAC,MAAM,KAAK,SAAS;AAC1C,YAAM,MAAM,QAAQ,KAAK,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACnD,YAAM,YAAY,KAAK,MAAM,KAAK,SAAS,EAAE,MAAM,IAAM,CAAC;AAC1D,gBAAU;AAAA,IACZ,WAAW,CAAC,WAAW,QAAQ,CAAC,KAAK,MAAM;AACzC,YAAM,GAAG,KAAK,MAAM,EAAE,OAAO,KAAK,CAAC;AACnC,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,MAAI,CAAC,WAAW,SAAS;AACvB,eAAW,OAAOA,QAAO,KAAM,OAAM,MAAM,GAAG,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAChE;AACA,SAAO;AACT;;;AD9BO,IAAM,uBAAuB;AAE7B,IAAM,yBAAyB,GAAG,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAY7D,IAAM,uBAAuB,GAAG,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASpD,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBhB,SAAS,mBAAmB,SAA8B;AAC/D,SAAO,YAAY,SACf,GAAG,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAc3B,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBV,GAAG,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkB3B,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiEhB;AAEO,IAAM,uBAAuB,mBAAmB,MAAM;AAEtD,SAAS,gBAAgB,gBAAgC;AAC9D,SAAO,KAAK,QAAQ,cAAc,GAAG,WAAW,WAAW,WAAW;AACxE;AAEA,SAAS,OAAO,gBAAwB,UAAuB,QAAuB;AACpF,QAAM,MAAM,gBAAgB,cAAc;AAC1C,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,OAAO;AAAA,MACL;AAAA,QACE,MAAM,KAAK,KAAK,aAAa;AAAA,QAC7B,SAAS,YAAY,SAAS,uBAAuB;AAAA,MACvD;AAAA,MACA,EAAE,MAAM,KAAK,KAAK,aAAa,GAAG,SAAS,mBAAmB,OAAO,EAAE;AAAA,IACzE;AAAA,IACA,MAAM,CAAC,KAAKC,SAAQ,GAAG,GAAGA,SAAQA,SAAQ,GAAG,CAAC,CAAC;AAAA,EACjD;AACF;AAEO,SAAS,sBAAsB,gBAA0C;AAC9E,SAAO,uBAAuB,OAAO,cAAc,CAAC;AACtD;AAEO,SAAS,gBACd,gBACA,SACA,UAAuB,QACL;AAClB,SAAO,iBAAiB,OAAO,gBAAgB,OAAO,GAAG,OAAO;AAClE;AAGA,eAAsB,oBAAoB,gBAAqD;AAC7F,MAAI,CAAE,MAAM,sBAAsB,cAAc,EAAI,QAAO;AAC3D,QAAM,SAAS,MAAMC,UAAS,KAAK,gBAAgB,cAAc,GAAG,aAAa,GAAG,MAAM;AAC1F,SAAO,OAAO,WAAW,GAAG,oBAAoB,UAAU,IAAI,SAAS;AACzE;;;AEnOA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,WAAAC,UAAS,QAAAC,OAAM,WAAAC,gBAAe;AAuBhC,IAAM,yBAAyB;AAEtC,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuDlB,SAAS,qBAAqB,SAA8B;AACjE,SAAO,YAAY,SACf,GAAG,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA8BZ,GAAG,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO7B,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiElB;AAEO,IAAM,yBAAyB,qBAAqB,MAAM;AAE1D,SAAS,mBAAmB,gBAAgC;AACjE,SAAOC,MAAKC,SAAQ,cAAc,GAAG,aAAa,WAAW,cAAc;AAC7E;AAEA,SAASC,QAAO,gBAAwB,UAAuB,QAAuB;AACpF,QAAM,OAAO,mBAAmB,cAAc;AAC9C,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,OAAO,CAAC,EAAE,MAAM,SAAS,qBAAqB,OAAO,EAAE,CAAC;AAAA,IACxD,MAAM,CAACC,SAAQ,IAAI,GAAGA,SAAQA,SAAQ,IAAI,CAAC,CAAC;AAAA,EAC9C;AACF;AAEO,SAAS,wBAAwB,gBAA0C;AAChF,SAAO,uBAAuBD,QAAO,cAAc,CAAC;AACtD;AAGO,SAAS,kBACd,gBACA,SACA,UAAuB,QACL;AAClB,SAAO,iBAAiBA,QAAO,gBAAgB,OAAO,GAAG,OAAO;AAClE;AAGA,eAAsB,sBAAsB,gBAAqD;AAC/F,MAAI,CAAE,MAAM,wBAAwB,cAAc,EAAI,QAAO;AAC7D,QAAM,SAAS,MAAME,UAAS,mBAAmB,cAAc,GAAG,MAAM;AACxE,SAAO,OAAO,WAAW,GAAG,sBAAsB,UAAU,IAAI,SAAS;AAC3E;;;AClOA,SAAS,SAAAC,QAAO,YAAAC,iBAAgB;AAChC,SAAS,WAAAC,UAAS,QAAAC,OAAM,WAAAC,gBAAe;AAmBhC,IAAM,sBAAsB;AASnC,IAAM,iBAA0B;AAAA,EAC9B,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS,GAAG,mBAAmB;AACjC;AAEA,IAAM,YAAuB;AAAA,EAC3B,EAAE,OAAO,oBAAoB,SAAS,GAAG,mBAAmB,WAAW;AAAA,EACvE;AAAA,IACE,OAAO;AAAA,IACP,SAAS;AAAA,IACT,SAAS,GAAG,mBAAmB;AAAA,EACjC;AAAA,EACA,EAAE,OAAO,QAAQ,SAAS,GAAG,mBAAmB,WAAW;AAAA;AAAA;AAAA,EAG3D,EAAE,OAAO,gBAAgB,SAAS,iBAAiB,SAAS,GAAG,mBAAmB,SAAS;AAAA,EAC3F;AACF;AAGA,IAAM,iBAA4B;AAAA,EAChC,EAAE,OAAO,oBAAoB,SAAS,GAAG,mBAAmB,QAAQ;AAAA,EACpE,EAAE,OAAO,gBAAgB,SAAS,iBAAiB,SAAS,GAAG,mBAAmB,SAAS;AAAA,EAC3F;AACF;AAIO,SAAS,mBAAmB,iBAAiB,QAAQ,IAAI,GAAW;AACzE,SAAOC,MAAKC,SAAQ,cAAc,GAAG,WAAW,eAAe;AACjE;AAGO,SAAS,YACd,UACA,UAAuB,QACuB;AAC9C,QAAM,OAAO,YAAY,SAAS,iBAAiB;AACnD,QAAM,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC;AACrD,QAAM,OAAqB,EAAE,GAAG,UAAU,OAAO,EAAE,GAAI,SAAS,SAAS,CAAC,EAAG,EAAE;AAC/E,QAAM,QAAQ,KAAK;AACnB,MAAI,UAAU;AAGd,aAAW,SAAS,OAAO,KAAK,KAAK,GAAG;AACtC,UAAM,SAAsB,CAAC;AAC7B,eAAW,SAAS,MAAM,KAAK,KAAK,CAAC,GAAG;AACtC,YAAM,QAAQ,MAAM,SAAS,CAAC,GAAG;AAAA,QAC/B,CAAC,SAAS,CAAC,KAAK,SAAS,WAAW,mBAAmB,KAAK,OAAO,IAAI,KAAK,OAAO;AAAA,MACrF;AACA,UAAI,KAAK,YAAY,MAAM,SAAS,CAAC,GAAG,OAAQ,WAAU;AAC1D,UAAI,KAAK,SAAS,EAAG,QAAO,KAAK,EAAE,GAAG,OAAO,OAAO,KAAK,CAAC;AAAA,IAC5D;AACA,QAAI,OAAO,SAAS,EAAG,OAAM,KAAK,IAAI;AAAA,QACjC,QAAO,MAAM,KAAK;AAAA,EACzB;AAEA,aAAW,OAAO,MAAM;AACtB,UAAM,SAAS,CAAC,GAAI,MAAM,IAAI,KAAK,KAAK,CAAC,CAAE;AAC3C,UAAM,gBAAgB,OAAO;AAAA,MAAU,CAAC,UACtC,MAAM,OAAO,KAAK,CAAC,SAAS,KAAK,YAAY,IAAI,OAAO;AAAA,IAC1D;AACA,QAAI,kBAAkB,IAAI;AACxB,aAAO,KAAK;AAAA,QACV,GAAI,IAAI,UAAU,EAAE,SAAS,IAAI,QAAQ,IAAI,CAAC;AAAA,QAC9C,OAAO,CAAC,EAAE,MAAM,WAAW,SAAS,IAAI,QAAQ,CAAC;AAAA,MACnD,CAAC;AACD,gBAAU;AAAA,IACZ,OAAO;AAIL,YAAM,WAAW,OAAO,aAAa;AACrC,UAAI,aAAa,IAAI,WAAW,aAAgB,SAAS,WAAW,SAAY;AAC9E,cAAM,EAAE,SAAS,aAAa,GAAG,KAAK,IAAI;AAC1C,eAAO,aAAa,IAAI,IAAI,UAAU,EAAE,GAAG,MAAM,SAAS,IAAI,QAAQ,IAAI;AAC1E,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,UAAM,IAAI,KAAK,IAAI;AAAA,EACrB;AAEA,SAAO,EAAE,UAAU,MAAM,QAAQ;AACnC;AAGO,SAAS,YAAY,UAAsE;AAChG,MAAI,CAAC,SAAS,MAAO,QAAO,EAAE,UAAU,SAAS,MAAM;AACvD,QAAM,OAAqB,EAAE,GAAG,UAAU,OAAO,EAAE,GAAG,SAAS,MAAM,EAAE;AACvE,QAAM,QAAQ,KAAK;AACnB,MAAI,UAAU;AAEd,aAAW,SAAS,OAAO,KAAK,KAAK,GAAG;AACtC,UAAM,aAA0B,CAAC;AACjC,eAAW,SAAS,MAAM,KAAK,KAAK,CAAC,GAAG;AAItC,YAAM,aAAa,MAAM,SAAS,CAAC,GAAG;AAAA,QACpC,CAAC,SAAS,CAAC,KAAK,SAAS,WAAW,mBAAmB;AAAA,MACzD;AACA,UAAI,UAAU,YAAY,MAAM,SAAS,CAAC,GAAG,OAAQ,WAAU;AAC/D,UAAI,UAAU,WAAW,EAAG;AAC5B,iBAAW,KAAK,EAAE,GAAG,OAAO,OAAO,UAAU,CAAC;AAAA,IAChD;AACA,QAAI,WAAW,WAAW,EAAG,QAAO,MAAM,KAAK;AAAA,QAC1C,OAAM,KAAK,IAAI;AAAA,EACtB;AAEA,SAAO,EAAE,UAAU,MAAM,QAAQ;AACnC;AAEO,SAAS,eAAe,UAAiC;AAC9D,QAAM,QAAQ,SAAS,SAAS,CAAC;AACjC,SAAO,OAAO,OAAO,KAAK,EAAE;AAAA,IAAK,CAAC,WAChC,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW,mBAAmB,CAAC,CAAC;AAAA,EAC7F;AACF;AAGO,SAAS,aAAa,UAA4C;AACvE,MAAI,CAAC,eAAe,QAAQ,EAAG,QAAO;AACtC,QAAM,cAAc,GAAG,mBAAmB;AAC1C,QAAM,OAAO,OAAO,OAAO,SAAS,SAAS,CAAC,CAAC,EAAE;AAAA,IAAK,CAAC,WACrD,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,KAAK,CAAC,SAAS,KAAK,YAAY,WAAW,CAAC;AAAA,EAClF;AACA,SAAO,OAAO,SAAS;AACzB;AAEA,eAAsB,aAAa,MAAqC;AACtE,MAAI;AACF,WAAO,KAAK,MAAM,MAAMC,UAAS,MAAM,MAAM,CAAC;AAAA,EAChD,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU,QAAO,CAAC;AAChE,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,cAAc,MAAc,UAAuC;AACvF,QAAMC,OAAMC,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAE9C,QAAM,YAAY,MAAM,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AACnF;;;AC3IA,IAAM,WASF;AAAA,EACF,mBAAmB;AAAA,IACjB,MAAM;AAAA,IACN,WAAW,OAAO,SAAS,eAAe,MAAM,aAAa,mBAAmB,IAAI,CAAC,CAAC;AAAA,IACtF,SAAS,OAAO,SAAS,aAAa,MAAM,aAAa,mBAAmB,IAAI,CAAC,CAAC;AAAA,IAClF,KAAK,OAAO,MAAM,SAAS,YAAY;AACrC,YAAM,OAAO,mBAAmB,IAAI;AACpC,YAAM,UAAU,MAAM,aAAa,IAAI;AACvC,YAAM,EAAE,UAAU,QAAQ,IAAI,UAAU,YAAY,SAAS,OAAO,IAAI,YAAY,OAAO;AAC3F,UAAI,QAAS,OAAM,cAAc,MAAM,QAAQ;AAC/C,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,mBAAmB;AAAA,IACjB,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS;AAAA,IACT,KAAK;AAAA,EACP;AAAA,EACA,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS;AAAA,IACT,KAAK;AAAA,EACP;AACF;AAGA,eAAsB,eACpB,MACA,QACA,SACA,UAAuB,QAC+B;AACtD,QAAM,UAAU,gBAAgB,MAAM,EAAE;AACxC,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,EAAE,MAAM,IAAI,IAAI,SAAS,OAAO;AACtC,SAAO,EAAE,QAAQ,KAAK,IAAI,GAAG,SAAS,MAAM,IAAI,MAAM,SAAS,OAAO,EAAE;AAC1E;AAGA,eAAsB,mBACpB,MACA,QACyC;AACzC,QAAM,UAAU,gBAAgB,MAAM,EAAE;AACxC,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,SAAS,OAAO,EAAE,QAAQ,IAAI;AACvC;AAGA,eAAsB,qBACpB,MACA,QACyB;AACzB,QAAM,UAAU,gBAAgB,MAAM,EAAE;AACxC,SAAO,UAAU,SAAS,OAAO,EAAE,UAAU,IAAI,IAAI;AACvD;AAQA,eAAsB,oBAAoB,MAAc,QAAuC;AAC7F,QAAM,OAAO,gBAAgB,MAAM,EAAE;AACrC,QAAM,SAAqB,CAAC;AAC5B,aAAW,MAAM,YAAY;AAC3B,QACE,OAAO,UACP,gBAAgB,EAAE,EAAE,cAAc,QACjC,MAAM,qBAAqB,MAAM,EAAE;AAEpC,aAAO,KAAK,EAAE;AAAA,EAClB;AACA,SAAO;AACT;AAGA,eAAsB,gBAAgB,MAAc,QAAoC;AACtF,SACG,MAAM,qBAAqB,MAAM,MAAM,MAAO,SAC9C,MAAM,oBAAoB,MAAM,MAAM,GAAG,SAAS;AAEvD;;;AC/HA,SAAS,QAAQ,SAAAC,QAAO,YAAAC,WAAU,MAAAC,WAAU;AAC5C,SAAS,WAAAC,UAAS,QAAAC,OAAM,WAAAC,gBAAe;AAqBvC,eAAsB,sBACpB,SAC8C;AAC9C,QAAM,OAAOC,MAAK,QAAQ,gBAAgB,WAAW;AACrD,QAAM,UAAU,MAAM,qBAAqB,IAAI;AAC/C,UAAQ,eAAe,CAAC;AACxB,UAAQ,WAAW,aAAa,QAAQ,SAAS,EAAE,IAAI;AAAA,IACrD,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACA,MAAI,QAAQ,UAAU;AACpB,YAAQ,WAAW,WAAW;AAAA,MAC5B,SAAS;AAAA,MACT,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACAC,SAAQ,QAAQ,gBAAgB,QAAQ,aAAa,yBAAyB;AAAA,MAChF;AAAA,MACA,KAAK,EAAE,wBAAwB,QAAQ,eAAe;AAAA,IACxD;AAAA,EACF,WAAW,QAAQ,WAAW,UAAU;AACtC,WAAO,QAAQ,WAAW;AAAA,EAC5B;AACA,QAAM,YAAY,MAAM,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAChF,SAAO,EAAE,MAAM,SAAS,OAAO,KAAK,QAAQ,UAAU,EAAE,KAAK,EAAE;AACjE;AASA,IAAM,kBAAkB;AAEjB,SAAS,mBAAmB,gBAAwB,WAAqC;AAC9F,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM,CAAC,OAAO;AAAA,IACd,KAAK;AAAA,MACH,wBAAwB;AAAA,MACxB,qBAAqB;AAAA,MACrB,wBAAwB;AAAA,IAC1B;AAAA,EACF;AACF;AAMA,eAAsB,eACpB,MACA,MACA,QACiB;AACjB,QAAM,OAAOD,MAAK,MAAM,IAAI;AAC5B,MAAI,WAAW,cAAe,MAAM,OAAO,IAAI,EAAI,QAAO;AAC1D,SAAQ,MAAM,OAAO,GAAG,IAAI,GAAG,IAAK,GAAG,IAAI,MAAM;AACnD;AAEA,eAAe,OAAO,MAAgC;AACpD,SAAO,OAAO,IAAI,EAAE;AAAA,IAClB,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACF;AAEO,SAAS,YAAY,QAAyB,QAAmC;AACtF,MAAI,WAAW,aAAc,QAAO;AACpC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,CAAC,OAAO,SAAS,GAAG,OAAO,IAAI;AAAA,IACxC,GAAI,OAAO,MAAM,EAAE,aAAa,OAAO,IAAI,IAAI,CAAC;AAAA,IAChD,SAAS;AAAA,EACX;AACF;AAEA,SAAS,WAAW,QAA+C;AACjE,SAAO,WAAW,aAAa,QAAQ;AACzC;AAMA,eAAsB,gBACpB,MACA,QACA,MACA,QACyD;AACzD,QAAM,UAAU,MAAM,OAAO,IAAI;AACjC,QAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW,IAAI;AAClD,QAAM,MAAM,WAAW,MAAM;AAC7B,QAAME,WAAU,EAAE,GAAK,OAAO,GAAG,KAA6C,CAAC,EAAG;AAClF,QAAM,QAAQ,YAAY,QAAQ,MAAM;AACxC,MAAI,KAAK,UAAUA,SAAQ,IAAI,CAAC,MAAM,KAAK,UAAU,KAAK,EAAG,QAAO;AACpE,MAAI,SAAU,QAAO;AACrB,EAAAA,SAAQ,IAAI,IAAI;AAChB,QAAM,OACJ,WAAW,cAAc,CAAC,UACtB,EAAE,SAAS,iBAAiB,GAAG,QAAQ,CAAC,GAAG,GAAGA,SAAQ,IACtD,EAAE,GAAG,QAAQ,CAAC,GAAG,GAAGA,SAAQ;AAClC,QAAMC,OAAMC,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAM,YAAY,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAC7E,SAAO,UAAU,YAAY;AAC/B;AAGA,eAAsB,gBACpB,MACA,QACA,MAC6C;AAC7C,QAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW,IAAI;AAClD,QAAM,MAAM,WAAW,MAAM;AAC7B,QAAMF,WAAU,EAAE,GAAK,OAAO,GAAG,KAA6C,CAAC,EAAG;AAClF,MAAI,EAAE,QAAQA,UAAU,QAAO;AAC/B,MAAI,SAAU,QAAO;AACrB,SAAOA,SAAQ,IAAI;AACnB,QAAM,OAAO,EAAE,GAAG,QAAQ,CAAC,GAAG,GAAGA,SAAQ;AACzC,QAAM,WAAW,OAAO,KAAK,IAAI,EAAE,OAAO,CAAC,MAAM,MAAM,OAAO,MAAM,SAAS;AAC7E,MAAI,OAAO,KAAKA,QAAO,EAAE,WAAW,KAAK,SAAS,WAAW,EAAG,OAAMG,IAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,MACzF,OAAM,YAAY,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAClF,SAAO;AACT;AAEA,eAAsB,aACpB,MACA,QACA,MACkB;AAClB,QAAM,EAAE,OAAO,IAAI,MAAM,WAAW,IAAI;AACxC,SAAO,SAAU,OAAO,WAAW,MAAM,CAAC,KAA6C,CAAC;AAC1F;AAOO,SAAS,WAAW,KAAoD;AAC7E,MAAI,WAAW;AACf,QAAM,OAAO,IACV,QAAQ,kDAAkD,CAAC,UAAU;AACpE,QAAI,MAAM,WAAW,GAAG,EAAG,QAAO;AAClC,eAAW;AACX,WAAO;AAAA,EACT,CAAC,EACA,QAAQ,kCAAkC,CAAC,OAAO,SAA6B,QAAQ,KAAK;AAC/F,SAAO,EAAE,OAAO,KAAK,MAAM,IAAI,GAAG,SAAS;AAC7C;AAEA,eAAe,WACb,MACiE;AACjE,MAAI;AACF,UAAM,EAAE,OAAO,SAAS,IAAI,WAAW,MAAMC,UAAS,MAAM,MAAM,CAAC;AACnE,UAAM,SAAS,SAAS,OAAO,UAAU,WAAY,QAAoC,CAAC;AAC1F,WAAO,EAAE,QAAQ,SAAS;AAAA,EAC5B,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU,QAAO,EAAE,QAAQ,CAAC,GAAG,UAAU,MAAM;AAC7F,UAAM,IAAI,MAAM,kBAAkB,IAAI,KAAM,MAAgB,OAAO,EAAE;AAAA,EACvE;AACF;AAEA,eAAe,qBAAqB,MAAyC;AAC3E,UAAQ,MAAM,WAAW,IAAI,GAAG;AAClC;;;AChMA,SAAS,YAAAC,WAAU,MAAAC,WAAU;AAC7B,SAAS,QAAAC,OAAM,WAAAC,gBAAe;;;ACwBvB,IAAM,mBAAmB;AAIhC,IAAM,QAAQ;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,cAAc;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,cAAc;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,SAAS;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,cAAc;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,cAAc;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,OAAO;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,WAAW,MAAmD;AACrE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAI,SAAS,UAAU,cAAc;AAAA,IACrC,GAAG;AAAA,IACH,GAAI,SAAS,UAAU,cAAc;AAAA,IACrC,GAAG;AAAA,EACL,EAAE,KAAK,IAAI;AACb;AAGA,SAAS,aAAa,MAAsB;AAC1C,SAAO,WAAW,OAAO,CAAC,OAAO,gBAAgB,EAAE,EAAE,cAAc,IAAI,EACpE,IAAI,CAAC,OAAO,gBAAgB,EAAE,EAAE,IAAI,EACpC,KAAK,IAAI;AACd;AAMO,SAAS,uBACd,QACA,OAA4C,SACpC;AACR,QAAM,OAAO,gBAAgB,MAAM,EAAE;AACrC,SAAO;AAAA,IACL,yBAAyB,aAAa,IAAI,CAAC;AAAA,IAC3C;AAAA,IACA;AAAA,IACA,kBAAkB,IAAI;AAAA,IACtB,sBAAsB,IAAI,OAAO,YAAY,IAAI,CAAC;AAAA,IAClD;AAAA,IACA,WAAW,IAAI;AAAA,EACjB,EAAE,KAAK,IAAI;AACb;AAGO,SAAS,kBAAkB,OAA+B;AAC/D,SAAO,MAAM,MAAM,kCAAkC,IAAI,CAAC,MAAM,UAAU,UAAU;AACtF;AAEO,SAAS,iBAAiB,QAAgC;AAC/D,SAAO,gBAAgB,MAAM,EAAE;AACjC;AAEO,SAAS,kBAAkB,QAAgC;AAChE,SAAO,gBAAgB,MAAM,EAAE;AACjC;;;ADrKA,IAAM,eAAe;AACrB,IAAM,aAAa;AAqBZ,SAAS,mBACd,QACA,OAA4C,SACpC;AACR,SAAO,GAAG,YAAY;AAAA,EAAK,uBAAuB,QAAQ,IAAI,CAAC;AAAA,EAAK,UAAU;AAAA;AAChF;AAEA,eAAsB,qBAAqB,SAAsD;AAC/F,QAAM,cAAcC,SAAQ,QAAQ,eAAe,QAAQ,IAAI,CAAC;AAChE,QAAM,OAAO,QAAQ,cAAcC,MAAK,aAAa,iBAAiB,QAAQ,MAAM,CAAC;AAErF,QAAM,WAAW,MAAM,cAAc,IAAI;AACzC,QAAM,OAAO,QAAQ,QAAS,MAAM,iBAAiB,QAAQ,QAAQ,WAAW;AAChF,MAAI,QAAQ,kBAAkB,aAAa,QAAQ,CAAC,gBAAgB,QAAQ,IAAI;AAC9E,WAAO,EAAE,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,YAAY;AAAA,EAC7D;AACA,MAAI,SAAS,YAAY;AACvB,UAAMC,WAAU,aAAa,OAAO,OAAO,YAAY,QAAQ;AAC/D,QAAIA,aAAY,QAAQA,aAAY,UAAU;AAC5C,aAAO,EAAE,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,YAAY;AAAA,IAC7D;AAEA,QAAIA,SAAQ,KAAK,MAAM,GAAI,OAAMC,IAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,QACpD,OAAM,YAAY,MAAMD,UAAS,EAAE,MAAM,IAAM,CAAC;AACrD,WAAO,EAAE,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,UAAU;AAAA,EAC3D;AACA,QAAM,EAAE,SAAS,OAAO,IAAI,YAAY,UAAU,mBAAmB,QAAQ,QAAQ,IAAI,CAAC;AAE1F,MAAI,WAAW,YAAa,QAAO,EAAE,MAAM,QAAQ,QAAQ,QAAQ,OAAO;AAE1E,QAAM,YAAY,MAAM,SAAS,EAAE,MAAM,IAAM,CAAC;AAChD,SAAO,EAAE,MAAM,QAAQ,QAAQ,QAAQ,OAAO;AAChD;AAGO,SAAS,YACd,UACA,OAC4C;AAC5C,MAAI,aAAa,KAAM,QAAO,EAAE,SAAS,OAAO,QAAQ,UAAU;AAElE,QAAM,QAAQ,SAAS,QAAQ,YAAY;AAC3C,QAAM,MAAM,SAAS,QAAQ,UAAU;AACvC,MAAI,UAAU,MAAM,QAAQ,MAAM,MAAM,OAAO;AAC7C,UAAM,SAAS,SAAS,MAAM,GAAG,KAAK;AACtC,UAAM,QAAQ,SAAS,MAAM,MAAM,WAAW,MAAM,EAAE,QAAQ,OAAO,EAAE;AACvE,UAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK;AAC1C,WAAO,EAAE,SAAS,UAAU,QAAQ,aAAa,WAAW,cAAc,UAAU;AAAA,EACtF;AAGA,QAAM,YACJ,SAAS,WAAW,KAAK,SAAS,SAAS,MAAM,IAC7C,KACA,SAAS,SAAS,IAAI,IACpB,OACA;AACR,SAAO,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,IAAI,QAAQ,QAAQ;AACvE;AASA,eAAsB,iBACpB,QACA,cAAc,QAAQ,IAAI,GACE;AAC5B,SAAQ,MAAM,gBAAgB,aAAa,MAAM,IAAK,UAAU;AAClE;AAGA,eAAsB,gBAAgB,MAA8C;AAClF,QAAM,WAAW,MAAM,cAAc,IAAI;AACzC,MAAI,aAAa,QAAQ,CAAC,gBAAgB,QAAQ,EAAG,QAAO;AAC5D,SAAO,kBAAkB,SAAS,MAAM,SAAS,QAAQ,YAAY,CAAC,CAAC;AACzE;AAEA,SAAS,gBAAgB,SAA0B;AACjD,QAAM,QAAQ,QAAQ,QAAQ,YAAY;AAC1C,SAAO,UAAU,MAAM,QAAQ,QAAQ,UAAU,IAAI;AACvD;AAGO,SAAS,YAAY,UAA0B;AACpD,MAAI,CAAC,gBAAgB,QAAQ,EAAG,QAAO;AACvC,QAAM,QAAQ,SAAS,QAAQ,YAAY;AAC3C,QAAM,MAAM,SAAS,QAAQ,UAAU,IAAI,WAAW;AACtD,QAAM,SAAS,SAAS,MAAM,GAAG,KAAK,EAAE,QAAQ,SAAS,IAAI;AAC7D,QAAM,QAAQ,SAAS,MAAM,GAAG,EAAE,QAAQ,OAAO,EAAE;AACnD,SAAO,GAAG,MAAM,GAAG,KAAK;AAC1B;AAEA,eAAe,cAAc,MAAsC;AACjE,MAAI;AACF,WAAO,MAAME,UAAS,MAAM,MAAM;AAAA,EACpC,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU,QAAO;AAC/D,UAAM;AAAA,EACR;AACF;;;AElJA,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,SAAAC,QAAO,YAAAC,iBAAgB;AAChC,SAAS,eAAe;AACxB,SAAS,WAAAC,UAAS,QAAAC,aAAY;;;ACH9B,SAAS,kBAAkB;AAC3B,SAAS,SAAAC,QAAO,YAAAC,iBAAgB;AAChC,SAAS,WAAAC,UAAS,QAAAC,OAAM,WAAAC,gBAAe;AACvC,SAAS,qBAAqB;AAavB,SAAS,wBAAwB,OAAO,QAAQ,IAAI,QAAQ,IAAY;AAC7E,MAAI,QAAQ,aAAa,UAAU;AACjC,WAAOC,MAAK,MAAM,WAAW,uBAAuB,UAAU,4BAA4B;AAAA,EAC5F;AACA,MAAI,QAAQ,aAAa,SAAS;AAChC,WAAOA,MAAK,QAAQ,IAAI,WAAW,MAAM,UAAU,4BAA4B;AAAA,EACjF;AACA,SAAOA,MAAK,MAAM,WAAW,UAAU,4BAA4B;AACrE;AAEA,eAAsB,iBAAiB,MAA4C;AACjF,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,MAAI;AACF,WAAO,KAAK,MAAM,MAAMC,UAAS,MAAM,MAAM,CAAC;AAAA,EAChD,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,4CAA4C,IAAI,KAAM,MAAgB,OAAO;AAAA,IAC/E;AAAA,EACF;AACF;AAEA,eAAsB,kBAAkB,MAAc,QAA4C;AAChG,QAAMC,OAAMC,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAG9C,QAAM,YAAY,MAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AACjF;AAgBO,SAAS,uBAA+B;AAC7C,QAAM,WAAW,QAAQ,KAAK,CAAC;AAC/B,MAAI,YAAY,WAAW,QAAQ,EAAG,QAAO;AAI7C,QAAM,OAAOA,SAAQ,cAAc,YAAY,GAAG,CAAC;AACnD,QAAM,aAAa;AAAA,IACjBC,SAAQ,MAAM,UAAU;AAAA;AAAA,IACxBA,SAAQ,MAAM,MAAM,OAAO,UAAU;AAAA;AAAA,IACrCA,SAAQ,MAAM,MAAM,UAAU;AAAA,EAChC;AACA,aAAW,aAAa,YAAY;AAClC,QAAI,WAAW,SAAS,EAAG,QAAO;AAAA,EACpC;AAGA,SAAO,YAAY,WAAW,CAAC,KAAK;AACtC;AAWO,SAAS,WAAW,SAAkD;AAC3E,QAAM,MAA8B;AAAA,IAClC,qBAAqB,QAAQ;AAAA,IAC7B,wBAAwBA,SAAQ,QAAQ,eAAe,QAAQ,IAAI,CAAC;AAAA,EACtE;AACA,MAAI,QAAQ,QAAS,KAAI,WAAW,QAAQ;AAC5C,MAAI,QAAQ,WAAY,KAAI,eAAe,QAAQ;AACnD,MAAI,QAAQ,YAAa,KAAI,eAAe,QAAQ;AACpD,SAAO;AAAA;AAAA;AAAA;AAAA,IAIL,SAAS,QAAQ;AAAA,IACjB,MAAM,CAAC,qBAAqB,GAAG,OAAO;AAAA,IACtC;AAAA,EACF;AACF;AAQA,eAAsB,mBAAmB,SAAsD;AAC7F,QAAM,OAAO,wBAAwB;AACrC,QAAM,SAAS,MAAM,iBAAiB,IAAI;AAC1C,SAAO,eAAe,CAAC;AACvB,QAAM,YAAY,QAAQ,cAAc,aAAa,QAAQ,SAAS;AACtE,QAAM,UAAU,OAAO,WAAW,SAAS,MAAM;AACjD,SAAO,WAAW,SAAS,IAAI,WAAW,OAAO;AACjD,QAAM,kBAAkB,MAAM,MAAM;AACpC,SAAO,EAAE,MAAM,WAAW,SAAS,CAAC,QAAQ;AAC9C;AAEA,eAAsB,kBACpB,WAC6C;AAC7C,QAAM,OAAO,wBAAwB;AACrC,QAAM,SAAS,MAAM,iBAAiB,IAAI;AAC1C,MAAI,CAAC,OAAO,cAAc,EAAE,aAAa,OAAO,aAAa;AAC3D,WAAO,EAAE,SAAS,OAAO,KAAK;AAAA,EAChC;AACA,SAAO,OAAO,WAAW,SAAS;AAClC,QAAM,kBAAkB,MAAM,MAAM;AACpC,SAAO,EAAE,SAAS,MAAM,KAAK;AAC/B;AAEA,eAAsB,oBAGnB;AACD,QAAM,OAAO,wBAAwB;AACrC,QAAM,SAAS,MAAM,iBAAiB,IAAI;AAC1C,QAAM,UAAU,OAAO,QAAQ,OAAO,cAAc,CAAC,CAAC,EACnD,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,eAAe,IAAI,WAAW,YAAY,CAAC,EACrE,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO;AAAA,IACtB;AAAA,IACA,WAAW,MAAM,KAAK,uBAAuB;AAAA,IAC7C,aAAa,MAAM,KAAK,gBAAgB;AAAA,EAC1C,EAAE;AACJ,SAAO,EAAE,MAAM,QAAQ;AACzB;;;AD5IO,SAAS,gBAAgB,OAAO,QAAQ,GAAW;AACxD,SAAOC,MAAK,MAAM,UAAU,aAAa;AAC3C;AAEA,eAAsB,gBAAgB,MAA+B;AACnE,MAAI,CAACC,YAAW,IAAI,EAAG,QAAO;AAC9B,SAAOC,UAAS,MAAM,MAAM;AAC9B;AAEA,eAAsB,iBAAiB,MAAc,SAAgC;AACnF,QAAMC,OAAMC,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAE9C,QAAM,YAAY,MAAM,SAAS,EAAE,MAAM,IAAM,CAAC;AAClD;AAkBA,eAAsB,kBACpB,SAC6B;AAC7B,QAAM,OAAO,gBAAgB;AAC7B,QAAM,YAAY,QAAQ,cAAc,aAAa,QAAQ,SAAS;AACtE,QAAM,UAAU,MAAM,gBAAgB,IAAI;AAC1C,QAAM,UAAU,eAAe,SAAS,SAAS;AACjD,QAAM,QAAQ,WAAW,OAAO;AAChC,MAAI,QAAQ;AACV,UAAM,MAAM,EAAE,GAAG,MAAM,KAAK,wBAAwB,QAAQ,YAAY;AAC1E,QAAM,OAAO,kBAAkB,SAAS,WAAW,KAAK;AACxD,MAAI,SAAS,QAAS,OAAM,iBAAiB,MAAM,IAAI;AACvD,SAAO,EAAE,MAAM,WAAW,SAAS,CAAC,SAAS,SAAS,SAAS,QAAQ;AACzE;AAEA,eAAsB,iBACpB,WAC6C;AAC7C,QAAM,OAAO,gBAAgB;AAC7B,QAAM,UAAU,MAAM,gBAAgB,IAAI;AAC1C,QAAM,OAAO,kBAAkB,SAAS,SAAS;AACjD,QAAM,UAAU,SAAS;AAEzB,QAAM,UAAU,KAAK,QAAQ;AAC7B,MAAI,QAAS,OAAM,iBAAiB,MAAM,UAAU,GAAG,OAAO;AAAA,IAAO,EAAE;AACvE,SAAO,EAAE,SAAS,KAAK;AACzB;AAEA,eAAsB,mBAGnB;AACD,QAAM,OAAO,gBAAgB;AAC7B,QAAM,SAAS,MAAM,gBAAgB,IAAI;AACzC,QAAM,UAAU,MAAM,KAAK,OAAO,SAAS,mCAAmC,CAAC,EAC5E,QAAQ,CAAC,UAAW,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAE,EAC/C,OAAO,CAAC,QAAQ,QAAQ,eAAe,IAAI,WAAW,YAAY,CAAC,EACnE,IAAI,CAAC,QAAQ;AACZ,UAAM,QAAQ,eAAe,QAAQ,GAAG;AACxC,WAAO;AAAA,MACL;AAAA,MACA,WAAW,aAAa,OAAO,qBAAqB;AAAA,MACpD,aAAa,aAAa,OAAO,cAAc;AAAA,IACjD;AAAA,EACF,CAAC;AACH,SAAO,EAAE,MAAM,QAAQ;AACzB;AAEA,SAAS,kBAAkB,QAAgB,WAAmB,OAAiC;AAC7F,QAAM,UAAU,kBAAkB,QAAQ,SAAS,EAAE,QAAQ;AAC7D,QAAM,QAAQ,kBAAkB,WAAW,KAAK;AAChD,SAAO,GAAG,OAAO,GAAG,UAAU,SAAS,EAAE,GAAG,KAAK;AAAA;AACnD;AAEA,SAAS,kBAAkB,QAAgB,WAA2B;AACpE,QAAM,UAAU,aAAa,SAAS;AACtC,QAAM,MAAM,OAAO;AACnB,QAAM,iBAAiB,OAAO,uBAAuB,OAAO,iCAAiC,GAAG;AAChG,QAAM,aAAa,OAAO,uBAAuB,OAAO,sCAAsC,GAAG;AACjG,SAAO,OACJ,QAAQ,IAAI,OAAO,gBAAgB,IAAI,GAAG,EAAE,EAC5C,QAAQ,IAAI,OAAO,YAAY,IAAI,GAAG,EAAE;AAC7C;AAEA,SAAS,eAAe,QAAgB,WAA2B;AACjE,QAAM,UAAU,aAAa,SAAS;AACtC,QAAM,MAAM,OAAO;AACnB,QAAM,UAAU,OAAO,uBAAuB,OAAO,kCAAkC,OAAO,sCAAsC,GAAG,qBAAqB,GAAG;AAC/J,QAAM,SAAS,OAAO,MAAM,IAAI,OAAO,SAAS,GAAG,CAAC,IAAI,CAAC,KAAK;AAC9D,QAAM,MACJ,OAAO;AAAA,IACL,IAAI;AAAA,MACF,OAAO,uBAAuB,OAAO,sCAAsC,GAAG;AAAA,MAC9E;AAAA,IACF;AAAA,EACF,IAAI,CAAC,KAAK;AACZ,SAAO,GAAG,MAAM;AAAA,EAAK,GAAG;AAC1B;AAEA,SAAS,eAAe,QAAgB,WAA4B;AAClE,SAAO,IAAI,OAAO,OAAO,uBAAuB,aAAa,SAAS,CAAC,WAAW,GAAG,EAAE;AAAA,IACrF;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,WAAmB,OAAiC;AAC7E,QAAM,QAAQ;AAAA,IACZ,gBAAgB,QAAQ,SAAS,CAAC;AAAA,IAClC,aAAa,WAAW,MAAM,OAAO,CAAC;AAAA,IACtC,UAAU,UAAU,MAAM,IAAI,CAAC;AAAA,EACjC;AACA,MAAI,MAAM,OAAO,OAAO,KAAK,MAAM,GAAG,EAAE,SAAS,GAAG;AAClD,UAAM,KAAK,IAAI,gBAAgB,QAAQ,SAAS,CAAC,OAAO;AACxD,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG,EAAE;AAAA,MAAK,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,MACvE,KAAK,cAAc,KAAK;AAAA,IAC1B,GAAG;AACD,YAAM,KAAK,GAAG,GAAG,MAAM,WAAW,KAAK,CAAC,EAAE;AAAA,IAC5C;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,aAAa,OAAe,KAA4B;AAC/D,SAAO,MAAM,MAAM,IAAI,OAAO,IAAI,aAAa,GAAG,CAAC,sBAAsB,GAAG,CAAC,IAAI,CAAC,KAAK;AACzF;AAEA,SAAS,QAAQ,KAAqB;AACpC,SAAO,IAAI,IAAI,WAAW,MAAM,MAAM,EAAE,WAAW,KAAK,KAAK,CAAC;AAChE;AAEA,SAAS,WAAW,OAAuB;AACzC,SAAO,KAAK,UAAU,KAAK;AAC7B;AAEA,SAAS,UAAU,QAA0B;AAC3C,SAAO,IAAI,OAAO,IAAI,UAAU,EAAE,KAAK,IAAI,CAAC;AAC9C;AAEA,SAAS,aAAa,OAAuB;AAC3C,SAAO,MAAM,QAAQ,uBAAuB,MAAM;AACpD;;;AEhKA,SAAS,SAAAC,QAAO,YAAAC,iBAAgB;AAChC,SAAS,WAAAC,gBAAe;AACxB,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,yBAAyB;AAClC,SAAS,SAAS,WAAW,aAAa,qBAAqB;AAaxD,SAAS,iBAAiB,OAAOC,SAAQ,GAAW;AACzD,SAAOC,MAAK,QAAQ,IAAI,eAAeA,MAAK,MAAM,SAAS,GAAG,aAAa;AAC7E;AAIA,eAAe,QAAQ,MAA+B;AACpD,MAAI;AACF,WAAO,MAAMC,UAAS,MAAM,MAAM;AAAA,EACpC,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU,QAAO;AAC/D,UAAM;AAAA,EACR;AACF;AAGA,SAAS,YAAY,KAA4B;AAC/C,MAAI;AACF,UAAM,QAAQ,UAAU,GAAG,KAAK,CAAC;AACjC,WAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAK,QAAmB;AAAA,EAC3F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,QAAQ,QAAyC;AACxD,QAAM,QAAQ,OAAO;AACrB,SAAO,SAAS,OAAO,UAAU,WAAY,QAAoC,CAAC;AACpF;AAEA,IAAM,SAAS,CAAC,SAAiB,KAAK,KAAK,MAAM,MAAM,CAAC,KAAK,KAAK,EAAE,WAAW,GAAG;AAClF,IAAM,WAAW,CAAC,SAAiB,KAAK,SAAS,KAAK,UAAU,EAAE;AAG3D,SAAS,eAAe,KAAa,MAAc,OAA+B;AACvF,QAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,QAAM,SAAS,MAAM,UAAU,CAAC,SAAS,0BAA0B,KAAK,IAAI,CAAC;AAC7E,QAAM,SAAS,CAACC,YACd,cAAc,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,EAC5B,QAAQ,EACR,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,GAAG,IAAI,OAAOA,OAAM,CAAC,GAAG,IAAI,EAAE;AAEjD,MAAI,WAAW,IAAI;AACjB,QAAI,UAAU,KAAM,QAAO;AAE3B,UAAM,OAAO,QAAQ,MAAM,IAAI,SAAS,IAAI,IAAI,MAAM,GAAG,GAAG;AAAA;AAC5D,WAAO,GAAG,IAAI;AAAA,EAAiB,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA,EACrD;AAEA,MAAI,MAAM,SAAS;AACnB,SAAO,MAAM,MAAM,UAAU,CAAC,MAAM,KAAK,MAAM,GAAG,KAAK,EAAE,EAAG;AAC5D,QAAM,QAAQ,MAAM,KAAK,EAAE,QAAQ,MAAM,SAAS,EAAE,GAAG,CAAC,GAAG,MAAM,SAAS,IAAI,CAAC;AAC/E,QAAM,QAAQ,MAAM,OAAO,CAAC,MAAM,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC;AACxD,QAAM,SAAS,MAAM,SAAS,IAAI,SAAS,MAAM,MAAM,CAAC,CAAW,KAAK,EAAE,IAAI;AAC9E,QAAM,SAAS,KAAK,QAAQ,uBAAuB,MAAM;AACzD,QAAM,UAAU,IAAI,OAAO,MAAM,MAAM,OAAO,MAAM,KAAK,MAAM,MAAM,MAAM,gBAAgB;AAC3F,QAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,QAAQ,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;AAE5D,MAAI,UAAU,QAAW;AACvB,QAAI,UAAU,KAAM,QAAO;AAC3B,UAAM,MAAM,MAAM,GAAG,EAAE,KAAK,UAAU;AACtC,UAAM,OAAO,IAAI,GAAG,GAAG,OAAO,MAAM,CAAC;AACrC,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AAEA,QAAM,OAAO,MAAM;AAAA,IACjB,CAAC,MAAM,IAAI,UAAU,MAAM,CAAC,KAAK,IAAI,KAAK,MAAM,MAAM,SAAS,MAAM,CAAC,KAAK,EAAE,KAAK;AAAA,EACpF;AACA,MAAI,OAAO,QAAQ;AACnB,SAAO,OAAO,QAAQ,MAAM,MAAM,OAAO,CAAC,KAAK,IAAI,KAAK,MAAM,GAAI;AAClE,QAAM,OAAO,OAAO,OAAO,OAAO,GAAI,UAAU,OAAO,CAAC,IAAI,OAAO,MAAM,CAAE;AAE3E,MAAI,UAAU,QAAQ,MAAM,SAAS,KAAK,MAAM,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,GAAG;AACpF,UAAM,OAAO,QAAQ,CAAC;AAAA,EACxB;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAKA,eAAsB,mBACpB,MACA,MACA,OAC0B;AAC1B,QAAM,MAAM,MAAM,QAAQ,IAAI;AAC9B,QAAM,SAAS,YAAY,GAAG;AAC9B,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,QAAQ,MAAM,EAAE,IAAI;AACpC,MAAI,UAAU,OAAO,YAAY,SAAY,kBAAkB,SAAS,KAAK,GAAG;AAC9E,WAAO;AAAA,EACT;AACA,QAAM,cAAc,EAAE,GAAG,QAAQ,MAAM,EAAE;AACzC,MAAI,UAAU,KAAM,QAAO,YAAY,IAAI;AAAA,MACtC,aAAY,IAAI,IAAI;AACzB,QAAM,EAAE,aAAa,GAAG,GAAG,KAAK,IAAI;AACpC,QAAM,WACJ,OAAO,KAAK,WAAW,EAAE,SAAS,IAAI,EAAE,GAAG,MAAM,aAAa,YAAY,IAAI;AAEhF,QAAM,OAAO,eAAe,KAAK,MAAM,KAAK;AAC5C,MAAI,CAAC,kBAAkB,YAAY,IAAI,GAAG,QAAQ,EAAG,QAAO;AAC5D,QAAMC,OAAMC,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAE9C,QAAM,YAAY,MAAM,MAAM,EAAE,MAAM,IAAM,CAAC;AAC7C,SAAO,UAAU,OAAO,YAAY,YAAY,SAAY,YAAY;AAC1E;AAEA,eAAsB,iBAAiB,MAAsC;AAC3E,SAAO,YAAY,MAAM,QAAQ,IAAI,CAAC;AACxC;AAEA,eAAsB,mBAAmB,MAAc,MAAgC;AACrF,QAAM,SAAS,MAAM,iBAAiB,IAAI;AAC1C,SAAO,WAAW,QAAQ,QAAQ,QAAQ,MAAM;AAClD;;;ACjHO,SAAS,cAAc,QAA2B;AACvD,SAAO,WAAW,UAAU,gBAAgB,IAAI,iBAAiB;AACnE;AAEA,eAAsB,aACpB,QACA,SACiB;AACjB,QAAM,EAAE,MAAM,MAAM,WAAW,QAAQ,IAAI;AAC3C,MAAI,WAAW,UAAU;AACvB,WAAO;AAAA,MACL,iBAAiB;AAAA,MACjB;AAAA,MACA,UAAU,mBAAmB,MAAM,SAAS,IAAI;AAAA,IAClD;AAAA,EACF;AACA,MAAI,CAAC,QAAS,SAAQ,MAAM,iBAAiB,IAAI,GAAG,UAAU,YAAY;AAC1E,QAAM,SAAS,MAAM,kBAAkB;AAAA,IACrC;AAAA,IACA,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AACD,SAAO,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY;AACnE;AAEA,eAAsB,aAAa,QAAmB,MAAgC;AACpF,MAAI,WAAW,SAAU,QAAO,mBAAmB,iBAAiB,GAAG,IAAI;AAC3E,UAAQ,MAAM,iBAAiB,GAAG,QAAQ,KAAK,CAAC,UAAU,MAAM,QAAQ,IAAI;AAC9E;;;AZGA,eAAsB,cACpB,SACuB;AACvB,QAAM,UAAU,gBAAgB,QAAQ,MAAM;AAC9C,MAAI,CAAC,eAAe,OAAO,EAAE,SAAS,QAAQ,IAAI,GAAG;AACnD,UAAM,IAAI;AAAA,MACR,GAAG,QAAQ,IAAI,qBAAqB,YAAY,QAAQ,IAAI,CAAC,UAAU,QAAQ,OAAO,oBAAoB;AAAA,IAC5G;AAAA,EACF;AACA,SAAO,MAAM,SAAS,QAAQ,MAAM,QAAQ,SAAS,MAAM;AAC7D;AAGA,eAAe,YAAY,MAAmC;AAC5D,QAAM,SAASC,MAAK,MAAM,cAAc,SAAS,YAAY;AAC7D,QAAM,UAAU,MAAMC,QAAO,MAAM,EAAE;AAAA,IACnC,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACA,MAAI,CAAC,QAAS,OAAM,gBAAgB,EAAE,gBAAgB,KAAK,CAAC;AAC5D,SAAO,EAAE,SAAS,SAAS,QAAQ,QAAQ,UAAU,cAAc,QAAQ;AAC7E;AAGA,eAAsB,gBAAgB,SAA+C;AACnF,SAAO,MAAM,SAAS,YAAY,KAAK;AACzC;AAEA,eAAe,MACb,SACA,MACA,SACuB;AACvB,QAAM,OAAOC,SAAQ,QAAQ,eAAe,QAAQ,IAAI,CAAC;AACzD,QAAM,UAAU,gBAAgB,QAAQ,MAAM;AAC9C,QAAM,QAAsB,CAAC;AAC7B,QAAM,QAAkB,CAAC;AAEzB,QAAM,YAAY,QAAQ,aAAc,MAAM,qBAAqB,IAAI;AACvE,QAAM,OAAO,aAAa,SAAS;AACnC,QAAM,SAAS,mBAAmB,QAAQ,MAAM;AAGhD,QAAM,eACJ,SAAS,cAAc,SAAS,SAAS,MAAM,iBAAiB,MAAM,QAAQ,SAAS,IAAI,CAAC;AAC9F,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,OAAOF,MAAK,MAAM,QAAQ,SAAS;AACzC,UAAM,KAAK,EAAE,SAAS,SAAS,QAAQ,MAAM,QAAQ,OAAO,CAAC;AAC7D,UAAM;AAAA,MACJ,+BAA+B,QAAQ,SAAS,mBAAmB,aAAa,IAAI,CAAC,OAAO,gBAAgB,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,IAClI;AAAA,EACF,OAAO;AAEL,UAAM,SAAS,SAAS,UAAU,MAAM,oBAAoB,MAAM,QAAQ,MAAM,IAAI,CAAC;AACrF,UAAM,QAAQ,MAAM,qBAAqB;AAAA,MACvC,QAAQ,QAAQ;AAAA,MAChB,aAAa;AAAA;AAAA,MAEb,MAAM,SAAS,SAAS,aAAa,OAAO,SAAS,IAAI,UAAU;AAAA,IACrE,CAAC;AACD,UAAM,KAAK,EAAE,SAAS,SAAS,QAAQ,MAAM,MAAM,QAAQ,MAAM,OAAO,CAAC;AACzE,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM;AAAA,QACJ,QAAQ,QAAQ,SAAS,iBAAiB,OAAO,IAAI,CAAC,OAAO,gBAAgB,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,MACnG;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM;AAAA,MACJ,GAAG,QAAQ,SAAS,oBAAoB,OAAO,IAAI,CAAC,OAAO,gBAAgB,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,IACjG;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM;AAAA,IAClB;AAAA,IACA,SAAS,WAAW,SAAS;AAAA,IAC7B,QAAQ;AAAA,IACR,SAAS,SAAS,SAAS;AAAA,EAC7B;AACA,MAAI,MAAO,OAAM,KAAK,KAAK;AAC3B,MAAI,SAAS,OAAQ,OAAM,KAAK,MAAM,YAAY,IAAI,CAAC;AAEvD,MAAI,UAAU,QAAQ,KAAK;AACzB,UAAM,OAAO,MAAM,eAAe,MAAM,QAAQ,IAAI,MAAM,QAAQ,IAAI,MAAM;AAC5E,UAAM,SAAS,UACX,MAAM,gBAAgB,MAAM,QAAQ,IAAI,QAAQ,MAAM,mBAAmB,MAAM,SAAS,CAAC,IACzF,MAAM,gBAAgB,MAAM,QAAQ,IAAI,QAAQ,IAAI;AACxD,UAAM,KAAK,EAAE,SAAS,OAAO,QAAQ,MAAM,OAAO,CAAC;AACnD,QAAI,WAAW,UAAU;AACvB,YAAM;AAAA,QACJ,GAAG,IAAI,6CAA6C,UAAU,QAAQ,QAAQ,SAAS,IAAI,8BAA8B,UAAU,KAAK,KAAK,UAAU,YAAY,QAAQ,IAAI,QAAQ,mBAAmB,MAAM,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE;AAAA,MACrO;AAAA,IACF;AAAA,EACF,WAAW,QAAQ,QAAQ;AACzB,UAAM,SAAS,cAAc,QAAQ,IAAI,MAAM;AAC/C,UAAM,SAAS,MAAM,aAAa,QAAQ,IAAI,QAAQ;AAAA,MACpD;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IACX,CAAC;AACD,UAAM,KAAK,EAAE,SAAS,OAAO,QAAQ,OAAO,CAAC;AAC7C,QAAI,WAAW,UAAU;AACvB,YAAM;AAAA,QACJ,GAAG,MAAM,iDAAiD,UAAU,QAAQ,QAAQ,SAAS,IAAI;AAAA,MACnG;AAAA,IACF;AAAA,EACF,OAAO;AACL,UAAM;AAAA,MACJ,yBAAyB,UAAU,QAAQ,QAAQ,0BAA0B,QAAQ,IAAI,MAAM;AAAA,IACjG;AAAA,EACF;AACA,MAAI,QAAQ,QAAQ,SAAS,WAAY,OAAM,KAAK,GAAG,QAAQ,IAAI,KAAK,QAAQ,IAAI,GAAG;AACvF,SAAO,EAAE,QAAQ,QAAQ,QAAQ,OAAO,MAAM;AAChD;AAEA,eAAe,iBACb,MACA,SACA,WACqB;AACrB,QAAM,OAAmB,CAAC;AAC1B,aAAW,MAAM,SAAS;AACxB,SAAK,MAAM,aAAa,IAAI,MAAM,SAAS,GAAG,IAAK,MAAK,KAAK,EAAE;AAAA,EACjE;AACA,SAAO;AACT;AAGA,eAAsB,SACpB,MACA,SACA,SAAmB,UACnB,UAAuB,QACK;AAC5B,QAAM,SAAS,MAAM,eAAe,MAAM,QAAQ,SAAS,OAAO;AAClE,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,OAAO,UAAW,UAAU,cAAc,YAAa;AACtE,SAAO,EAAE,SAAS,SAAS,QAAQ,OAAO,QAAQ,OAAO;AAC3D;AAcA,eAAsB,aACpB,QACA,cAAc,QAAQ,IAAI,GAC1B,WACuB;AACvB,QAAM,OAAOE,SAAQ,WAAW;AAChC,QAAM,UAAU,gBAAgB,MAAM;AACtC,QAAM,YAAY,MAAM,gBAAgBF,MAAK,MAAM,QAAQ,SAAS,CAAC;AACrE,QAAM,QAAQ,MAAM,qBAAqB,MAAM,MAAM;AACrD,QAAM,OAAO,aAAa,aAAc,MAAM,qBAAqB,IAAI,CAAE;AACzE,QAAM,MACJ,UAAU,QAAQ,MACd,MAAM;AAAA,IACJ,MAAM,eAAe,MAAM,QAAQ,IAAI,MAAM,QAAQ,IAAI,MAAM;AAAA,IAC/D,QAAQ,IAAI;AAAA,IACZ;AAAA,EACF,IACA,MAAM,aAAa,QAAQ,IAAI,QAAQ,IAAI,EAAE,MAAM,MAAM,KAAK;AAEpE,QAAM,WAAqB,CAAC;AAC5B,MAAI,SAAS,cAAc,SAAS;AAClC,aAAS;AAAA,MACP,2BAA2B,QAAQ,SAAS,sFAAsF,MAAM;AAAA,IAC1I;AAAA,EACF;AACA,MAAI,cAAc,WAAW,UAAU,OAAO;AAC5C,aAAS;AAAA,MACP,GAAG,QAAQ,SAAS;AAAA,IACtB;AAAA,EACF;AAEA,QAAM,iBAAiB,UAAU,QAAQ,cAAc,UAAU,UAAU;AAC3E,QAAM,OAA6B,QAC9B,MAAM,mBAAmB,MAAM,MAAM,MAAO,SAC3C,SACA,UACD,mBAAmB,MAAM,aAAa;AAC3C,SAAO,EAAE,QAAQ,MAAM,WAAW,OAAO,KAAK,SAAS;AACzD;","names":["access","join","resolve","readFile","dirname","plugin","dirname","readFile","readFile","dirname","join","resolve","join","resolve","plugin","dirname","readFile","mkdir","readFile","dirname","join","resolve","join","resolve","readFile","mkdir","dirname","mkdir","readFile","rm","dirname","join","resolve","join","resolve","servers","mkdir","dirname","rm","readFile","readFile","rm","join","resolve","resolve","join","content","rm","readFile","existsSync","mkdir","readFile","dirname","join","mkdir","readFile","dirname","join","resolve","join","readFile","mkdir","dirname","resolve","join","existsSync","readFile","mkdir","dirname","mkdir","readFile","homedir","dirname","join","homedir","join","readFile","indent","mkdir","dirname","join","access","resolve"]}