infra-kit 0.1.134 → 0.3.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.
- package/dist/{boot-YCABHUPP.js → boot-MTVHM4N7.js} +2 -2
- package/dist/{chunk-TFHVQOVF.js → chunk-4MRYIYJP.js} +2 -2
- package/dist/chunk-E2NWNNC6.js +2 -0
- package/dist/chunk-E2NWNNC6.js.map +7 -0
- package/dist/{chunk-3OMHS5RE.js → chunk-E2SEBLGJ.js} +2 -2
- package/dist/chunk-KZP2SY37.js +4 -0
- package/dist/chunk-KZP2SY37.js.map +7 -0
- package/dist/{chunk-27PDA2PN.js → chunk-NC2TIQ3M.js} +2 -2
- package/dist/chunk-RLZ5IB2E.js +238 -0
- package/dist/chunk-RLZ5IB2E.js.map +7 -0
- package/dist/chunk-TI33V5G2.js +6 -0
- package/dist/chunk-TI33V5G2.js.map +7 -0
- package/dist/{chunk-FAR2L74S.js → chunk-TPVXMJ7N.js} +1 -1
- package/dist/cli.js +10 -8
- package/dist/cli.js.map +4 -4
- package/dist/dev-server.js +23 -20
- package/dist/dev-server.js.map +4 -4
- package/dist/dev-wizard-run-5HQOGE3R.js +2 -0
- package/dist/dev-wizard-run-5HQOGE3R.js.map +7 -0
- package/dist/mcp.js +1 -1
- package/dist/mcp.js.map +3 -3
- package/dist/persistent-ink-dev-ui-KOI44ZVT.js +2 -0
- package/dist/persistent-ink-dev-ui-KOI44ZVT.js.map +7 -0
- package/dist/safe-stderr-HXDR2DNJ.js +2 -0
- package/dist/update-check.js +1 -1
- package/package.json +3 -14
- package/dist/chunk-2YHPK2VY.js +0 -221
- package/dist/chunk-2YHPK2VY.js.map +0 -7
- package/dist/chunk-3F7XYJWY.js +0 -3
- package/dist/chunk-3F7XYJWY.js.map +0 -7
- package/dist/chunk-DOG3OPNW.js +0 -2
- package/dist/chunk-DOG3OPNW.js.map +0 -7
- package/dist/chunk-JD5T4VCB.js +0 -6
- package/dist/chunk-JD5T4VCB.js.map +0 -7
- package/dist/chunk-LTKR3F4N.js +0 -2
- package/dist/chunk-LTKR3F4N.js.map +0 -7
- package/dist/chunk-TUVUKM4K.js +0 -2
- package/dist/chunk-TUVUKM4K.js.map +0 -7
- package/dist/chunk-Z6KTUVIC.js +0 -2
- package/dist/chunk-Z6KTUVIC.js.map +0 -7
- package/dist/dev-wizard-run-HGQO6FZV.js +0 -2
- package/dist/dev-wizard-run-HGQO6FZV.js.map +0 -7
- package/dist/entry/index.d.ts +0 -4
- package/dist/entry/vite.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/lib/package-config/package-config-schema.d.ts +0 -35
- package/dist/lib/package-config/package-config.d.ts +0 -99
- package/dist/lib/release-slug/release-slug.d.ts +0 -43
- package/dist/lib/vendor/config-schema.d.ts +0 -58
- package/dist/lib/vite/vite.d.ts +0 -168
- package/dist/persistent-ink-dev-ui-FUJQDFNY.js +0 -8
- package/dist/persistent-ink-dev-ui-FUJQDFNY.js.map +0 -7
- package/dist/vite.js +0 -2
- package/dist/vite.js.map +0 -7
- /package/dist/{boot-YCABHUPP.js.map → boot-MTVHM4N7.js.map} +0 -0
- /package/dist/{chunk-TFHVQOVF.js.map → chunk-4MRYIYJP.js.map} +0 -0
- /package/dist/{chunk-3OMHS5RE.js.map → chunk-E2SEBLGJ.js.map} +0 -0
- /package/dist/{chunk-27PDA2PN.js.map → chunk-NC2TIQ3M.js.map} +0 -0
- /package/dist/{chunk-FAR2L74S.js.map → chunk-TPVXMJ7N.js.map} +0 -0
- /package/dist/{index.js.map → safe-stderr-HXDR2DNJ.js.map} +0 -0
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/commands/audit/audit.ts", "../src/lib/package-validator/fs-utils.ts", "../src/lib/package-validator/package-validator.ts", "../src/lib/package-validator/loader/config-loader.ts", "../src/lib/package-validator/loader/package-discovery.ts", "../src/lib/package-validator/checks/config-check.ts", "../src/lib/package-validator/checks/files-check.ts", "../src/lib/package-validator/checks/scripts-check.ts", "../src/lib/package-validator/checks/turbo-check.ts", "../src/types.ts", "../src/commands/audit/preset-proxy-check.ts", "../src/commands/doctor/doctor.ts", "../src/commands/init/agent-files.ts", "../src/lib/managed-block/managed-block.ts", "../src/commands/init/init.ts", "../src/lib/config-bootstrap/config-bootstrap.ts", "../src/lib/config-templates/config-templates.ts", "../src/lib/path-display/path-display.ts", "../src/commands/init/migrate-config.ts", "../src/lib/vendor/factory-config.ts", "../src/lib/vendor/factory-config-schema.ts", "../src/integrations/doppler/doppler-cli-auth.ts", "../src/integrations/doppler/doppler-errors.ts", "../src/integrations/doppler/doppler-project.ts", "../src/lib/config-overrides/config-overrides.ts", "../src/lib/warm-cache/warm-cache.ts", "../src/commands/env-clear/env-clear.ts", "../src/commands/env-list/env-list.ts", "../src/commands/env-load/env-load.ts", "../src/lib/errors/operation-error.ts", "../src/commands/env-status/env-status.ts", "../src/commands/gh-merge-dev/gh-merge-dev.ts", "../src/integrations/gh/gh-cli-auth/gh-cli-auth.ts", "../src/integrations/gh/gh-release-prs/gh-release-prs.ts", "../src/lib/release-utils/release-utils.ts", "../src/integrations/jira/api.ts", "../src/lib/git-guard/git-guard.ts", "../src/lib/prompts/release-picker.ts", "../src/lib/errors/prompt-cancelled-error.ts", "../src/lib/json-output/json-output.ts", "../src/commands/gh-release-deliver/gh-release-deliver.ts", "../src/lib/errors/format-zx-error.ts", "../src/lib/worktrees/remove-worktrees.ts", "../src/commands/gh-release-deploy-all/gh-release-deploy-all.ts", "../src/commands/gh-release-deploy-selected/gh-release-deploy-selected.ts", "../src/commands/gh-release-list/gh-release-list.ts", "../src/commands/release-create/release-create.ts", "../src/lib/version-utils/load-existing-versions.ts", "../src/lib/version-utils/version-utils.ts", "../src/lib/version-utils/next-version.ts", "../src/commands/release-desc-edit/release-desc-edit.ts", "../src/commands/vendor-check/vendor-check.ts", "../src/lib/vendor/hash.ts", "../src/lib/vendor/manifest.ts", "../src/lib/vendor/walk.ts", "../src/lib/vendor/skip-sets.ts", "../src/commands/vendor-diff/vendor-diff.ts", "../src/lib/vendor/config.ts", "../src/lib/vendor/sync-ops.ts", "../src/commands/vendor-manifest/vendor-manifest.ts", "../src/commands/vendor-sync/vendor-sync.ts", "../src/commands/version/version.ts", "../src/commands/worktrees-add/worktrees-add.ts", "../src/integrations/cursor/add-folders-to-workspace.ts", "../src/integrations/cursor/launch-cursor.ts", "../src/integrations/cursor/reconcile-workspace-folders.ts", "../src/integrations/cursor/remove-folders-from-workspace.ts", "../src/integrations/cursor/resolve-workspace-path.ts", "../src/integrations/cursor/open-cursor-workspace.ts", "../src/integrations/zed/add-folders-to-zed-workspace.ts", "../src/integrations/zed/open-zed-workspace.ts", "../src/integrations/zed/reuse-zed-workspace.ts", "../src/lib/assert-never/assert-never.ts", "../src/integrations/ide/add-ide-worktree-folders.ts", "../src/integrations/ide/open-ide-workspace.ts", "../src/integrations/ide/provider-label.ts", "../src/integrations/ide/remove-ide-worktree-folders.ts", "../src/integrations/ide/types.ts", "../src/commands/worktrees-list/worktrees-list.ts", "../src/commands/worktrees-reload/worktrees-reload.ts", "../src/commands/worktrees-remove/worktrees-remove.ts", "../src/commands/worktrees-sync/worktrees-sync.ts", "../src/lib/command-catalog/command-catalog.ts"],
|
|
4
|
-
"sourcesContent": ["import path from 'node:path'\nimport process from 'node:process'\nimport { z } from 'zod'\n\nimport { getProjectRoot } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { ROOT_DEFAULT_RULES } from 'src/lib/package-config'\nimport type { ResolvedPackageRules } from 'src/lib/package-config'\nimport { discoverPackages, pathExists, validatePackage } from 'src/lib/package-validator'\nimport type { PackageValidationResult } from 'src/lib/package-validator'\nimport { defineMcpTool, textContent } from 'src/types'\n\nimport { checkDevPresets } from './preset-proxy-check'\n\n// TODO [DO]: extract `audit` into its own standalone CLI tool, decoupled from infra-kit.\n\ninterface AuditOptions {\n /** Audit every non-vendor workspace package instead of just the current one. */\n all?: boolean\n /** Audit the monorepo root (turbo pipeline + root commands) instead of a package. */\n root?: boolean\n /** Directory to resolve the current package from. Defaults to `process.cwd()`. */\n cwd?: string\n}\n\n/** A directory to audit plus the under-the-hood defaults that apply to it. */\ninterface AuditTarget {\n dir: string\n baseline?: Readonly<ResolvedPackageRules>\n}\n\n/**\n * Walk upward from `start` to the nearest directory containing a package.json.\n * Used so `infra-kit audit` (no `--all`) targets the package whose package.json\n * script invoked it, regardless of the exact working directory.\n *\n * @example\n * await findPackageRoot('/repo/packages/serverless-config/src')\n * // => '/repo/packages/serverless-config'\n */\nconst findPackageRoot = async (start: string): Promise<string> => {\n let current = path.resolve(start)\n\n while (current !== path.dirname(current)) {\n if (await pathExists(path.join(current, 'package.json'))) {\n return current\n }\n\n current = path.dirname(current)\n }\n\n if (await pathExists(path.join(current, 'package.json'))) {\n return current\n }\n\n throw new Error(`No package.json found in or above ${start}`)\n}\n\n/**\n * Resolve which directories to audit, and the baseline defaults each uses:\n * `root` \u2192 the monorepo root with {@link ROOT_DEFAULT_RULES}; `all` \u2192 every\n * discovered non-vendor package; otherwise the package walked up from cwd.\n */\nconst resolveTargets = async (options: AuditOptions): Promise<AuditTarget[]> => {\n if (options.root) {\n return [{ dir: await getProjectRoot(), baseline: ROOT_DEFAULT_RULES }]\n }\n\n if (options.all) {\n const dirs = await discoverPackages(await getProjectRoot())\n\n return dirs.map((dir) => {\n return { dir }\n })\n }\n\n return [{ dir: await findPackageRoot(options.cwd ?? process.cwd()) }]\n}\n\n/**\n * Print a package's audit result as doctor-style `[PASS]`/`[FAIL]` lines.\n */\nconst logResult = (result: PackageValidationResult): void => {\n const header = result.passed ? 'PASS' : 'FAIL'\n\n logger.info(`\\n${result.packageName} \u2014 ${header}`)\n\n for (const check of result.checks) {\n const icon = check.status === 'pass' ? '[PASS]' : '[FAIL]'\n\n logger.info(` ${icon} ${check.name}: ${check.message}`)\n }\n}\n\n/**\n * Audit the monorepo root (`root`), every non-vendor workspace package (`all`),\n * or the package resolved by walking up from the working directory (default \u2014\n * the shape used by a package's `\"infra-kit-check\": \"pnpm exec infra-kit audit\"` script). The\n * returned `structuredContent.allPassed` lets the CLI set a non-zero exit code so\n * the audit fails CI; this function never calls `process.exit` so the MCP tool\n * can reuse it.\n *\n * @example\n * // CLI inside packages/serverless-config: `infra-kit audit`\n * await audit() // audits the current package\n * await audit({ all: true }) // audits every non-vendor workspace package\n * await audit({ root: true }) // audits the monorepo root (turbo + root commands)\n */\nexport const audit = async (options: AuditOptions = {}) => {\n const targets = await resolveTargets(options)\n\n const results: PackageValidationResult[] = []\n\n for (const target of targets) {\n results.push(await validatePackage(target.dir, target.baseline))\n }\n\n // Root audit also validates project-level devServersPresets proxy locality (a `local`\n // route override must have its backend launched by the preset). Skipped when the\n // project declares no presets. Only meaningful at the root \u2014 presets live in the\n // project infra-kit.json, not in any single package.\n if (options.root) {\n const presetResult = await checkDevPresets(targets[0]!.dir)\n\n if (presetResult) {\n results.push(presetResult)\n }\n }\n\n for (const result of results) {\n logResult(result)\n }\n\n const allPassed = results.every((result) => {\n return result.passed\n })\n\n logger.info(`\\n${allPassed ? '\u2705 All valid' : '\u274C Audit failed'} (${results.length} checked)`)\n\n const structuredContent = {\n allPassed,\n packages: results.map((result) => {\n return {\n name: result.packageName,\n passed: result.passed,\n checks: result.checks,\n }\n }),\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\nconst auditInputSchema = {\n all: z.boolean().optional().describe('Audit every non-vendor workspace package'),\n root: z.boolean().optional().describe('Audit the monorepo root (turbo pipeline + root commands)'),\n}\n\nconst auditOutputSchema = {\n allPassed: z.boolean().describe('Whether every audited package passed all checks'),\n packages: z\n .array(\n z.object({\n name: z.string(),\n passed: z.boolean(),\n checks: z.array(\n z.object({\n name: z.string(),\n status: z.enum(['pass', 'fail']),\n message: z.string(),\n }),\n ),\n }),\n )\n .describe('Per-package check results'),\n}\n\n// MCP Tool Registration\nexport const auditMcpTool = defineMcpTool({\n name: 'audit',\n description:\n 'Audit packages against infra-kit.config.ts rules (config present and valid, required scripts, required files, and turbo tasks for the root). Defaults to the current package; all=true audits every non-vendor workspace package; root=true audits the monorepo root.',\n inputSchema: auditInputSchema,\n outputSchema: auditOutputSchema,\n handler: (params) => {\n return audit({ all: params.all, root: params.root })\n },\n})\n", "import fs from 'node:fs/promises'\n\n/**\n * Resolve whether a path is reachable, suppressing ENOENT into a boolean.\n *\n * @example\n * await pathExists('/etc/hosts') // => true\n * await pathExists('/nope') // => false\n */\nexport const pathExists = async (target: string): Promise<boolean> => {\n try {\n await fs.access(target)\n\n return true\n } catch {\n return false\n }\n}\n", "import path from 'node:path'\n\nimport { DEFAULT_RULES } from 'src/lib/package-config'\nimport type { ResolvedPackageRules } from 'src/lib/package-config'\n\nimport { checkConfig, checkFiles, checkScripts, checkTurbo } from './checks'\nimport { readPackageJson } from './loader'\nimport type { PackageCheck, PackageValidationResult } from './types'\n\n// Re-exported on the historical import path so consumers and tests that reach\n// for the loader through `package-validator` keep resolving after the split.\nexport { discoverPackages, loadPackageConfig } from './loader'\nexport type { PackageCheck, PackageValidationResult } from './types'\n\n/**\n * Validate a single directory against its `infra-kit.config.ts` rules: the config\n * must be present and valid, every required script must be declared, every\n * required file must exist, and (root only) every required turbo task must be\n * defined. When the config fails to load, only that check is reported (the rules\n * are unknown, so the rule-based checks are skipped). `baseline` selects which\n * under-the-hood defaults apply \u2014 package defaults or {@link ROOT_DEFAULT_RULES}.\n *\n * @example\n * const result = await validatePackage('/repo/packages/serverless-config')\n * // result.passed reflects the package's conformance; result.checks lists each check\n */\nexport const validatePackage = async (\n packageDir: string,\n baseline: Readonly<ResolvedPackageRules> = DEFAULT_RULES,\n): Promise<PackageValidationResult> => {\n const pkgJson = await readPackageJson(packageDir)\n const packageName = pkgJson.name ?? path.basename(packageDir)\n\n const { check: configCheck, rules } = await checkConfig(packageDir, baseline)\n const checks: PackageCheck[] = [configCheck]\n\n if (rules) {\n checks.push(...checkScripts(pkgJson.scripts ?? {}, rules.requiredScripts))\n checks.push(...(await checkFiles(packageDir, rules.requiredFiles)))\n checks.push(...(await checkTurbo(packageDir, rules.turboTasks)))\n }\n\n const passed = checks.every((check) => {\n return check.status === 'pass'\n })\n\n return { packageDir, packageName, checks, passed }\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { z } from 'zod'\n\nimport { DEFAULT_RULES, packageConfigSchema, resolvePackageConfig } from 'src/lib/package-config'\nimport type { ResolvedPackageRules } from 'src/lib/package-config'\n\nimport { pathExists } from '../fs-utils'\n\n/** Per-package config filename every validated package must provide. */\nexport const PACKAGE_CONFIG_FILE = 'infra-kit.config.ts'\n\ninterface PackageJsonShape {\n name?: string\n scripts?: Record<string, string>\n}\n\n/**\n * Read and JSON-parse a package.json, returning an empty object when it is\n * missing or unreadable so callers can degrade into a clear \"missing\" check.\n */\nexport const readPackageJson = async (packageDir: string): Promise<PackageJsonShape> => {\n try {\n const raw = await fs.readFile(path.join(packageDir, 'package.json'), 'utf-8')\n\n return JSON.parse(raw) as PackageJsonShape\n } catch {\n return {}\n }\n}\n\n/**\n * Load, resolve, and validate a package's `infra-kit.config.ts`.\n *\n * Dynamic-imports the file (ESM), resolves the Vite-style factory or object\n * default export, validates the result against {@link packageConfigSchema}, and\n * merges it over the defaults. Throws a descriptive error when the file is\n * absent or the resolved config violates the schema.\n *\n * @example\n * await loadPackageConfig('/repo/packages/serverless-config')\n * // => { requiredScripts: [], requiredFiles: ['serverless.common.yml'], turboTasks: [] }\n */\nexport const loadPackageConfig = async (\n packageDir: string,\n baseline: Readonly<ResolvedPackageRules> = DEFAULT_RULES,\n): Promise<ResolvedPackageRules> => {\n const configPath = path.join(packageDir, PACKAGE_CONFIG_FILE)\n\n if (!(await pathExists(configPath))) {\n throw new Error(`${PACKAGE_CONFIG_FILE} not found at ${configPath}`)\n }\n\n // Cache-bust with the file mtime so repeated loads (long-running MCP server)\n // pick up edits without a process restart. `.ts` configs load via Node's\n // native type stripping (the repo requires Node >= 24).\n const stat = await fs.stat(configPath)\n const moduleUrl = `${pathToFileURL(configPath).href}?mtime=${Number(stat.mtimeMs)}`\n\n const imported = (await import(moduleUrl)) as { default?: unknown }\n const rawExport = imported.default\n\n if (rawExport === undefined) {\n throw new Error(`${PACKAGE_CONFIG_FILE} at ${configPath} has no default export`)\n }\n\n const resolvedExport = typeof rawExport === 'function' ? await (rawExport as () => unknown)() : rawExport\n\n const parsed = packageConfigSchema.safeParse(resolvedExport)\n\n if (!parsed.success) {\n throw new Error(`Invalid ${PACKAGE_CONFIG_FILE} at ${configPath}: ${z.prettifyError(parsed.error)}`)\n }\n\n return resolvePackageConfig(parsed.data, baseline)\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport yaml from 'yaml'\n\nimport { pathExists } from '../fs-utils'\n\nconst WORKSPACE_FILE = 'pnpm-workspace.yaml'\n\n/**\n * List the immediate child directories of `dir`, returning `[]` when the path\n * can't be read (e.g. the parent glob segment matched a non-existent dir).\n */\nconst listChildDirs = async (dir: string): Promise<string[]> => {\n const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => {\n return []\n })\n\n return entries\n .filter((entry) => {\n return entry.isDirectory()\n })\n .map((entry) => {\n return path.join(dir, entry.name)\n })\n}\n\n/**\n * Apply one glob segment to a set of directories: `*` fans out to every child\n * directory, a literal segment keeps the dirs where that child path exists.\n */\nconst expandSegment = async (dirs: string[], segment: string): Promise<string[]> => {\n const next: string[] = []\n\n for (const dir of dirs) {\n if (segment === '*') {\n next.push(...(await listChildDirs(dir)))\n\n continue\n }\n\n const candidate = path.join(dir, segment)\n\n if (await pathExists(candidate)) {\n next.push(candidate)\n }\n }\n\n return next\n}\n\n/**\n * Expand a single pnpm-workspace glob (only the `*` segment wildcard is\n * supported, which covers every pattern this monorepo uses) into directories.\n */\nconst expandGlob = async (projectRoot: string, pattern: string): Promise<string[]> => {\n let dirs = [projectRoot]\n\n for (const segment of pattern.split('/')) {\n dirs = await expandSegment(dirs, segment)\n }\n\n return dirs\n}\n\n/**\n * Discover validatable workspace packages from `pnpm-workspace.yaml`.\n *\n * Negation patterns (`!\u2026`) and everything under `vendor/` are excluded \u2014\n * vendor is mirrored from `starter-workspace` and is checksum-enforced by\n * `pnpm vendor:check`, so its configs are owned upstream, not here. Only\n * directories that actually contain a package.json are returned.\n *\n * @example\n * await discoverPackages('/repo')\n * // => ['/repo/apps/infra-kit/cli', '/repo/packages/serverless-config']\n */\nexport const discoverPackages = async (projectRoot: string): Promise<string[]> => {\n const raw = await fs.readFile(path.join(projectRoot, WORKSPACE_FILE), 'utf-8')\n const parsed = (yaml.parse(raw) ?? {}) as { packages?: string[] }\n\n const patterns = (parsed.packages ?? []).filter((pattern) => {\n return !pattern.startsWith('!') && !pattern.startsWith('vendor')\n })\n\n const found = new Set<string>()\n\n for (const pattern of patterns) {\n const dirs = await expandGlob(projectRoot, pattern)\n\n for (const dir of dirs) {\n if (await pathExists(path.join(dir, 'package.json'))) {\n found.add(dir)\n }\n }\n }\n\n return [...found].sort()\n}\n", "import { DEFAULT_RULES } from 'src/lib/package-config'\nimport type { ResolvedPackageRules } from 'src/lib/package-config'\n\nimport { PACKAGE_CONFIG_FILE, loadPackageConfig } from '../loader'\nimport type { PackageCheck } from '../types'\n\n/**\n * Build the \"config present and valid\" check, returning the resolved rules when\n * the load succeeds so the caller can run the rule-based checks against them.\n * When the config fails to load the rules are `null` and the caller skips the\n * rule-based checks (the expectations are unknown).\n */\nexport const checkConfig = async (\n packageDir: string,\n baseline: Readonly<ResolvedPackageRules> = DEFAULT_RULES,\n): Promise<{ check: PackageCheck; rules: ResolvedPackageRules | null }> => {\n try {\n const rules = await loadPackageConfig(packageDir, baseline)\n\n return {\n check: { name: PACKAGE_CONFIG_FILE, status: 'pass', message: 'present and valid' },\n rules,\n }\n } catch (err) {\n return {\n check: { name: PACKAGE_CONFIG_FILE, status: 'fail', message: (err as Error).message },\n rules: null,\n }\n }\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport type { PackageCheck } from '../types'\n\n/**\n * Check that every required file exists relative to the package root AND is a\n * regular file. A directory that happens to share the required name fails \u2014 a\n * required `readme.md` must be the file, not a folder.\n */\nexport const checkFiles = async (packageDir: string, requiredFiles: string[]): Promise<PackageCheck[]> => {\n return Promise.all(\n requiredFiles.map(async (file) => {\n const stat = await fs.stat(path.join(packageDir, file)).catch(() => {\n return null\n })\n\n if (!stat) {\n return { name: `file:${file}`, status: 'fail' as const, message: `missing file: ${file}` }\n }\n\n if (!stat.isFile()) {\n return { name: `file:${file}`, status: 'fail' as const, message: `not a file: ${file} (found a directory)` }\n }\n\n return { name: `file:${file}`, status: 'pass' as const, message: 'exists' }\n }),\n )\n}\n", "import type { PackageCheck } from '../types'\n\n/**\n * Check that every required script is present in the package.json `scripts` map\n * and carries a runnable command. A key declared with an empty or whitespace-only\n * value fails as well \u2014 an empty script silently no-ops in CI, so presence alone\n * is not enough.\n */\nexport const checkScripts = (scripts: Record<string, string>, requiredScripts: string[]): PackageCheck[] => {\n return requiredScripts.map((script) => {\n const value = scripts[script]\n\n if (typeof value !== 'string') {\n return { name: `script:${script}`, status: 'fail', message: `missing \"${script}\" in package.json scripts` }\n }\n\n if (value.trim().length === 0) {\n return { name: `script:${script}`, status: 'fail', message: `\"${script}\" is empty in package.json scripts` }\n }\n\n return { name: `script:${script}`, status: 'pass', message: 'defined' }\n })\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport type { PackageCheck } from '../types'\n\nconst TURBO_FILE = 'turbo.json'\n\n/**\n * Check that every required turbo task is defined in turbo.json `tasks`. A root\n * task may be keyed as either `name` or `//#name`, so both forms count as present.\n * Runs only when the resolved rules ask for turbo tasks (the monorepo root).\n *\n * Reports a single clear diagnostic when turbo.json is unreadable or carries no\n * `tasks` object, rather than emitting one identical \"missing task\" line per\n * required task.\n */\nexport const checkTurbo = async (packageDir: string, requiredTasks: string[]): Promise<PackageCheck[]> => {\n if (requiredTasks.length === 0) {\n return []\n }\n\n let parsed: { tasks?: Record<string, unknown> }\n\n try {\n const raw = await fs.readFile(path.join(packageDir, TURBO_FILE), 'utf-8')\n\n parsed = JSON.parse(raw) as { tasks?: Record<string, unknown> }\n } catch (err) {\n return [{ name: TURBO_FILE, status: 'fail', message: `cannot read/parse ${TURBO_FILE}: ${(err as Error).message}` }]\n }\n\n const tasks = parsed.tasks\n\n if (tasks === null || typeof tasks !== 'object') {\n return [{ name: TURBO_FILE, status: 'fail', message: `no \"tasks\" object defined in ${TURBO_FILE}` }]\n }\n\n return requiredTasks.map((task) => {\n const defined = task in tasks || `//#${task}` in tasks\n\n return {\n name: `turbo:${task}`,\n status: defined ? 'pass' : 'fail',\n message: defined ? 'defined' : `missing turbo task \"${task}\" in ${TURBO_FILE}`,\n }\n })\n}\n", "import type { z } from 'zod'\n\nexport interface ToolsExecutionResult<TStructured = Record<string, unknown>> {\n [x: string]: unknown\n content: {\n type: 'text'\n text: string\n }[]\n structuredContent?: TStructured\n}\n\nexport interface RequiredConfirmedOptionArg {\n confirmedCommand: boolean\n}\n\nexport interface McpTool<TIn extends z.ZodRawShape = z.ZodRawShape, TOut extends z.ZodRawShape = z.ZodRawShape> {\n name: string\n description: string\n inputSchema: TIn\n outputSchema: TOut\n handler: (\n params: z.infer<z.ZodObject<TIn>> & RequiredConfirmedOptionArg,\n ) => Promise<ToolsExecutionResult<z.infer<z.ZodObject<TOut>>>>\n}\n\n/**\n * Build the dual-channel content array shared by every MCP tool. Narrows the\n * literal `type: 'text'` so handlers can use inferred return types without TS\n * widening `type` to `string` \u2014 which would otherwise break assignability\n * against the MCP SDK's content union.\n *\n * @example\n * return {\n * content: textContent(JSON.stringify(structuredContent, null, 2)),\n * structuredContent,\n * }\n */\nexport const textContent = (text: string): ToolsExecutionResult['content'] => {\n return [{ type: 'text', text }]\n}\n\n/**\n * Factory that ties the handler's return type to the declared `outputSchema`\n * so `structuredContent` is checked against the schema at compile time. If a\n * handler accidentally drops or renames a field, TS errors at the registration\n * site rather than at runtime in an MCP client.\n *\n * @example\n * export const envLoadMcpTool = defineMcpTool({\n * name: 'env-load',\n * description: '...',\n * inputSchema: { config: z.string() },\n * outputSchema: {\n * filePath: z.string(),\n * variableCount: z.number(),\n * project: z.string(),\n * config: z.string(),\n * },\n * handler: envLoad,\n * })\n */\nexport const defineMcpTool = <TIn extends z.ZodRawShape, TOut extends z.ZodRawShape>(\n tool: McpTool<TIn, TOut>,\n): McpTool<TIn, TOut> => {\n return tool\n}\n", "/**\n * Root-audit checks for `devServersPresets`: target-key grammar, then proxy locality.\n *\n * Bridges the impure world (project config + discovery + each frontend's loaded\n * `infra-kit.config.ts`) to the pure {@link validatePresetKeys} / {@link validatePresetProxy}\n * rules, then shapes the result as a {@link PackageValidationResult} so it slots into the\n * existing `infra-kit audit --root` output. Skipped (returns null) when the project declares\n * no `devServersPresets`.\n */\nimport path from 'node:path'\n\nimport { discoverApiApps, discoverUiApps } from 'src/dev/discovery'\nimport { resolvePreset, validatePresetKeys, validatePresetProxy } from 'src/dev/presets'\nimport type { DiscoveredParts, PresetProxyContext } from 'src/dev/presets'\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\nimport type { DevPresets } from 'src/lib/infra-kit-config'\nimport type { PackageCheck, PackageValidationResult } from 'src/lib/package-validator'\nimport { loadDev } from 'src/lib/vite/vite'\n\n/** A frontend config that a preset references but that failed to load (bad/invalid config). */\ninterface LoadError {\n app: string\n message: string\n}\n\ninterface RoutePkgLookup {\n routePkg: PresetProxyContext['routePkg']\n loadErrors: LoadError[]\n}\n\n/**\n * The set of frontend app folders any preset's proxy override actually references \u2014\n * the only configs the audit needs to load. Scoping to these keeps an unrelated\n * broken frontend config from failing (or crashing) the devServersPresets audit.\n */\nconst collectReferencedApps = (presets: DevPresets, discovered: DiscoveredParts): string[] => {\n const apps = new Set<string>()\n\n for (const preset of Object.values(presets)) {\n for (const app of Object.keys(resolvePreset(preset, discovered).proxy)) {\n apps.add(app)\n }\n }\n\n return [...apps]\n}\n\n/**\n * Load each referenced frontend's `dev.proxy.routes` into a sync `(app, route) \u2192 pkg`\n * lookup. A config that throws (schema-invalid, import error) is captured as a\n * {@link LoadError} instead of rejecting, so one bad config yields an actionable\n * audit line rather than crashing the whole `audit --root`.\n */\nconst buildRoutePkgLookup = async (root: string, apps: string[]): Promise<RoutePkgLookup> => {\n const routesByApp: Record<string, Record<string, string>> = {}\n const loadErrors: LoadError[] = []\n\n await Promise.all(\n apps.map(async (app) => {\n try {\n const dev = await loadDev(path.join(root, 'apps', app, 'ui'))\n const routes = dev?.proxy?.routes\n\n if (!routes) return\n\n routesByApp[app] = Object.fromEntries(\n Object.entries(routes).map(([route, route_]) => {\n return [route, route_.packageName]\n }),\n )\n } catch (error) {\n loadErrors.push({ app, message: error instanceof Error ? error.message : String(error) })\n }\n }),\n )\n\n return {\n routePkg: (app, route) => {\n return routesByApp[app]?.[route]\n },\n loadErrors,\n }\n}\n\n/** Shape validator issues + config load errors into audit check lines (fail-only; caller adds the pass line). */\nconst toFailChecks = (issues: ReturnType<typeof validatePresetProxy>, loadErrors: LoadError[]): PackageCheck[] => {\n return [\n ...loadErrors.map((error): PackageCheck => {\n return {\n name: `devServersPresets:config:${error.app}`,\n status: 'fail',\n message: `could not load apps/${error.app}/ui/infra-kit.config.ts (referenced by a preset proxy override): ${error.message}`,\n }\n }),\n ...issues.map((issue): PackageCheck => {\n return { name: `devServersPresets:${issue.preset}`, status: 'fail', message: issue.message }\n }),\n ]\n}\n\n/**\n * Run the `devServersPresets` audit for the monorepo root: first the target-key grammar\n * (every key must name an `<app>/api` or `<app>/ui` package), then proxy locality. Returns\n * a synthetic `devServersPresets` validation result (one pass line, or one fail line per\n * bad key / violation / unloadable referenced config), or null when there are no presets\n * to check.\n *\n * @example\n * const result = await checkDevPresets('/repo')\n * // result?.checks[0] => { name: 'devServersPresets:proxy-locality', status: 'pass', message: '\u2026' }\n */\nexport const checkDevPresets = async (root: string): Promise<PackageValidationResult | null> => {\n const presets = (await getInfraKitConfig()).devServersPresets\n\n if (!presets || Object.keys(presets).length === 0) {\n return null\n }\n\n // Grammar first: an unparseable target key makes `resolvePreset` throw, so it has to\n // surface as an audit line here rather than crash every check below it.\n const keyIssues = validatePresetKeys(presets)\n\n if (keyIssues.length > 0) {\n const checks = keyIssues.map((issue): PackageCheck => {\n return { name: `devServersPresets:${issue.preset}`, status: 'fail', message: issue.message }\n })\n\n return { packageDir: root, packageName: 'devServersPresets', checks, passed: false }\n }\n\n const apiApps = discoverApiApps(root)\n const uiApps = discoverUiApps(root)\n const discovered: DiscoveredParts = {\n api: apiApps.map((a) => {\n return a.name\n }),\n ui: uiApps.map((a) => {\n return a.name\n }),\n }\n\n const { routePkg, loadErrors } = await buildRoutePkgLookup(root, collectReferencedApps(presets, discovered))\n\n const ctx: PresetProxyContext = {\n discovered,\n apiPkgByApp: Object.fromEntries(\n apiApps.map((a) => {\n return [a.name, a.packageName]\n }),\n ),\n routePkg,\n }\n\n const failChecks = toFailChecks(validatePresetProxy(presets, ctx), loadErrors)\n\n const checks: PackageCheck[] =\n failChecks.length === 0\n ? [\n {\n name: 'devServersPresets:proxy-locality',\n status: 'pass',\n message: 'every local proxy override resolves to a backend the preset launches',\n },\n ]\n : failChecks\n\n return { packageDir: root, packageName: 'devServersPresets', checks, passed: failChecks.length === 0 }\n}\n", "import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { z } from 'zod'\nimport { $ } from 'zx'\n\nimport { AGENTS_MARKER_END, AGENTS_MARKER_START } from 'src/commands/init/agent-files'\nimport { MARKER_END, MARKER_START, buildShellBlock } from 'src/commands/init/init'\nimport {\n caFingerprintMatches,\n defaultIsListening,\n defaultIsProxyServing,\n formatPortlessCommand,\n handshakeChainsToCa,\n listRoutes,\n readCaPath,\n resolvePortlessBin,\n} from 'src/dev/proxy/portless-driver'\nimport type { HandshakeResult, PortlessRoute } from 'src/dev/proxy/portless-driver'\nimport { getDopplerProject, listDopplerProjects } from 'src/integrations/doppler'\nimport { describeOverrides, readOverrideSummary } from 'src/lib/config-overrides'\nimport { DEFAULT_WARM_TTL_SECONDS, ENV_LOAD_FILE, getProjectWarmCacheDir } from 'src/lib/constants'\nimport { getProjectRoot } from 'src/lib/git-utils/git-utils'\nimport {\n DEFAULT_DEV_PROXY_PORT,\n getInfraKitConfig,\n getInfraKitConfigPaths,\n resetInfraKitConfigCache,\n resolveConfiguredIdes,\n} from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { hasManagedBlock } from 'src/lib/managed-block'\nimport { tildify } from 'src/lib/path-display'\nimport { canonicalizeProjectRoot } from 'src/lib/warm-cache'\nimport { defineMcpTool, textContent } from 'src/types'\n\ninterface CheckResult {\n name: string\n status: 'pass' | 'fail'\n message: string\n}\n\nconst checkCommand = async (\n name: string,\n command: string[],\n successMsg: string,\n failMsg: string,\n): Promise<CheckResult> => {\n try {\n await $`${command}`\n\n return { name, status: 'pass', message: successMsg }\n } catch {\n return { name, status: 'fail', message: failMsg }\n }\n}\n\nconst checkZshrcInitialized = (): CheckResult => {\n const name = 'zshrc init block'\n const zshrcPath = path.join(os.homedir(), '.zshrc')\n\n if (!fs.existsSync(zshrcPath)) {\n return { name, status: 'fail', message: '~/.zshrc not found. Run: infra-kit init' }\n }\n\n const content = fs.readFileSync(zshrcPath, 'utf-8')\n const startIdx = content.indexOf(MARKER_START)\n const endIdx = content.indexOf(MARKER_END)\n\n if (startIdx === -1 || endIdx === -1 || endIdx < startIdx) {\n return {\n name,\n status: 'fail',\n message: 'infra-kit shell block missing from ~/.zshrc. Run: infra-kit init',\n }\n }\n\n const installedBlock = content.slice(startIdx, endIdx + MARKER_END.length).trim()\n const expectedBlock = buildShellBlock().trim()\n\n if (installedBlock !== expectedBlock) {\n return {\n name,\n status: 'fail',\n message: 'infra-kit shell block in ~/.zshrc is out of date. Run: infra-kit init',\n }\n }\n\n return { name, status: 'pass', message: 'infra-kit shell block in ~/.zshrc is up to date' }\n}\n\n/**\n * Surface the state of the project-scoped warm env cache (populated by an\n * auto-loaded shell startup). Informational only \u2014 always passes \u2014 since a\n * missing or stale warm file just means the next auto-load will (re)populate\n * it rather than indicating a problem.\n */\nconst checkWarmCache = async (): Promise<CheckResult> => {\n const name = 'warm cache'\n\n let root: string\n\n try {\n root = await getProjectRoot()\n } catch {\n return { name, status: 'pass', message: 'warm cache: not a git project' }\n }\n\n const canon = canonicalizeProjectRoot(root)\n\n if (!canon) {\n return { name, status: 'pass', message: 'warm cache: unavailable' }\n }\n\n const warmFile = path.join(getProjectWarmCacheDir(canon), ENV_LOAD_FILE)\n\n if (!fs.existsSync(warmFile)) {\n return { name, status: 'pass', message: 'warm cache: none yet (populated on next auto-load)' }\n }\n\n const ageSeconds = Math.floor((Date.now() - fs.statSync(warmFile).mtimeMs) / 1000)\n const ageMinutes = Math.floor(ageSeconds / 60)\n const ttlMinutes = Math.floor(DEFAULT_WARM_TTL_SECONDS / 60)\n\n if (ageSeconds >= DEFAULT_WARM_TTL_SECONDS) {\n return {\n name,\n status: 'pass',\n message: `warm cache: present but stale (${ageMinutes}m old, TTL ${ttlMinutes}m) \u2014 will refresh`,\n }\n }\n\n return { name, status: 'pass', message: `warm cache: present, ${ageMinutes}m old` }\n}\n\nconst checkPnpmWorkspaceVirtualStore = async (): Promise<CheckResult> => {\n const name = 'pnpm enableGlobalVirtualStore'\n\n try {\n const root = await getProjectRoot()\n const yamlPath = path.join(root, 'pnpm-workspace.yaml')\n\n if (!fs.existsSync(yamlPath)) {\n return { name, status: 'fail', message: `pnpm-workspace.yaml not found at ${yamlPath}` }\n }\n\n const content = fs.readFileSync(yamlPath, 'utf-8')\n // eslint-disable-next-line sonarjs/super-linear-regex\n const enabled = /^\\s*enableGlobalVirtualStore\\s*:\\s*true\\s*$/m.test(content)\n\n if (!enabled) {\n return {\n name,\n status: 'fail',\n message: 'enableGlobalVirtualStore: true is missing in pnpm-workspace.yaml',\n }\n }\n\n return { name, status: 'pass', message: 'enableGlobalVirtualStore: true is set' }\n } catch (err) {\n return {\n name,\n status: 'fail',\n message: `Failed to read pnpm-workspace.yaml: ${(err as Error).message}`,\n }\n }\n}\n\nconst checkInfraKitConfigValid = async (): Promise<CheckResult> => {\n const name = 'infra-kit config valid'\n\n try {\n resetInfraKitConfigCache()\n await getInfraKitConfig()\n\n return {\n name,\n status: 'pass',\n message: 'infra-kit.json is valid (user overrides applied if present)',\n }\n } catch (err) {\n return { name, status: 'fail', message: (err as Error).message }\n }\n}\n\n/**\n * Verify the Doppler project configured in infra-kit.json (`envManagement.config.name`)\n * actually exists in the authenticated account \u2014 the proactive catch for the\n * mismatch that otherwise only surfaces as a cryptic `env-load` failure. Skips\n * (informational pass) when the config can't be read or the project list can't be\n * fetched (auth/network), so a logged-out user isn't misdiagnosed as\n * missing-project \u2014 the adjacent `doppler authenticated` check owns that failure.\n */\nexport const checkDopplerProjectExists = async (): Promise<CheckResult> => {\n const name = 'doppler project exists'\n\n let project: string\n\n try {\n project = await getDopplerProject()\n } catch {\n return { name, status: 'pass', message: 'Skipped \u2014 infra-kit config could not be read (see config check)' }\n }\n\n const projects = await listDopplerProjects()\n\n if (projects === null) {\n return { name, status: 'pass', message: 'Skipped \u2014 could not list Doppler projects (see doppler auth check)' }\n }\n\n if (projects.includes(project)) {\n return { name, status: 'pass', message: `Doppler project \"${project}\" exists` }\n }\n\n return {\n name,\n status: 'fail',\n message: `Doppler project \"${project}\" not found (set in infra-kit.json \u2192 envManagement.config.name). Available: ${\n projects.length > 0 ? projects.join(', ') : 'none'\n }`,\n }\n}\n\n/**\n * Surface where this developer's user-scope override file lives and what it CONTAINS. Always passes\n * (informational) inside a git repo; fails only when the config paths can't be resolved at all.\n *\n * Three-way on purpose. Every non-excluded command now seeds layer 3 in its preAction hook \u2014 and\n * `doctor` is one of them, so by the time this check runs the file should already exist. An ABSENT\n * file therefore no longer means \"not yet created\", it means the seed FAILED (read-only `$HOME`, a\n * sandbox, a locked-down CI image). That failure path is `logger.debug` only, so this line is the\n * ONLY place a permanently-failing seed becomes visible \u2014 it must not be confusable with a healthy\n * empty file. Content reporting is shared with `config path` via `readOverrideSummary` /\n * `describeOverrides` (one JSON-tolerance policy: raw keys, malformed degrades, never throws).\n *\n * @example\n * await checkUserOverridePath()\n * // {\n * // name: 'user override path',\n * // status: 'pass',\n * // message: '~/.infra-kit/projects/api/infra-kit.json (2 override(s): ide, dev) \u2014 project: api',\n * // }\n * // absent -> '\u2026 (not created \u2014 seed failed?) \u2014 project: api'\n * // empty -> '\u2026 (empty \u2014 no overrides) \u2014 project: api'\n * // corrupt -> '\u2026 (unreadable \u2014 invalid JSON) \u2014 project: api'\n */\nexport const checkUserOverridePath = async (): Promise<CheckResult> => {\n const name = 'user override path'\n\n try {\n const paths = await getInfraKitConfigPaths()\n const exists = fs.existsSync(paths.userProject)\n const summary = await readOverrideSummary(paths.userProject, exists)\n const suffix = exists ? describeOverrides(summary) : '(not created \u2014 seed failed?)'\n\n return {\n name,\n status: 'pass',\n message: `${tildify(paths.userProject)} ${suffix} \u2014 project: ${paths.projectName}`,\n }\n } catch (err) {\n return { name, status: 'fail', message: (err as Error).message }\n }\n}\n\n/**\n * Surface a lingering legacy `~/.infra-kit/config.json` left behind after the\n * user-global config was renamed to `infra-kit.json`. The on-`init` auto-rename\n * is the migration path, but a user who edits the file and never re-runs `init`\n * would silently have their overrides stop applying \u2014 this makes that visible.\n *\n * Branches on whether the canonical `infra-kit.json` already exists so the\n * message is accurate: only the \"no infra-kit.json yet\" case means overrides are\n * not being applied; when both exist the legacy file is merely stale.\n * Informational pass (no warning) when the paths can't resolve \u2014 mirrors\n * {@link checkIdeInstalled}: nothing to surface if there's no resolvable target.\n *\n * @example\n * await checkLegacyUserGlobalConfig()\n * // { name: 'legacy user-global config', status: 'fail', message: 'Legacy user-global config.json found \u2026' }\n */\nexport const checkLegacyUserGlobalConfig = async (): Promise<CheckResult> => {\n const name = 'legacy user-global config'\n\n let paths: Awaited<ReturnType<typeof getInfraKitConfigPaths>>\n\n try {\n paths = await getInfraKitConfigPaths()\n } catch {\n return { name, status: 'pass', message: 'Skipped \u2014 infra-kit config paths could not be resolved' }\n }\n\n const legacyPath = path.join(path.dirname(paths.userGlobal), 'config.json')\n\n if (!fs.existsSync(legacyPath)) {\n return { name, status: 'pass', message: 'No legacy user-global config.json' }\n }\n\n if (!fs.existsSync(paths.userGlobal)) {\n return {\n name,\n status: 'fail',\n message: `Legacy user-global config.json found at ${tildify(legacyPath)} \u2014 run \\`infra-kit init\\` to migrate it (your overrides are not being applied).`,\n }\n }\n\n return {\n name,\n status: 'fail',\n message: `Stale legacy config.json found at ${tildify(legacyPath)} \u2014 ${tildify(paths.userGlobal)} is active; remove the old file.`,\n }\n}\n\ninterface IdeProbe {\n ok: boolean\n label: string\n failMsg: string\n}\n\nconst IDE_PROBE_META: Record<'cursor' | 'zed', { command: string[]; label: string; failMsg: string }> = {\n cursor: {\n command: ['cursor', '--version'],\n label: 'Cursor',\n failMsg: 'Cursor is not installed. Install from: https://cursor.com/',\n },\n zed: {\n command: ['zed', '--version'],\n label: 'Zed',\n failMsg: 'Zed is not installed. Install from: https://zed.dev/',\n },\n}\n\nconst probeIde = async (provider: 'cursor' | 'zed'): Promise<IdeProbe> => {\n const meta = IDE_PROBE_META[provider]\n\n try {\n await $`${meta.command}`\n\n return { ok: true, label: meta.label, failMsg: meta.failMsg }\n } catch {\n return { ok: false, label: meta.label, failMsg: meta.failMsg }\n }\n}\n\n/**\n * Check that every editor configured under `ide` is installed. Reads the merged\n * infra-kit config and probes each matching binary (`cursor`/`zed`). Passes only\n * if all configured editors are present; fails listing any that are missing.\n * Informational pass when no IDE is configured or the config can't be read \u2014 an\n * unconfigured editor is a valid setup, and config validity is reported\n * separately by `checkInfraKitConfigValid`.\n */\nexport const checkIdeInstalled = async (): Promise<CheckResult> => {\n const name = 'ide installed'\n\n let providers: ('cursor' | 'zed')[]\n\n try {\n resetInfraKitConfigCache()\n const config = await getInfraKitConfig()\n\n providers = resolveConfiguredIdes(config).map((ide) => {\n return ide.provider\n })\n } catch {\n return { name, status: 'pass', message: 'Skipped \u2014 infra-kit config could not be read (see config check)' }\n }\n\n if (providers.length === 0) {\n return { name, status: 'pass', message: 'No IDE configured (ide unset)' }\n }\n\n const probes = await Promise.all(\n providers.map((provider) => {\n return probeIde(provider)\n }),\n )\n const missing = probes.filter((probe) => {\n return !probe.ok\n })\n\n if (missing.length === 0) {\n return {\n name,\n status: 'pass',\n message: `Installed: ${probes\n .map((probe) => {\n return probe.label\n })\n .join(', ')}`,\n }\n }\n\n return {\n name,\n status: 'fail',\n message: missing\n .map((probe) => {\n return probe.failMsg\n })\n .join('; '),\n }\n}\n\nconst RTK_REQUIRED_INDEXES = [1, 2, 3, 5, 7] as const\n\nconst checkRtkConfigured = async (): Promise<CheckResult> => {\n const name = 'rtk configured'\n\n try {\n const result = await $`rtk init --show`\n const statusLines = result.stdout\n .split('\\n')\n .map((l) => {\n return l.trim()\n })\n .filter((l) => {\n return l.startsWith('[ok]') || l.startsWith('[--]')\n })\n\n const failed: number[] = []\n\n for (const idx of RTK_REQUIRED_INDEXES) {\n const line = statusLines[idx - 1]\n\n if (!line || !line.startsWith('[ok]')) {\n failed.push(idx)\n }\n }\n\n if (failed.length > 0) {\n return {\n name,\n status: 'fail',\n message: `rtk setup incomplete (items ${failed.join(', ')} not [ok]). Run: rtk init -g --auto-patch`,\n }\n }\n\n return {\n name,\n status: 'pass',\n message: 'rtk hook, RTK.md, global CLAUDE.md, settings.json, and Cursor hook are configured',\n }\n } catch (err) {\n return {\n name,\n status: 'fail',\n message: `Failed to run 'rtk init --show': ${(err as Error).message}`,\n }\n }\n}\n\n/**\n * Check that the repo agent-instruction guidance managed by `infra-kit init` exists:\n * the guidance block in `CLAUDE.md`. Presence only. Repo-gated: returns no checks\n * when run outside an infra-kit repo so doctor never crashes there. A repo that\n * predates the AGENTS.md\u2192CLAUDE.md migration will report this check as failing until\n * `infra-kit init` is re-run.\n */\nexport const checkAgentFiles = async (): Promise<CheckResult[]> => {\n let mainConfigPath: string\n\n try {\n mainConfigPath = (await getInfraKitConfigPaths()).main\n } catch {\n return []\n }\n\n if (!fs.existsSync(mainConfigPath)) return []\n\n const claudePath = path.join(path.dirname(mainConfigPath), 'CLAUDE.md')\n const content = fs.existsSync(claudePath) ? fs.readFileSync(claudePath, 'utf-8') : ''\n const present = hasManagedBlock(content, AGENTS_MARKER_START, AGENTS_MARKER_END)\n\n return [\n {\n name: 'CLAUDE.md block',\n status: present ? 'pass' : 'fail',\n message: present ? 'CLAUDE.md block present' : 'infra-kit block missing from CLAUDE.md. Run: infra-kit init',\n },\n ]\n}\n\n/**\n * The one-time, out-of-band, ROOT command that installs the `:443` daemon. infra-kit never runs it \u2014 it\n * only ever prints it (principle 3: infra-kit probes and prints, it never elevates).\n *\n * Rendered from the resolved `dist/cli.js`, never as a bare `portless`: portless is an npm dependency in\n * `node_modules`, not a global bin, and `sudo` swaps `PATH` for `secure_path` \u2014 so the bare form cannot\n * resolve under sudo even in a shell where it otherwise would. See {@link formatPortlessCommand}.\n */\nconst installDaemonCmd = (bin: string): string => {\n return formatPortlessCommand(['service', 'install'], { sudo: true, bin })\n}\n\n/** The sudo-free \"trust the local CA\" command, rendered from the same resolved bin. */\nconst trustCmd = (bin: string): string => {\n return formatPortlessCommand(['trust'], { bin })\n}\n\n/** TLS chain failures that mean \"the daemon's cert does not chain to the `ca.pem` we hold\". */\nconst CA_MISMATCH_CODES = new Set(['SELF_SIGNED_CERT_IN_CHAIN', 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'])\n\n/**\n * A validating probe of an IP literal sends no SNI and lands on portless's default cert, which has no IP\n * SAN \u2014 so this code means OUR probe forgot a `servername`, never that the user's CA went bad. It must\n * never reach a `portless trust` remediation: doing so would tell every correctly-configured user to\n * repair a trust store that is fine.\n */\nconst PROBE_BUG_CODE = 'ERR_TLS_CERT_ALTNAME_INVALID'\n\n/** Every process/fs seam the portless checks touch, injected so tests never reach the real daemon. */\nexport interface PortlessCheckDeps {\n resolveBin?: () => string | null\n isProxyServing?: (port: number, tls: boolean) => Promise<boolean>\n handshake?: (port: number, servername: string) => Promise<HandshakeResult>\n caTrusted?: () => boolean\n routes?: () => PortlessRoute[]\n isListening?: (port: number) => Promise<boolean>\n caPath?: () => string\n}\n\n/** Is the daemon serving portless-over-TLS on the dev proxy port? Proven on the wire; reads no state file. */\nconst checkPortlessServing = async (\n isProxyServing: NonNullable<PortlessCheckDeps['isProxyServing']>,\n bin: string,\n): Promise<CheckResult> => {\n const name = `portless serving TLS on :${DEFAULT_DEV_PROXY_PORT}`\n const serving = await isProxyServing(DEFAULT_DEV_PROXY_PORT, true)\n\n if (!serving) {\n return {\n name,\n status: 'fail',\n message: `No portless daemon is serving HTTPS on :${DEFAULT_DEV_PROXY_PORT}. Install it once (needs root): \\`${installDaemonCmd(bin)}\\`.`,\n }\n }\n\n return { name, status: 'pass', message: `portless is serving HTTPS on :${DEFAULT_DEV_PROXY_PORT}` }\n}\n\n/**\n * Does the daemon serve a certificate that chains to the `ca.pem` on disk? The only check that VALIDATES\n * the chain \u2014 the serving probe above deliberately does not, so that a down daemon and an untrusted CA stay\n * two distinguishable failures with two different fixes.\n *\n * Probes `localhost` (always in the default cert's SANs, so it works on a fresh machine with zero aliases)\n * and, when one is registered, a real alias \u2014 which validates the exact path the browser takes.\n */\nconst checkPortlessCaChain = async (\n handshake: NonNullable<PortlessCheckDeps['handshake']>,\n routes: PortlessRoute[],\n caPath: string,\n bin: string,\n): Promise<CheckResult> => {\n const name = 'portless CA chain valid'\n const servernames = [\n 'localhost',\n ...routes.slice(0, 1).map((route) => {\n return route.name\n }),\n ]\n const results = await Promise.all(\n servernames.map(async (servername) => {\n return { servername, result: await handshake(DEFAULT_DEV_PROXY_PORT, servername) }\n }),\n )\n const failed = results.filter((entry) => {\n return !entry.result.ok\n })\n\n if (failed.length === 0) {\n return {\n name,\n status: 'pass',\n message: `Served certificate chains to ${tildify(caPath)} (${servernames.join(', ')})`,\n }\n }\n\n const codes = failed.map((entry) => {\n return entry.result.ok ? '' : entry.result.code\n })\n\n if (codes.includes(PROBE_BUG_CODE)) {\n return {\n name,\n status: 'fail',\n message: `Internal check error: the TLS probe got ${PROBE_BUG_CODE} for servername(s) ${failed\n .map((entry) => {\n return entry.servername\n })\n .join(', ')} \u2014 doctor's probe is wrong, your trust store is not implicated. Please report this.`,\n }\n }\n\n if (\n codes.some((code) => {\n return CA_MISMATCH_CODES.has(code)\n })\n ) {\n return {\n name,\n status: 'fail',\n message: `The daemon is serving a certificate that does not chain to ${tildify(caPath)}. Its CA was regenerated \u2014 run \\`${trustCmd(bin)}\\`, or reinstall: \\`${installDaemonCmd(bin)}\\`.`,\n }\n }\n\n return { name, status: 'fail', message: `TLS handshake failed (${codes.join(', ')})` }\n}\n\n/**\n * Was `portless trust` run for the CA now on disk? Fingerprint marker only \u2014 it proves the marker was\n * written for THIS `ca.pem` and the CA has not been regenerated since. It does NOT prove the keychain still\n * trusts it: a user who deletes the certificate by hand in Keychain Access leaves the marker behind and\n * passes here. Accepted residual (see `caFingerprintMatches`); the chain check above is what proves what the\n * daemon actually serves.\n */\nconst checkPortlessCaTrusted = (trusted: boolean, caPath: string, bin: string): CheckResult => {\n const name = 'portless CA trusted'\n\n if (!trusted) {\n return {\n name,\n status: 'fail',\n message: `The portless CA is not trusted (or was regenerated). Run: \\`${trustCmd(bin)}\\` (no sudo needed).`,\n }\n }\n\n return { name, status: 'pass', message: `${tildify(caPath)} matches the recorded ca.trusted fingerprint` }\n}\n\n/**\n * Routes whose target port has nothing listening \u2014 a worktree whose runner was SIGKILLed. Newly worth\n * surfacing under HTTPS: a dead target is now an opaque 502 from the proxy rather than a connection refusal.\n */\nconst checkPortlessStaleRoutes = async (\n routes: PortlessRoute[],\n isListening: NonNullable<PortlessCheckDeps['isListening']>,\n bin: string,\n): Promise<CheckResult> => {\n const name = 'portless routes'\n\n if (routes.length === 0) return { name, status: 'pass', message: 'No portless routes registered' }\n\n const probed = await Promise.all(\n routes.map(async (route) => {\n return { route, live: await isListening(route.port) }\n }),\n )\n const stale = probed\n .filter((entry) => {\n return !entry.live\n })\n .map((entry) => {\n return entry.route.name\n })\n\n if (stale.length === 0) {\n return { name, status: 'pass', message: `${routes.length} portless route(s), all live` }\n }\n\n return {\n name,\n status: 'fail',\n message: `${stale.length} stale portless route(s): ${stale.join(', ')}. Remove with \\`${formatPortlessCommand(['alias', '--remove', '<name>'], { bin })}\\`.`,\n }\n}\n\n/**\n * The portless block of doctor: is the HTTPS dev proxy installed, serving, trusted, and free of dead routes?\n * This is the diagnostic surface for the port-free HTTPS dev URLs \u2014 it is what tells a developer to run\n * `trust` (sudo-free) or the one-time `service install` (root), and it never conflates the two. Both are\n * printed via {@link formatPortlessCommand} \u2014 as `<node> <cli.js> \u2026`, the only form that runs.\n *\n * Every check observes the daemon **on the wire or on our own disk** \u2014 never through portless's `proxy.port`\n * / `proxy.pid` / `proxy.tls` markers, which are process-global singletons that ANY daemon start rewrites and\n * ANY daemon stop deletes. Gating on them made doctor report a perfectly healthy `:443` daemon as dead.\n *\n * Reports only: nothing here is auto-run, and nothing requiring sudo ever could be.\n *\n * @example\n * await checkPortless()\n * // [{ name: 'portless installed', status: 'pass', message: '\u2026' }, \u2026 5 checks]\n */\nexport const checkPortless = async (deps: PortlessCheckDeps = {}): Promise<CheckResult[]> => {\n const resolveBin = deps.resolveBin ?? resolvePortlessBin\n const isProxyServing = deps.isProxyServing ?? defaultIsProxyServing\n const handshake = deps.handshake ?? handshakeChainsToCa\n const caTrusted = deps.caTrusted ?? caFingerprintMatches\n const readRoutes = deps.routes ?? listRoutes\n const isListening = deps.isListening ?? defaultIsListening\n const caPath = (deps.caPath ?? readCaPath)()\n // Resolved ONCE, then threaded into every remediation below: each one is rendered as `<node> <bin> \u2026`, so a\n // check that could not name the binary could not print a runnable fix either.\n const bin = resolveBin()\n\n if (bin === null) {\n return [\n {\n name: 'portless installed',\n status: 'fail',\n message: 'portless is not resolvable from node_modules \u2014 run `pnpm install`.',\n },\n ]\n }\n\n const installed: CheckResult = {\n name: 'portless installed',\n status: 'pass',\n message: 'portless is resolvable from node_modules',\n }\n const routes = readRoutes()\n const serving = await checkPortlessServing(isProxyServing, bin)\n // Nothing is answering on :443 \u2014 there is no certificate to validate, so the chain check would only add a\n // second, derivative failure to the one the user must fix first.\n const chain: CheckResult =\n serving.status === 'fail'\n ? { name: 'portless CA chain valid', status: 'pass', message: 'Skipped \u2014 no daemon to handshake with' }\n : await checkPortlessCaChain(handshake, routes, caPath, bin)\n\n return [\n installed,\n serving,\n chain,\n checkPortlessCaTrusted(caTrusted(), caPath, bin),\n await checkPortlessStaleRoutes(routes, isListening, bin),\n ]\n}\n\n/**\n * Check installation and authentication status of gh, doppler, aws, and rtk CLIs\n */\nexport const doctor = async () => {\n const baseChecks: CheckResult[] = await Promise.all([\n checkCommand(\n 'gh installed',\n ['gh', '--version'],\n 'GitHub CLI is installed',\n 'GitHub CLI is not installed. Install from: https://cli.github.com/',\n ),\n checkCommand(\n 'gh authenticated',\n ['gh', 'auth', 'status'],\n 'GitHub CLI is authenticated',\n 'GitHub CLI is not authenticated. Run: gh auth login',\n ),\n checkCommand(\n 'doppler installed',\n ['doppler', '--version'],\n 'Doppler CLI is installed',\n 'Doppler CLI is not installed. Install from: https://docs.doppler.com/docs/install-cli',\n ),\n checkCommand(\n 'doppler authenticated',\n ['doppler', 'me'],\n 'Doppler CLI is authenticated',\n 'Doppler CLI is not authenticated. Run: doppler login',\n ),\n checkCommand(\n 'aws installed',\n ['aws', '--version'],\n 'AWS CLI is installed',\n 'AWS CLI is not installed. Install from: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html',\n ),\n checkCommand(\n 'rtk installed',\n ['rtk', '--version'],\n 'RTK is installed',\n 'RTK is not installed. Install from: https://github.com/rtk-ai/rtk',\n ),\n checkCommand(\n 'typescript-language-server installed',\n ['typescript-language-server', '--version'],\n 'typescript-language-server is installed',\n 'typescript-language-server is not installed. Install from: https://github.com/typescript-language-server/typescript-language-server#installing',\n ),\n checkCommand(\n 'cmux installed',\n ['cmux', '--version'],\n 'cmux is installed',\n 'cmux is not installed. Install from: https://cmux.com/',\n ),\n checkRtkConfigured(),\n Promise.resolve(checkZshrcInitialized()),\n checkWarmCache(),\n checkPnpmWorkspaceVirtualStore(),\n checkInfraKitConfigValid(),\n checkDopplerProjectExists(),\n checkUserOverridePath(),\n checkLegacyUserGlobalConfig(),\n checkIdeInstalled(),\n ])\n\n const checks: CheckResult[] = [...baseChecks, ...(await checkPortless()), ...(await checkAgentFiles())]\n\n logger.info('Doctor check results:\\n')\n\n for (const check of checks) {\n const icon = check.status === 'pass' ? '[PASS]' : '[FAIL]'\n\n logger.info(` ${icon} ${check.name}: ${check.message}`)\n }\n\n const structuredContent = {\n checks: checks.map((c) => {\n return { name: c.name, status: c.status, message: c.message }\n }),\n allPassed: checks.every((c) => {\n return c.status === 'pass'\n }),\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\n// MCP Tool Registration\nexport const doctorMcpTool = defineMcpTool({\n name: 'doctor',\n description: 'Check installation and authentication status of gh, doppler, aws, and rtk CLIs',\n inputSchema: {},\n outputSchema: {\n checks: z\n .array(\n z.object({\n name: z.string().describe('Name of the check'),\n status: z.enum(['pass', 'fail']).describe('Check result'),\n message: z.string().describe('Details about the check result'),\n }),\n )\n .describe('List of all check results'),\n allPassed: z.boolean().describe('Whether all checks passed'),\n },\n handler: doctor,\n})\n", "import fs from 'node:fs'\nimport path from 'node:path'\n\nimport { getInfraKitConfigPaths } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { hasManagedBlock, removeManagedBlock, upsertManagedBlock } from 'src/lib/managed-block'\n\nimport packageJson from '../../../package.json' with { type: 'json' }\n\n/** Markers for the generated guidance block (HTML comments \u2014 invisible in rendered markdown). */\nexport const AGENTS_MARKER_START = '<!-- infra-kit:begin -->'\nexport const AGENTS_MARKER_END = '<!-- infra-kit:end -->'\n/** The version line lives on its own line (OMC `OMC:VERSION:` precedent) so the markers stay constant-matchable. */\nconst AGENTS_VERSION_PREFIX = '<!-- infra-kit:version '\n\n/**\n * Marker pair for the legacy `@AGENTS.md` import region once injected into CLAUDE.md.\n * Migration-only now: `writeAgentFiles` strips this region so the full guidance body\n * can take its place. TODO(remove after ~2 release cycles once repos have re-run init).\n */\nexport const AGENTS_IMPORT_START = '<!-- infra-kit:import:begin -->'\nexport const AGENTS_IMPORT_END = '<!-- infra-kit:import:end -->'\n\nconst AGENTS_FILE = 'AGENTS.md'\nconst CLAUDE_FILE = 'CLAUDE.md'\n\nexport type WriteAction = 'created' | 'updated' | 'unchanged' | 'removed'\n\nexport interface AgentFileWrite {\n path: string\n action: WriteAction\n}\n\nexport interface WriteAgentFilesResult {\n /** True when run outside an infra-kit repo (no `infra-kit.json` at the git root). */\n skipped: boolean\n /** Repo root the files were written under, or null when skipped. */\n root: string | null\n written: AgentFileWrite[]\n}\n\n/** The version comment line, e.g. `<!-- infra-kit:version 0.1.105 -->`. */\nconst buildVersionLine = (version: string): string => {\n return `${AGENTS_VERSION_PREFIX}${version} -->`\n}\n\n/**\n * The generated guidance body (without the surrounding markers). Describes the\n * infra-kit CLI surface so any AI agent working in the repo learns it. Curated\n * from the real command set; the version line records which CLI version generated it.\n */\nconst buildAgentsBody = (version: string): string => {\n return [\n buildVersionLine(version),\n '',\n '# infra-kit',\n '',\n 'This repository uses the **infra-kit** CLI for environment, worktree, and release workflows.',\n 'This block is generated by `infra-kit init` \u2014 edit text *outside* the markers, never inside.',\n '',\n '## Commands (`ik` = `pnpm exec infra-kit`)',\n '',\n '- `ik env-load -c <config>` / `ik env-clear` / `ik env-status` \u2014 load, clear, or inspect Doppler env vars for a config (e.g. `dev`). Source the returned file to apply.',\n '- `ik worktrees add` / `worktrees list` / `worktrees reload` / `worktrees remove` / `worktrees sync` \u2014 manage release and feature git worktrees.',\n '- `ik release create` / `release list` / `release deploy-all` / `release deploy-selected` / `release deliver` / `release desc-edit` \u2014 release-branch and deploy flow.',\n '- `ik release merge-dev` \u2014 merge the dev branch into every release branch.',\n '- `ik audit` \u2014 audit packages against `infra-kit.config.ts` rules.',\n '- `ik doctor` \u2014 check gh / doppler / aws / rtk install + auth and repo setup.',\n '- `ik init` \u2014 (re)install shell integration and regenerate these agent-instruction files.',\n '',\n '## Conventions',\n '',\n '- Tickets are prefixed by area: `[FE]` frontend, `[BE]` backend, `[DO]` DevOps/infra, `[APP]` mobile app, `[IDEA]` proposal, `[ROOT]` cross-cutting. Pick the single dominant area.',\n '- Environment variables are managed through Doppler via `ik env-load` \u2014 never commit secrets.',\n ].join('\\n')\n}\n\nconst assertNotSymlink = (filePath: string): void => {\n if (fs.existsSync(filePath) && fs.lstatSync(filePath).isSymbolicLink()) {\n throw new Error(`Refusing to write ${filePath} because the destination is a symlink`)\n }\n}\n\n/** Copy a file to a timestamped `<file>.backup.<timestamp>` sibling. */\nconst backupFile = (filePath: string): void => {\n const stamp = new Date().toISOString().replace(/[:.]/g, '-')\n\n fs.copyFileSync(filePath, `${filePath}.backup.${stamp}`)\n}\n\n/**\n * Write `nextContent` to `filePath` with OMC-style safety: refuse symlinks,\n * back up the prior file with a timestamp before overwriting, and skip the\n * write entirely when nothing changed (keeps re-runs churn-free).\n */\nconst writeManaged = (filePath: string, nextContent: string): WriteAction => {\n assertNotSymlink(filePath)\n\n const existed = fs.existsSync(filePath)\n const previous = existed ? fs.readFileSync(filePath, 'utf-8') : null\n\n if (previous === nextContent) return 'unchanged'\n\n if (existed) backupFile(filePath)\n\n fs.mkdirSync(path.dirname(filePath), { recursive: true })\n fs.writeFileSync(filePath, nextContent, 'utf-8')\n\n return existed ? 'updated' : 'created'\n}\n\nconst readOr = (filePath: string, fallback: string): string => {\n return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : fallback\n}\n\nconst assertBlockPresent = (filePath: string, start: string, end: string): void => {\n const content = fs.readFileSync(filePath, 'utf-8')\n\n if (!hasManagedBlock(content, start, end)) {\n throw new Error(`Post-write validation failed: managed block missing from ${filePath}`)\n }\n}\n\n/**\n * Migrate a legacy `AGENTS.md` now that the guidance lives solely in `CLAUDE.md`.\n * Strips the infra-kit managed block, then asymmetrically:\n * - no infra-kit block at all (hand-authored, not ours) \u2192 leave untouched (`unchanged`),\n * - file was purely generated (nothing but whitespace remains) \u2192 back up + delete (`removed`),\n * - hand-authored content surrounds the block \u2192 back up + write the block-free remainder (`updated`).\n * Every destructive path leaves a timestamped `.backup.` first.\n */\nconst migrateLegacyAgentsFile = (agentsPath: string): WriteAction => {\n if (!fs.existsSync(agentsPath)) return 'unchanged'\n\n assertNotSymlink(agentsPath)\n\n const content = fs.readFileSync(agentsPath, 'utf-8')\n const stripped = removeManagedBlock(content, AGENTS_MARKER_START, AGENTS_MARKER_END)\n\n if (stripped === null) return 'unchanged'\n\n if (stripped.trim() === '') {\n backupFile(agentsPath)\n fs.rmSync(agentsPath)\n\n return 'removed'\n }\n\n return writeManaged(agentsPath, stripped)\n}\n\n/**\n * Generate (or refresh) the repo agent-instruction guidance, now hosted solely in\n * `CLAUDE.md` (preserving hand-authored content outside the markers). Also migrates\n * legacy setups \u2014 stripping the old `@AGENTS.md` import region from `CLAUDE.md` and\n * backing up/removing a now-redundant `AGENTS.md`. Repo-gated: a no-op outside an\n * infra-kit repo. Idempotent (within a CLI version) and non-destructive.\n *\n * @example\n * await writeAgentFiles()\n * // INFO: Agent-instruction files synced (infra-kit 0.1.105)\n */\nexport const writeAgentFiles = async (): Promise<WriteAgentFilesResult> => {\n let mainConfigPath: string\n\n try {\n mainConfigPath = (await getInfraKitConfigPaths()).main\n } catch {\n logger.info('Skipped agent-instruction files \u2014 not inside an infra-kit repo')\n\n return { skipped: true, root: null, written: [] }\n }\n\n if (!fs.existsSync(mainConfigPath)) {\n logger.info('Skipped agent-instruction files \u2014 no infra-kit.json at the repo root')\n\n return { skipped: true, root: null, written: [] }\n }\n\n const root = path.dirname(mainConfigPath)\n const version = packageJson.version\n const body = buildAgentsBody(version)\n\n const agentsPath = path.join(root, AGENTS_FILE)\n const claudePath = path.join(root, CLAUDE_FILE)\n\n // CLAUDE.md now hosts the full guidance body. Strip the legacy `@AGENTS.md`\n // import region first (if present), then upsert the body under the begin/end\n // markers \u2014 disjoint marker pairs, so the two operations never collide.\n let claudeContent = readOr(claudePath, '')\n\n claudeContent = removeManagedBlock(claudeContent, AGENTS_IMPORT_START, AGENTS_IMPORT_END) ?? claudeContent\n claudeContent = upsertManagedBlock({\n content: claudeContent,\n body,\n startMarker: AGENTS_MARKER_START,\n endMarker: AGENTS_MARKER_END,\n placement: 'replace-in-place',\n })\n\n // CLAUDE.md is the only file we manage. A legacy AGENTS.md is migrated away.\n const written: AgentFileWrite[] = [\n { path: claudePath, action: writeManaged(claudePath, claudeContent) },\n { path: agentsPath, action: migrateLegacyAgentsFile(agentsPath) },\n ]\n\n assertBlockPresent(claudePath, AGENTS_MARKER_START, AGENTS_MARKER_END)\n\n for (const file of written) {\n // Always report CLAUDE.md; only mention a migrated file when it actually changed.\n if (file.action === 'unchanged' && file.path !== claudePath) continue\n\n logger.info(` ${file.action.padEnd(9)} ${path.relative(root, file.path)}`)\n }\n\n logger.info(`Agent-instruction files synced (infra-kit ${version})`)\n\n return { skipped: false, root, written }\n}\n", "/**\n * Generic \"managed block\" helpers \u2014 a marker-delimited region a tool owns and\n * rewrites idempotently while never touching content outside the markers.\n *\n * This is the same mechanism `infra-kit init` already uses for the `~/.zshrc`\n * shell block (`# -- infra-kit:begin -- \u2026 # -- infra-kit:end --`), lifted here so\n * it can be reused for the repo agent-instruction guidance block in `CLAUDE.md`.\n * It mirrors the design of OMC's\n * `<!-- OMC:START --> \u2026 <!-- OMC:END -->` CLAUDE.md installer.\n */\n\n/**\n * Where a freshly upserted block lands relative to existing content.\n *\n * - `replace-in-place`: if the block already exists, rewrite it where it sits\n * (surrounding text untouched, verbatim); if absent, append it at end-of-file.\n * - `append-end`: strip any existing block from wherever it is, then append the\n * fresh block at end-of-file (i.e. always relocate to the end). This preserves\n * the historical `~/.zshrc` behavior of `removeExistingBlock` + append.\n */\nexport type BlockPlacement = 'replace-in-place' | 'append-end'\n\nexport interface UpsertManagedBlockArgs {\n /** Existing file content (`''` for a new file). */\n content: string\n /** Inner body to place between the markers (WITHOUT the markers). */\n body: string\n startMarker: string\n endMarker: string\n /** Defaults to `replace-in-place`. */\n placement?: BlockPlacement\n}\n\n/**\n * Whether `content` contains a well-formed `start \u2026 end` block. A reversed pair\n * (end before start) is treated as absent \u2014 the same guard `doctor.ts` applies\n * to the zshrc block, ported here so corrupted markers never match.\n *\n * @example\n * hasManagedBlock('a<!--s-->x<!--e-->b', '<!--s-->', '<!--e-->') // => true\n * hasManagedBlock('<!--e--><!--s-->', '<!--s-->', '<!--e-->') // => false (reversed)\n */\nexport const hasManagedBlock = (content: string, startMarker: string, endMarker: string): boolean => {\n const startIdx = content.indexOf(startMarker)\n const endIdx = content.indexOf(endMarker)\n\n return startIdx !== -1 && endIdx !== -1 && endIdx >= startIdx\n}\n\n/**\n * Remove the first complete `start \u2026 end` block, preserving surrounding text.\n * Returns `null` when no well-formed block is present (no markers, or reversed),\n * so callers can fall through to legacy-format handling \u2014 this matches the\n * `string | null` contract of `init.ts`'s original `removeBetween`, with the\n * added reversed-marker guard.\n *\n * @example\n * removeManagedBlock('top\\n<!--s-->mid<!--e-->\\nbot', '<!--s-->', '<!--e-->')\n * // => 'top\\nbot'\n */\nexport const removeManagedBlock = (content: string, startMarker: string, endMarker: string): string | null => {\n const startIdx = content.indexOf(startMarker)\n const endIdx = content.indexOf(endMarker)\n\n if (startIdx === -1 || endIdx === -1 || endIdx < startIdx) return null\n\n // eslint-disable-next-line sonarjs/super-linear-regex\n const before = content.slice(0, startIdx).replace(/\\n+$/, '')\n const after = content.slice(endIdx + endMarker.length).replace(/^\\n+/, '')\n\n return before + (after ? `\\n${after}` : '')\n}\n\n/**\n * Read the version token that follows `versionPrefix` (e.g.\n * `'<!-- infra-kit:version '`). Returns the token up to the next whitespace or\n * `>`, or `null` if the prefix is absent. Mirrors OMC's `OMC:VERSION:` line.\n *\n * @example\n * extractVersion('<!-- infra-kit:version 0.1.105 -->', '<!-- infra-kit:version ')\n * // => '0.1.105'\n */\nexport const extractVersion = (content: string, versionPrefix: string): string | null => {\n const idx = content.indexOf(versionPrefix)\n\n if (idx === -1) return null\n\n const rest = content.slice(idx + versionPrefix.length)\n const match = rest.match(/^([^\\s>]+)/)\n\n return match ? match[1]! : null\n}\n\n/**\n * Compose a full block string: `start\\n{body}\\nend`. Kept identical in shape to\n * the historical `buildShellBlock()` output so doctor's exact-match comparison\n * stays valid.\n */\nexport const buildManagedBlock = (startMarker: string, body: string, endMarker: string): string => {\n return `${startMarker}\\n${body}\\n${endMarker}`\n}\n\n/**\n * Insert or update a managed block in `content`, preserving everything outside\n * the markers. Idempotent: re-running with the same body yields the same block\n * and never nests duplicates.\n *\n * @example\n * // fresh file\n * upsertManagedBlock({ content: '', body: 'hi', startMarker: '<!--s-->', endMarker: '<!--e-->' })\n * // => '<!--s-->\\nhi\\n<!--e-->\\n'\n *\n * @example\n * // existing block replaced in place, surrounding text kept\n * upsertManagedBlock({\n * content: 'top\\n<!--s-->\\nold\\n<!--e-->\\nbot',\n * body: 'new', startMarker: '<!--s-->', endMarker: '<!--e-->',\n * })\n * // => 'top\\n<!--s-->\\nnew\\n<!--e-->\\nbot'\n */\nexport const upsertManagedBlock = ({\n content,\n body,\n startMarker,\n endMarker,\n placement = 'replace-in-place',\n}: UpsertManagedBlockArgs): string => {\n const block = buildManagedBlock(startMarker, body, endMarker)\n const present = hasManagedBlock(content, startMarker, endMarker)\n\n if (placement === 'replace-in-place' && present) {\n const startIdx = content.indexOf(startMarker)\n const endIdx = content.indexOf(endMarker) + endMarker.length\n\n return content.slice(0, startIdx) + block + content.slice(endIdx)\n }\n\n // append-end, or replace-in-place with no existing block: drop any stale block\n // then append the fresh one at end-of-file.\n const stripped = present ? (removeManagedBlock(content, startMarker, endMarker) ?? content) : content\n // eslint-disable-next-line sonarjs/super-linear-regex\n const base = stripped.replace(/\\n+$/, '')\n\n return base.length > 0 ? `${base}\\n${block}\\n` : `${block}\\n`\n}\n", "import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport { seedCreatedMessage, seedUserProjectConfig } from 'src/lib/config-bootstrap'\nimport { CONFIG_STUB, buildUserGlobalExample, buildVendorExample } from 'src/lib/config-templates'\nimport { getInfraKitConfigPaths } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { removeManagedBlock, upsertManagedBlock } from 'src/lib/managed-block'\n\nimport { writeAgentFiles } from './agent-files'\nimport {\n migrateFactoryConfigToJson,\n migrateLegacyConfig,\n migrateUserGlobalConfigFilename,\n normalizeLegacyIdeStructures,\n} from './migrate-config'\n\nexport const MARKER_START = '# -- infra-kit:begin --'\nexport const MARKER_END = '# -- infra-kit:end --'\n\nconst LEGACY_PAIRED: [start: string, end: string][] = [['# region infra-kit', '# endregion infra-kit']]\nconst LEGACY_SINGLE = '# infra-kit shell functions'\n\n/**\n * Append infra-kit shell functions to .zshrc, migrate any legacy `infra-kit.yml`\n * config layers to JSON, normalize existing JSON configs from the old IDE structure\n * (strip the removed `ide.config.mode`), convert a legacy factory `vendor.config.ts`\n * to `vendor.json`, and seed the user-global config at ~/.infra-kit/infra-kit.json on\n * first run. Idempotent: a subsequent run replaces the existing zshrc block in place,\n * has nothing left to migrate or normalize, and leaves the REAL config files\n * (infra-kit.json / vendor.json) untouched \u2014 but the annotated `.example.jsonc`\n * reference files are refreshed on every run so they always reflect the current schema.\n *\n * @example\n * // CLI: `infra-kit init` (or via the `pnpm dx-init` alias)\n * // INFO: Added infra-kit shell functions to /Users/me/.zshrc\n * // INFO: Wrote user-global config to /Users/me/.infra-kit/infra-kit.json (see \u2026/infra-kit.example.jsonc \u2026)\n * // INFO: Run `source ~/.zshrc` or open a new terminal to activate.\n */\nexport const init = async (): Promise<void> => {\n const zshrcPath = path.join(os.homedir(), '.zshrc')\n\n // Strip any prior block (current or legacy markers) anywhere in the file, then\n // append a fresh block at end-of-file via the shared managed-block utility \u2014\n // the historical `removeExistingBlock` + append behavior, now centralized.\n const existing = fs.existsSync(zshrcPath) ? removeExistingBlock(fs.readFileSync(zshrcPath, 'utf-8')) : ''\n\n const updated = upsertManagedBlock({\n content: existing,\n body: buildShellBody(),\n startMarker: MARKER_START,\n endMarker: MARKER_END,\n placement: 'append-end',\n })\n\n fs.writeFileSync(zshrcPath, updated)\n\n logger.info(`Added infra-kit shell functions to ${zshrcPath}`)\n\n // Convert any legacy infra-kit.yml config layers to JSON before seeding, so a\n // migrated infra-kit.json is not re-seeded as an empty stub.\n await migrateLegacyConfig()\n\n // Migrate existing JSON configs from the old IDE structure to the new one\n // (strip the removed `ide.config.mode` field). No-op for already-clean configs.\n await normalizeLegacyIdeStructures()\n\n // Rename a legacy user-global config.json \u2192 infra-kit.json (single canonical\n // filename). MUST run before seeding: otherwise the seeder checks the new name,\n // doesn't find it, and writes an empty stub that shadows the user's real config.\n await migrateUserGlobalConfigFilename()\n\n // Convert a legacy machine-local factory config from executable TS\n // (~/.infra-kit/vendor.config.ts) to static JSON (~/.infra-kit/vendor.json).\n // Independent of the infra-kit.json layers; grouped with the other migrations.\n await migrateFactoryConfigToJson()\n\n seedUserGlobalConfig()\n\n // Best-effort, non-fatal, repo-gated: keep the agent-instruction files in sync\n // with the CLI surface. A no-op outside an infra-kit repo.\n await writeAgentFiles()\n\n // Close the legacy-yml migration gap so a single `dx-init` leaves EVERY example current.\n await reseedUserProjectConfig()\n\n // The shell integration is zsh-only (zmodload zsh/stat, add-zsh-hook, ${dir:h}).\n // Warn non-fatally if the user's login shell isn't zsh so the block isn't a\n // silent no-op for them.\n const shell = process.env.SHELL ?? ''\n\n if (!shell.includes('zsh')) {\n logger.warn(\n `Your login shell ($SHELL=${shell || 'unset'}) is not zsh. The infra-kit shell integration (env-load/env-clear/auto-load) is zsh-only and won't activate in bash/fish.`,\n )\n }\n\n logger.info('Run `source ~/.zshrc` or open a new terminal to activate.')\n}\n\n/**\n * Seed the user-global config on first run and (re)write the annotated reference\n * files. The real `~/.infra-kit/infra-kit.json` (empty `{}`) is written only when\n * absent so user edits are preserved. The non-loaded `.example.jsonc` reference\n * files (`infra-kit.example.jsonc` + `vendor.example.jsonc`) are rewritten on EVERY\n * run so existing users always get the current, complete schema documentation.\n *\n * Deliberately seeds NO real `vendor.json` \u2014 that is scaffolded by\n * `infra-kit vendor config --init` (a stub here would block `--init`, trip the\n * factory migration's no-overwrite guard, and fail the schema's `targets.min(1)`).\n *\n * @example\n * seedUserGlobalConfig()\n * // first call: writes ~/.infra-kit/infra-kit.json ({}) + both .example.jsonc files\n * // later calls: leaves infra-kit.json alone, refreshes both .example.jsonc files\n */\nexport const seedUserGlobalConfig = (): void => {\n const userConfigDir = path.join(os.homedir(), '.infra-kit')\n const userConfigPath = path.join(userConfigDir, 'infra-kit.json')\n\n fs.mkdirSync(userConfigDir, { recursive: true })\n\n // Reference examples are non-loaded docs \u2014 always refresh them so re-running init\n // delivers the current schema (and the newly-added vendor example) to existing users.\n fs.writeFileSync(path.join(userConfigDir, 'infra-kit.example.jsonc'), buildUserGlobalExample(), 'utf-8')\n fs.writeFileSync(path.join(userConfigDir, 'vendor.example.jsonc'), buildVendorExample(), 'utf-8')\n\n if (fs.existsSync(userConfigPath)) {\n logger.info(`User-global config already present at ${userConfigPath} (refreshed reference examples)`)\n\n return\n }\n\n fs.writeFileSync(userConfigPath, CONFIG_STUB, 'utf-8')\n\n logger.info(`Wrote user-global config to ${userConfigPath} (see the sibling .example.jsonc files for reference)`)\n}\n\n/**\n * Re-run the layer-3 seed at the END of `init()`, closing the legacy-yml migration gap.\n *\n * The per-command seed lives in the program's preAction hook, which evaluates its \"is this an\n * infra-kit repo?\" gate (does `<repo-root>/infra-kit.json` exist?) BEFORE the action body runs. On\n * the legacy-yml migration path `init()` itself CREATES that file (`migrateLegacyConfig`), so the\n * gate had already declined and this very run would otherwise leave the layer-3 example unwritten \u2014\n * self-healing only on the user's next command. Re-running the gate here makes one `dx-init` enough.\n *\n * The UNGATED primitive, deliberately: `ensureUserProjectConfig()` is an ENTRY-BOUNDARY primitive\n * whose once-per-process guard has ALREADY fired in preAction, so calling it here would be a silent\n * no-op. It is unusable as a re-seed hook. Anything else (no git repo, no project config, an\n * unwritable `$HOME`) degrades to a debug line \u2014 seeding a reference file must never fail `init`.\n *\n * Separate from {@link seedUserGlobalConfig}, which keeps its layer-2-only duty and stays sync.\n *\n * Honours `INFRA_KIT_NO_SEED` itself: the primitive is ungated by design, so without this check the\n * kill switch would be airtight on every path EXCEPT `init`.\n *\n * @example\n * await reseedUserProjectConfig()\n * // after a legacy infra-kit.yml migration:\n * // INFO: Created ~/.infra-kit/projects/api/infra-kit.json \u2014 see \u2026/infra-kit.example.jsonc \u2026\n */\nconst reseedUserProjectConfig = async (): Promise<void> => {\n if (process.env.INFRA_KIT_NO_SEED) return\n\n try {\n const paths = await getInfraKitConfigPaths()\n\n // The same D2 gate the preAction seed applies \u2014 re-evaluated now that the migrations have run.\n if (!fs.existsSync(paths.main)) return\n\n const result = await seedUserProjectConfig(paths)\n\n if (result.createdConfig) {\n logger.info(seedCreatedMessage(result))\n }\n } catch (err) {\n logger.debug({ err, msg: 'Skipped seeding the user-project config (init).' })\n }\n}\n\nconst isBlockLine = (line: string): boolean => {\n return (\n line.startsWith('#') ||\n line.startsWith('alias ') ||\n line.startsWith('env-load') ||\n line.startsWith('env-clear') ||\n line.startsWith('env-status') ||\n line.startsWith('if ') ||\n line.startsWith(' export INFRA_KIT_SESSION') ||\n line.startsWith('export _INFRA_KIT_') ||\n line.startsWith(': ${_INFRA_KIT_') ||\n line.startsWith('fi') ||\n line.startsWith('zmodload ') ||\n line.startsWith('autoload ') ||\n line.startsWith('add-zsh-hook ') ||\n line.startsWith('_infra_kit_autoload')\n )\n}\n\nconst removeExistingBlock = (content: string): string => {\n // 1. Current markers\n const result = removeManagedBlock(content, MARKER_START, MARKER_END)\n\n if (result !== null) return result\n\n // 2. Legacy paired markers (# region / # endregion)\n for (const [start, end] of LEGACY_PAIRED) {\n const legacyResult = removeManagedBlock(content, start, end)\n\n if (legacyResult !== null) return legacyResult\n }\n\n // 3. Oldest format: single marker + heuristic scan\n const legacyIdx = content.indexOf(LEGACY_SINGLE)\n\n if (legacyIdx === -1) return content\n\n // eslint-disable-next-line sonarjs/super-linear-regex\n const before = content.slice(0, legacyIdx).replace(/\\n+$/, '')\n const afterLines = content.slice(legacyIdx).split('\\n')\n\n let i = 0\n\n while (i < afterLines.length && isBlockLine(afterLines[i]!)) {\n i++\n }\n\n const remaining = afterLines.slice(i).join('\\n')\n\n return before + (remaining ? `\\n${remaining}` : '')\n}\n\n/**\n * The inner shell-function lines (no markers). Composed into the full marked\n * block by {@link buildShellBlock} and fed to `upsertManagedBlock` by `init()`.\n */\nexport const buildShellBody = (): string => {\n const runCmd = 'pnpm exec infra-kit'\n\n return [\n 'zmodload zsh/stat 2>/dev/null',\n 'zmodload zsh/datetime 2>/dev/null',\n 'zmodload zsh/sched 2>/dev/null',\n // eslint-disable-next-line no-template-curly-in-string\n 'if [[ -z \"${INFRA_KIT_SESSION}\" ]]; then',\n ' export INFRA_KIT_SESSION=$(head -c 4 /dev/urandom | xxd -p)',\n 'fi',\n // eslint-disable-next-line no-template-curly-in-string\n ': ${_INFRA_KIT_LAST_LOAD_MTIME:=0}',\n // eslint-disable-next-line no-template-curly-in-string\n ': ${_INFRA_KIT_LAST_CLEAR_MTIME:=0}',\n // eslint-disable-next-line no-template-curly-in-string\n ': ${_INFRA_KIT_SHELL_STARTED:=${EPOCHSECONDS:-0}}',\n // Warm-cache source TTL (seconds); MUST equal DEFAULT_WARM_TTL_SECONDS (node\n // eviction) \u2014 both 2h. A warm file older than this is not sourced at startup.\n // eslint-disable-next-line no-template-curly-in-string\n ': ${_INFRA_KIT_WARM_TTL:=7200}',\n 'export _INFRA_KIT_LAST_LOAD_MTIME _INFRA_KIT_LAST_CLEAR_MTIME _INFRA_KIT_SHELL_STARTED _INFRA_KIT_WARM_TTL',\n `env-load() { local f m; f=$(${runCmd} env-load \"$@\") || return; m=$(zstat +mtime -- \"$f\" 2>/dev/null || echo 0); _INFRA_KIT_LAST_LOAD_MTIME=$m; source \"$f\"; ${runCmd} env-status; }`,\n `env-clear() { local f m; f=$(${runCmd} env-clear \"$@\") || return; m=$(zstat +mtime -- \"$f\" 2>/dev/null || echo 0); _INFRA_KIT_LAST_CLEAR_MTIME=$m; source \"$f\"; ${runCmd} env-status; }`,\n `env-status() { ${runCmd} env-status; }`,\n // No `alias ik=\u2026` here: a global install provides real `infra-kit` and `ik` bins, and an alias\n // would SHADOW the global `ik`, forcing the project-local `pnpm exec` even when a global CLI exists.\n // (`isBlockLine` still lists `alias ` so re-running `init` strips the stale alias from old configs.)\n // Print an async notice without corrupting an already-drawn prompt. The\n // startup poll fires via `sched` at an IDLE prompt (ZLE active) which does\n // NOT redraw the prompt, so a bare `print` lands appended to the visible\n // prompt line and looks like un-removable typed input. When ZLE is active we\n // erase the current line (CR + clear-to-EOL), print the message on its own\n // line, then `zle reset-prompt` to redraw the prompt below it. In precmd /\n // shell-startup (ZLE inactive) `zle` is false and this is a plain stderr\n // print above the about-to-be-drawn prompt \u2014 unchanged behavior.\n '_infra_kit_notify() {',\n // When ZLE is active, erase the current prompt line first and redraw after;\n // both are no-ops when ZLE is inactive so this stays a plain stderr print.\n \" zle && print -u2 -n $'\\\\r\\\\e[K'\",\n ' print -u2 -- \"$1\"',\n ' zle && zle reset-prompt',\n '}',\n '_infra_kit_autoload() {',\n ' [[ -z \"$INFRA_KIT_SESSION\" ]] && return',\n // eslint-disable-next-line no-template-curly-in-string\n ' local cache_root=\"${XDG_CACHE_HOME:-$HOME/.cache}/infra-kit\"',\n ' local dir=\"$cache_root/$INFRA_KIT_SESSION\"',\n ' local load_file=\"$dir/env-load.sh\"',\n ' local clear_file=\"$dir/env-clear.sh\"',\n ' local load_mtime=0 clear_mtime=0',\n ' [[ -f \"$load_file\" ]] && load_mtime=$(zstat +mtime -- \"$load_file\" 2>/dev/null || echo 0)',\n ' [[ -f \"$clear_file\" ]] && clear_mtime=$(zstat +mtime -- \"$clear_file\" 2>/dev/null || echo 0)',\n ' if (( load_mtime > _INFRA_KIT_LAST_LOAD_MTIME && load_mtime >= _INFRA_KIT_SHELL_STARTED && load_mtime >= clear_mtime )); then',\n ' source \"$load_file\"',\n ' _INFRA_KIT_LAST_LOAD_MTIME=$load_mtime',\n // eslint-disable-next-line no-template-curly-in-string\n ' _infra_kit_notify \"infra-kit: auto-loaded vars for ${INFRA_KIT_ENV_CONFIG:-?}\"',\n ' fi',\n ' if (( clear_mtime > _INFRA_KIT_LAST_CLEAR_MTIME && clear_mtime >= _INFRA_KIT_SHELL_STARTED && clear_mtime > load_mtime )); then',\n ' source \"$clear_file\"',\n ' _INFRA_KIT_LAST_CLEAR_MTIME=$clear_mtime',\n ' _infra_kit_notify \"infra-kit: auto-cleared env\"',\n ' fi',\n '}',\n 'autoload -Uz add-zsh-hook',\n 'if (( _INFRA_KIT_SHELL_STARTED > 0 )); then',\n ' add-zsh-hook precmd _infra_kit_autoload',\n 'fi',\n // Non-blocking startup poll: the shell-startup spawn (below) writes env-load.sh\n // asynchronously (node + Doppler fetch, ~0.5-2s), so the FIRST precmd \u2014 which\n // fires before the first prompt \u2014 runs before the file lands and sources nothing;\n // without this the vars only appear after the user's first command. This one-shot\n // re-arming poll re-runs the precmd sourcer once per second, at the idle prompt\n // with NO user command, until it sources the freshly written file (its mtime is\n // >= _INFRA_KIT_SHELL_STARTED so the existing gate accepts it) OR the startup\n // window closes. _infra_kit_autoload sets _INFRA_KIT_LAST_LOAD_MTIME on a\n // successful source, which lifts it to >= _INFRA_KIT_SHELL_STARTED and stops the\n // re-arm. Bounded to WINDOW seconds so an unconfigured project / down Doppler\n // that never writes a file only costs a handful of no-op ticks, then falls back\n // to the precmd hook. Sources the same freshly written file as precmd, so there\n // is no stale/last-known-good secrets window.\n '_INFRA_KIT_AUTOLOAD_WINDOW=6',\n 'export _INFRA_KIT_AUTOLOAD_WINDOW',\n '_infra_kit_poll_autoload() {',\n ' _infra_kit_autoload',\n // eslint-disable-next-line no-template-curly-in-string\n ' if (( _INFRA_KIT_LAST_LOAD_MTIME < _INFRA_KIT_SHELL_STARTED )) && (( ${EPOCHSECONDS:-0} - _INFRA_KIT_SHELL_STARTED < _INFRA_KIT_AUTOLOAD_WINDOW )); then',\n ' (( $+builtins[sched] )) && sched +1 _infra_kit_poll_autoload',\n ' fi',\n '}',\n // Warm cache: source the project-scoped last-known-good env-load.sh INSTANTLY at\n // startup (pure zsh, no node/Doppler) so vars are present at prompt-0, before the\n // backgrounded refresh lands. `key` is sha256 of the canonical project dir passed\n // in by the caller \u2014 byte-identical to node's warmCacheKey (printf %s | shasum,\n // full 64-hex). Skips when: no sha tool (degrade to poll), no warm file, a project\n // clear-marker is at least as new (clear wins ties), or the file is past TTL\n // (bounds a rotated secret served at prompt-0). Deliberately does NOT set\n // _INFRA_KIT_LAST_LOAD_MTIME (leaves it 0) so the poll/precmd still source the\n // fresh SESSION file (different path, newer mtime) and upgrade warm->fresh.\n '_infra_kit_warm_source() {',\n ' local canon=\"$1\" key',\n ' if (( $+commands[shasum] )); then',\n ' key=$(printf %s \"$canon\" | shasum -a 256 | cut -c1-64)',\n ' elif (( $+commands[sha256sum] )); then',\n ' key=$(printf %s \"$canon\" | sha256sum | cut -c1-64)',\n ' else',\n ' return',\n ' fi',\n // eslint-disable-next-line no-template-curly-in-string\n ' local cache_root=\"${XDG_CACHE_HOME:-$HOME/.cache}/infra-kit\"',\n ' local warm=\"$cache_root/projects/$key/env-load.sh\"',\n ' local wclear=\"$cache_root/projects/$key/env-clear.sh\"',\n ' [[ -f \"$warm\" ]] || return',\n ' local wm=0 cm=0',\n ' wm=$(zstat +mtime -- \"$warm\" 2>/dev/null || echo 0)',\n ' [[ -f \"$wclear\" ]] && cm=$(zstat +mtime -- \"$wclear\" 2>/dev/null || echo 0)',\n ' (( cm >= wm )) && return',\n // eslint-disable-next-line no-template-curly-in-string\n ' (( ${EPOCHSECONDS:-0} - wm >= _INFRA_KIT_WARM_TTL )) && return',\n ' source \"$warm\"',\n // eslint-disable-next-line no-template-curly-in-string\n ' _infra_kit_notify \"infra-kit: loaded cached vars for ${INFRA_KIT_ENV_CONFIG:-?} (refreshing\u2026)\"',\n '}',\n // One-shot env auto-load when a NEW shell opens inside an infra-kit project\n // or worktree (config: envAutoLoad.trigger \"shell-startup\"). Cheap pure-zsh\n // project gate (walk up for infra-kit.json) avoids spawning node in unrelated\n // shells; the spawn is skipped only when the already-loaded env belongs to THIS\n // project root (INFRA_KIT_ENV_PROJECT_ROOT), so cd'ing into a DIFFERENT project\n // re-loads instead of silently keeping the previous project's secrets. The spawn\n // is DETACHED+backgrounded ( ... & ) so it never blocks the prompt; it only\n // WRITES env-load.sh. The startup poll armed right after (and the precmd hook as\n // a later-prompt fallback) sources the file once it lands. Trade-off: the gate\n // can't read the merged JSON config, so a fresh shell in a project WITHOUT\n // envAutoLoad still spawns one background process that resolves config, finds\n // nothing, and exits.\n '_infra_kit_startup_autoload() {',\n ' [[ -z \"$INFRA_KIT_SESSION\" ]] && return',\n ' local dir=\"$PWD\" prev=\"\"',\n ' while [[ -n \"$dir\" && \"$dir\" != \"$prev\" ]]; do',\n ' if [[ -f \"$dir/infra-kit.json\" ]]; then',\n ' [[ -n \"$INFRA_KIT_ENV_CONFIG\" && \"$dir\" == \"$INFRA_KIT_ENV_PROJECT_ROOT\" ]] && return',\n // canon = realpath of the project dir; the SAME string node hashes for the warm\n // key (passed via --project-dir), so the two keys are byte-identical.\n // eslint-disable-next-line no-template-curly-in-string\n ' local canon=\"${dir:A}\"',\n ' _infra_kit_warm_source \"$canon\"',\n ` ( ${runCmd} env-autoload --project-dir \"$canon\" & ) >/dev/null 2>&1`,\n ' (( $+builtins[sched] )) && sched +1 _infra_kit_poll_autoload',\n ' return',\n ' fi',\n ' prev=\"$dir\"',\n // eslint-disable-next-line no-template-curly-in-string\n ' dir=\"${dir:h}\"',\n ' done',\n '}',\n 'if (( _INFRA_KIT_SHELL_STARTED > 0 )); then',\n ' _infra_kit_startup_autoload',\n 'fi',\n ].join('\\n')\n}\n\n/**\n * The full marker-delimited shell block (`MARKER_START \u2026 MARKER_END`). Kept as\n * a single composed string so `doctor`'s exact-match freshness check stays valid.\n */\nexport const buildShellBlock = (): string => {\n return `${MARKER_START}\\n${buildShellBody()}\\n${MARKER_END}`\n}\n", "// DEFAULT import, deliberately: the module is used as `fs.writeFile(...)` / `fs.mkdir(...)` so every\n// call is a property lookup on the live module object, which is what `vi.spyOn(fs, 'writeFile')`\n// patches. A named import (`import { writeFile }`) is bound at import time and the spy silently does\n// NOT intercept it \u2014 verified empirically (default form: spy sees 1 call; named form: spy sees 0\n// calls AND the real write still happens). The \"0 writes in steady state\" tests would then be false\n// greens while the seed churned the disk on every command. Do not \"modernize\" this import.\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport { CONFIG_STUB, buildUserProjectExample } from 'src/lib/config-templates'\nimport { getInfraKitConfigPaths, resetMergedConfigCache } from 'src/lib/infra-kit-config'\nimport type { InfraKitConfigPaths } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { fileExists, tildify } from 'src/lib/path-display'\n\nexport interface SeedUserProjectResult {\n configPath: string\n examplePath: string\n createdConfig: boolean\n refreshedExample: boolean\n}\n\n/**\n * Once-per-process guard for {@link ensureUserProjectConfig}. The MCP server is long-lived and\n * handles N tool calls in one process \u2014 without this it would re-stat the config on every call.\n */\nlet seeded = false\n\n/**\n * Derive the non-loaded `.example.jsonc` sibling for a config path. The config loader only globs the\n * three exact `infra-kit.json` filenames, so the sibling never enters the merge chain.\n *\n * @example\n * exampleSiblingPath('/u/.infra-kit/projects/api/infra-kit.json')\n * // => '/u/.infra-kit/projects/api/infra-kit.example.jsonc'\n */\nconst exampleSiblingPath = (jsonPath: string): string => {\n return jsonPath.replace(/\\.json$/, '.example.jsonc')\n}\n\n/**\n * Read a file, degrading a missing/unreadable file to `null` so callers can content-compare without\n * branching on ENOENT.\n *\n * @example\n * await readFileOrNull('/nope.jsonc') // => null\n */\nconst readFileOrNull = async (filePath: string): Promise<string | null> => {\n try {\n return await fs.readFile(filePath, 'utf-8')\n } catch {\n return null\n }\n}\n\n/**\n * Create the layer-3 user-project override file (`~/.infra-kit/projects/<repo>/infra-kit.json`) and\n * keep its annotated `.example.jsonc` sibling in sync with the current schema.\n *\n * UNGATED and MAY THROW \u2014 this is the primitive, shared by `config edit` and by the entry-boundary\n * wrapper {@link ensureUserProjectConfig}. It deliberately does NOT honour `INFRA_KIT_NO_SEED`\n * (that kill switch guards the wrapper only) and does NOT check for a committed project config.\n *\n * An existing `infra-kit.json` is NEVER touched \u2014 user bytes are sacred. The example is rewritten\n * only when its content differs (content compare, not mtime), so the steady state performs zero\n * writes. The MERGED config cache is reset only when the `.json` was actually created: the `.jsonc`\n * example is not part of the merge chain, so refreshing it invalidates nothing. The path memo is\n * left alone either way \u2014 see {@link resetMergedConfigCache}.\n *\n * @example\n * const paths = await getInfraKitConfigPaths()\n * await seedUserProjectConfig(paths)\n * // => { configPath: '/u/.infra-kit/projects/api/infra-kit.json',\n * // examplePath: '/u/.infra-kit/projects/api/infra-kit.example.jsonc',\n * // createdConfig: true, refreshedExample: true }\n */\nexport const seedUserProjectConfig = async (paths: InfraKitConfigPaths): Promise<SeedUserProjectResult> => {\n const configPath = paths.userProject\n const examplePath = exampleSiblingPath(configPath)\n\n await fs.mkdir(path.dirname(configPath), { recursive: true })\n\n const createdConfig = !(await fileExists(configPath))\n\n if (createdConfig) {\n // JSON can't carry comments, so seed an empty-but-valid config and drop the annotated guidance\n // next to it in a non-loaded .example.jsonc.\n await fs.writeFile(configPath, CONFIG_STUB, 'utf-8')\n }\n\n const example = buildUserProjectExample(paths.projectName)\n const refreshedExample = (await readFileOrNull(examplePath)) !== example\n\n if (refreshedExample) {\n await fs.writeFile(examplePath, example, 'utf-8')\n }\n\n if (createdConfig) {\n // Only the .json is a real merge layer \u2014 never invalidate the cache for a .jsonc-only refresh.\n // MERGED cache only: the new layer changes the merge result, but the resolved paths are a pure\n // function of cwd + homedir, so dropping the path memo here would re-spawn both `git rev-parse`\n // calls on the one run that creates the file.\n resetMergedConfigCache()\n }\n\n return { configPath, examplePath, createdConfig, refreshedExample }\n}\n\n/**\n * The one INFO line announcing a freshly created layer-3 config. Shared by all three seed call sites\n * (the entry-boundary bootstrap below, `config edit`, and `init`) \u2014 they announce the same event and\n * must not drift into three wordings.\n *\n * @example\n * seedCreatedMessage(result)\n * // => 'Created ~/.infra-kit/projects/api/infra-kit.json \u2014 see ~/.infra-kit/projects/api/infra-kit.example.jsonc for the annotated reference.'\n */\nexport const seedCreatedMessage = (result: SeedUserProjectResult): string => {\n return `Created ${tildify(result.configPath)} \u2014 see ${tildify(result.examplePath)} for the annotated reference.`\n}\n\n/**\n * Entry-boundary bootstrap: gate, seed, swallow. NEVER throws and NEVER changes the exit code, so it\n * is safe to await from a CLI preAction hook or an MCP tool-call boundary.\n *\n * Order: the `INFRA_KIT_NO_SEED` kill switch (truthiness, so `vi.stubEnv(\u2026, '')` disarms it), the\n * once-per-process guard, path resolution (throws outside a git repo \u2192 silent no-op), then the D2\n * gate \u2014 no committed `<repo-root>/infra-kit.json` means this is not an infra-kit project, so\n * nothing is ever created under `~/.infra-kit/projects/` for it.\n *\n * Any seed failure (EACCES / EROFS / ENOSPC / read-only HOME) is `logger.debug`-ed and swallowed.\n * Exactly one `logger.info` line is emitted, and only when the `.json` was actually created; an\n * example-only refresh is `logger.debug` (rewriting a non-loaded reference file is not news).\n * Steady state is completely silent.\n *\n * @example\n * // CLI preAction / MCP tool boundary\n * await ensureUserProjectConfig()\n * // first run in an infra-kit repo \u2014 INFO: Created ~/.infra-kit/projects/api/infra-kit.json \u2014 \u2026\n * // every later run \u2014 silent, zero writes\n */\nexport const ensureUserProjectConfig = async (): Promise<void> => {\n if (process.env.INFRA_KIT_NO_SEED) {\n return\n }\n\n if (seeded) {\n return\n }\n\n seeded = true\n\n let paths: InfraKitConfigPaths\n\n try {\n paths = await getInfraKitConfigPaths()\n } catch {\n // Outside a git repo `git rev-parse --show-toplevel` fails. Not an infra-kit project \u2014 no-op.\n return\n }\n\n if (!(await fileExists(paths.main))) {\n return\n }\n\n try {\n const result = await seedUserProjectConfig(paths)\n\n if (result.createdConfig) {\n logger.info(seedCreatedMessage(result))\n } else if (result.refreshedExample) {\n logger.debug(`Refreshed ${tildify(result.examplePath)} to match the current schema.`)\n }\n } catch (err) {\n logger.debug({ err, msg: `Skipped seeding ${tildify(paths.userProject)} (config bootstrap failed).` })\n }\n}\n\n/**\n * Clear the once-per-process seed guard. Tests only \u2014 production seeds exactly once per process.\n *\n * @example\n * resetUserProjectSeedGuard()\n * await ensureUserProjectConfig() // resolves paths again instead of short-circuiting\n */\nexport const resetUserProjectSeedGuard = (): void => {\n seeded = false\n}\n", "/**\n * The annotated JSONC reference templates for every infra-kit config file, in one\n * place. Extracted from `commands/init` (layers 2 + vendor) and `commands/config`\n * (layer 3), which each carried a private copy that drifted out of sync with the\n * schema.\n *\n * PURE by construction: strings only, zero I/O \u2014 no `node:fs`, `node:os` or\n * `node:path` here. Callers own the writing; this module owns the words. That is\n * what lets `config-templates.test.ts` diff the templates against\n * `infraKitConfigObject.shape` with no filesystem fakes.\n */\n\n/**\n * The real config files are strict JSON and cannot carry comments, so every seeded\n * `infra-kit.json` starts life as an empty-but-valid object; the guidance lives in\n * the non-loaded `.example.jsonc` sibling built below.\n */\nexport const CONFIG_STUB = '{}\\n'\n\n// The documented key set, shared verbatim by the layer-2 (user-global) and layer-3\n// (user-project) examples so the two can never disagree about what the schema is.\n// Covers ALL nine top-level keys of `infraKitConfigObject` \u2014 `config-templates.test.ts`\n// fails the build if a tenth is added to the schema and not documented here.\n//\n// Every line is a `//` comment: the surrounding braces are the only live JSON, so the\n// file always parses to `{}` once the comments are stripped.\nconst CONFIG_KEY_DOCS = ` // \"environments\": [\"dev\", \"staging\", \"prod\"], // string[] (>=1) \u2014 required in layer 1\n //\n // \"envManagement\": { // required in layer 1; provider-tagged\n // \"provider\": \"doppler\",\n // \"config\": { \"name\": \"my-doppler-project\" }\n // },\n //\n // \"ide\": {\n // \"provider\": \"cursor\",\n // \"config\": { \"workspaceConfigPath\": \"/path/to/your.code-workspace\" }\n // },\n // // Or, for Zed (no workspace file \u2014 one window with all worktrees via \"zed <root> <wt...>\"):\n // \"ide\": { \"provider\": \"zed\", \"config\": {} },\n // // Or drive BOTH editors at once with an array (at most one entry per provider):\n // \"ide\": [\n // { \"provider\": \"cursor\", \"config\": { \"workspaceConfigPath\": \"/path/to/your.code-workspace\" } },\n // { \"provider\": \"zed\", \"config\": {} }\n // ],\n //\n // \"taskManager\": {\n // \"provider\": \"jira\",\n // \"config\": { \"baseUrl\": \"https://acme.atlassian.net\", \"projectId\": 123 }\n // },\n //\n // \"worktrees\": {\n // \"openInGithubDesktop\": false,\n // \"openInCmux\": true,\n // // cmux pane layout for opened worktrees: \"two-columns\" (default, left | right)\n // // or \"three-pane\" (left split top/bottom + full-height right).\n // \"cmux\": { \"layout\": \"two-columns\" }\n // },\n //\n // // Auto-load Doppler env when working inside this project/worktree. Omit to\n // // disable. \"trigger\" (pick one): \"shell-startup\" (new shells) | \"cli-invocation\"\n // // (before each infra-kit command, primes subsequent commands). \"config\" is the\n // // environment to load (must be one of \"environments\"). Requires the committed\n // // infra-kit.json at the git repo root, and the zsh shell integration\n // // (infra-kit init + a new shell). zsh only.\n // \"envAutoLoad\": { \"trigger\": \"shell-startup\", \"config\": \"dev\" },\n //\n // // Per-app local-dev overrides, keyed by APP FOLDER name (the directory under\n // // apps/). Both fields optional: \"port\" pins the local listen port (otherwise\n // // <APP>_PORT / PORT / a built-in default), \"prefixUrl\" pins the URL prefix the\n // // app is served under. An unknown app name is ignored at resolve time, never a\n // // parse error.\n // \"dev\": {\n // \"client\": { \"port\": 4001, \"prefixUrl\": \"/api\" }\n // },\n //\n // // Named \\`infra-kit dev <preset>\\` sessions. The keys under \"apps\" are PACKAGES \u2014\n // // \"<app>/api\" or \"<app>/ui\". A bare \"<app>\" is a FOLDER, not a package, and is\n // // REJECTED. Per target: \"watchDeps\" (api targets) rebuilds the shared-package\n // // dependency closure and restarts on change; \"proxy\" maps a route path to\n // // \"local\" | \"cloud\" for this session only. \"cmux\": true runs each launched target\n // // in its own cmux pane. Omit \"apps\" entirely to launch every discovered target.\n // \"devServersPresets\": {\n // \"backend\": {\n // \"apps\": {\n // \"client/api\": { \"watchDeps\": true, \"proxy\": { \"/api\": \"local\", \"/auth\": \"cloud\" } },\n // \"client/ui\": {}\n // },\n // \"cmux\": true\n // }\n // },\n //\n // // DEPRECATED \u2014 accepted but IGNORED; safe to delete. Remove it and this note in a\n // // future release.\n // //\n // // The proxy port is no longer configurable. Every dev URL is now\n // // https://<release>.<package>.localhost with NO port, and the only port that can\n // // serve a port-free HTTPS URL is 443 \u2014 so a setting here could only put the port\n // // back into the URL. Set up the daemon once (the install is the only step needing\n // // root; trusting its local CA does not). Run \\`infra-kit doctor\\`: it checks both and\n // // prints each fix as a command you can paste, with the paths filled in for your\n // // machine. Do not type a bare \\`portless\\` \u2014 it lives in node_modules, not on PATH.\n // // infra-kit itself never elevates.\n // \"devProxy\": { \"port\": 443 }`\n\n/**\n * Annotated JSONC reference for the user-global (layer 2) config, written next to the\n * real `~/.infra-kit/infra-kit.json` by `infra-kit init`. Documents every top-level\n * key of the schema.\n *\n * @example\n * buildUserGlobalExample()\n * // => '// infra-kit user-global config \u2014 ~/.infra-kit/infra-kit.json\\n\u2026\\n{\\n // \"environments\": \u2026\\n}\\n'\n */\nexport const buildUserGlobalExample = (): string => {\n return `// infra-kit user-global config \u2014 ~/.infra-kit/infra-kit.json\n//\n// Merge chain (later layers override earlier ones at top-level keys):\n// 1. <repo>/infra-kit.json \u2014 committed project config (required)\n// 2. ~/.infra-kit/infra-kit.json \u2014 user-global (the sibling of this file)\n// 3. ~/.infra-kit/projects/<repo-name>/infra-kit.json \u2014 user-scope per-project override\n//\n// Merge is shallow: setting a top-level key replaces that whole section from\n// layer 1. Arrays do not concatenate. Top-level keys recognized:\n// environments, envManagement, ide, taskManager, worktrees, envAutoLoad, dev,\n// devServersPresets, devProxy. The schema is strict \u2014 an unrecognized top-level\n// key is a parse error, not a silently ignored one.\n//\n// This .example.jsonc is reference only \u2014 it is NOT loaded. Put real global\n// overrides in the sibling infra-kit.json (strict JSON: no comments, double-quoted\n// keys). Per-project tweaks belong in layer 3 \u2014 run \\`infra-kit config edit\\`.\n//\n// Every recognized key is documented below. NOTE: \\`environments\\` and\n// \\`envManagement\\` are REQUIRED in the committed project infra-kit.json (layer 1)\n// and are usually NOT set in this user-global layer \u2014 they are shown here only to\n// document the full, valid config shape.\n{\n${CONFIG_KEY_DOCS}\n}\n`\n}\n\n/**\n * Annotated JSONC reference for the user-scope per-project override (layer 3), written\n * alongside the real `~/.infra-kit/projects/<projectName>/infra-kit.json`. Same key\n * documentation as layer 2 \u2014 only the header differs. Deterministic: the project name\n * is the only input, and no path or timestamp from the host leaks into the output.\n *\n * @example\n * buildUserProjectExample('hulyo-monorepo')\n * // => '// infra-kit user override for hulyo-monorepo \u2014 ~/.infra-kit/projects/hulyo-monorepo/infra-kit.json\\n\u2026'\n */\nexport const buildUserProjectExample = (projectName: string): string => {\n return `// infra-kit user override for ${projectName} \u2014 ~/.infra-kit/projects/${projectName}/infra-kit.json\n//\n// Layer 3 (highest precedence) of the config merge chain. Shallow-merged on top of\n// <repo>/infra-kit.json (layer 1) and ~/.infra-kit/infra-kit.json (layer 2) \u2014 a\n// top-level key set here replaces that whole section wholesale; arrays do not\n// concatenate. Top-level keys recognized: environments, envManagement, ide,\n// taskManager, worktrees, envAutoLoad, dev, devServersPresets, devProxy. The schema\n// is strict \u2014 an unrecognized top-level key is a parse error, not a silently ignored\n// one.\n//\n// This .example.jsonc is reference only \u2014 it is NOT loaded. Put real overrides\n// in the sibling infra-kit.json (strict JSON: no comments, double-quoted keys).\n//\n// Every recognized key is documented below. NOTE: \\`environments\\` and\n// \\`envManagement\\` are REQUIRED in the committed project infra-kit.json (layer 1)\n// and are usually NOT set in this override layer \u2014 they are shown here only to\n// document the full, valid config shape.\n{\n${CONFIG_KEY_DOCS}\n}\n`\n}\n\n/**\n * Annotated JSONC reference for the machine-local factory registry\n * (`~/.infra-kit/vendor.json`). Independent of the infra-kit.json merge chain: the real\n * file is scaffolded by `infra-kit vendor config --init`, never seeded by `init`.\n *\n * @example\n * buildVendorExample()\n * // => '// infra-kit factory registry \u2014 ~/.infra-kit/vendor.json\\n\u2026\\n{\\n // \"workspaceDir\": \u2026\\n}\\n'\n */\nexport const buildVendorExample = (): string => {\n return `// infra-kit factory registry \u2014 ~/.infra-kit/vendor.json\n//\n// Machine-local registry the vendor commands (sync/manifest/diff) read to know\n// where your project repos live and which ones to stamp. This .example.jsonc is\n// reference only \u2014 it is NOT loaded. The real file is the strict-JSON sibling\n// vendor.json (no comments, double-quoted keys); run \\`infra-kit vendor config --init\\`\n// to scaffold it.\n{\n // \"workspaceDir\": \"~/projects\", // string (absolute or ~-prefixed) \u2014 where target repos are cloned\n // \"targets\": [\"my-repo-a\", \"my-repo-b\"] // string[] (>=1) \u2014 repo dir names resolved under workspaceDir\n}\n`\n}\n", "import fs from 'node:fs/promises'\nimport os from 'node:os'\n\n/**\n * Shared path-display helpers for CLI output. Extracted from `commands/config`\n * and `commands/init/migrate-config` (which each had private copies) so config\n * surfaces print paths consistently. Keep this dependency-light (node builtins\n * only) \u2014 it is imported by several commands.\n */\n\n/**\n * Resolve whether a file is reachable, suppressing ENOENT (and any access error)\n * into a boolean. Used to render `[\u2713]`/`[ ]` existence markers.\n *\n * @example\n * await fileExists('/etc/hosts') // => true\n * await fileExists('/nope.txt') // => false\n */\nexport const fileExists = async (filePath: string): Promise<boolean> => {\n try {\n await fs.access(filePath)\n\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Replace the user's home prefix with `~` so logged paths stay short and portable\n * across machines. Leaves non-home paths untouched.\n *\n * @example\n * // os.homedir() === '/Users/arthur'\n * tildify('/Users/arthur/.infra-kit/infra-kit.json') // => '~/.infra-kit/infra-kit.json'\n * tildify('/etc/hosts') // => '/etc/hosts'\n */\nexport const tildify = (filePath: string): string => {\n const home = os.homedir()\n\n return filePath.startsWith(home) ? `~${filePath.slice(home.length)}` : filePath\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport yaml from 'yaml'\nimport { z } from 'zod'\n\nimport {\n getInfraKitConfigPaths,\n infraKitConfigSchema,\n infraKitOverrideConfigSchema,\n resetInfraKitConfigCache,\n} from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { fileExists, tildify } from 'src/lib/path-display'\nimport { getFactoryConfigPath } from 'src/lib/vendor/factory-config'\nimport { factoryConfigSchema } from 'src/lib/vendor/factory-config-schema'\n\ninterface MigrateLayer {\n label: string\n /** Legacy YAML source path. */\n yml: string\n /** Target JSON path. */\n json: string\n /** Validates the parsed object before any write (main vs override schema). */\n schema: z.ZodType\n}\n\n/**\n * Swap a resolved `.json` config path back to its legacy `.yml` sibling.\n *\n * @example\n * legacyYmlPath('/repo/infra-kit.json') // => '/repo/infra-kit.yml'\n */\nconst legacyYmlPath = (jsonPath: string): string => {\n return jsonPath.replace(/\\.json$/, '.yml')\n}\n\n/**\n * Convert any legacy `infra-kit.yml` config layers to `infra-kit.json` as part\n * of `infra-kit init`. Best-effort and non-fatal: each merge-chain layer\n * (project, user-global, user-project) is migrated independently, and a\n * conflict (both `.yml` and `.json` present) or an invalid `.yml` warns and\n * skips that layer rather than aborting init or touching the other layers.\n * Idempotent \u2014 already-JSON layers are left untouched.\n *\n * @example\n * await migrateLegacyConfig()\n * // INFO: \u2713 Migrated infra-kit.yml \u2192 infra-kit.json\n * // (no output when there is nothing legacy to convert)\n */\nexport const migrateLegacyConfig = async (): Promise<void> => {\n let paths: Awaited<ReturnType<typeof getInfraKitConfigPaths>>\n\n try {\n paths = await getInfraKitConfigPaths()\n } catch {\n // No resolvable project (e.g. init run outside a repo) \u2014 nothing to migrate.\n return\n }\n\n const layers: MigrateLayer[] = [\n { label: 'infra-kit.json', yml: legacyYmlPath(paths.main), json: paths.main, schema: infraKitConfigSchema },\n {\n label: '~/.infra-kit/infra-kit.json',\n // The user-global JSON filename changed (config.json \u2192 infra-kit.json), but\n // the legacy YAML it converts from kept the old `config.yml` name. Pin it\n // here so it does not drift to `infra-kit.yml` via legacyYmlPath(); the\n // main/userProject layers keep deriving their `.yml` sibling normally\n // because their `.json` filename never changed.\n yml: path.join(path.dirname(paths.userGlobal), 'config.yml'),\n json: paths.userGlobal,\n schema: infraKitOverrideConfigSchema,\n },\n {\n label: `~/.infra-kit/projects/${paths.projectName}/infra-kit.json`,\n yml: legacyYmlPath(paths.userProject),\n json: paths.userProject,\n schema: infraKitOverrideConfigSchema,\n },\n ]\n\n let migrated = 0\n\n for (const layer of layers) {\n const [ymlExists, jsonExists] = await Promise.all([fileExists(layer.yml), fileExists(layer.json)])\n\n if (!ymlExists) continue\n\n if (jsonExists) {\n logger.info(\n `\u26A0 Skipped ${tildify(layer.yml)} \u2014 ${tildify(layer.json)} already exists (remove the stale .yml manually)`,\n )\n\n continue\n }\n\n // Keep per-layer migration non-fatal even for malformed YAML or I/O errors\n // (TOCTOU after the existence probe, EACCES, read-only FS): warn and skip\n // so one bad layer never aborts `init` or the other layers.\n try {\n const raw = await fs.readFile(layer.yml, 'utf-8')\n const parsed = (yaml.parse(raw) ?? {}) as unknown\n const result = layer.schema.safeParse(parsed)\n\n if (!result.success) {\n logger.info(`\u26A0 Skipped ${tildify(layer.yml)} \u2014 invalid config: ${z.prettifyError(result.error)}`)\n\n continue\n }\n\n await fs.mkdir(path.dirname(layer.json), { recursive: true })\n await fs.writeFile(layer.json, `${JSON.stringify(result.data, null, 2)}\\n`, 'utf-8')\n await fs.rm(layer.yml, { force: true })\n\n logger.info(`\u2713 Migrated ${tildify(layer.yml)} \u2192 ${tildify(layer.json)}`)\n migrated++\n } catch (err) {\n logger.info(`\u26A0 Skipped ${tildify(layer.yml)} \u2014 ${(err as Error).message}`)\n }\n }\n\n if (migrated > 0) {\n resetInfraKitConfigCache()\n }\n}\n\n/**\n * Rename the user-global config from the legacy `config.json` filename to the\n * canonical `infra-kit.json` (and its `config.example.jsonc` reference to\n * `infra-kit.example.jsonc`), so every merge-chain layer shares one filename.\n * Run by `infra-kit init` before the user-global config is seeded, so a user's\n * existing overrides are carried forward instead of being shadowed by a fresh\n * `{}` stub written under the new name.\n *\n * Best-effort and non-fatal per file: a missing source is skipped silently\n * (idempotent \u2014 nothing to rename on a second run), and an already-present\n * target is never overwritten (the stale `config.json` is left in place so no\n * bytes are lost \u2014 the user reconciles it manually). Resets the config cache\n * when anything was renamed.\n *\n * @example\n * await migrateUserGlobalConfigFilename()\n * // \u2713 Renamed ~/.infra-kit/config.json \u2192 ~/.infra-kit/infra-kit.json\n * // (no output when there is nothing legacy to rename)\n */\nexport const migrateUserGlobalConfigFilename = async (): Promise<void> => {\n let paths: Awaited<ReturnType<typeof getInfraKitConfigPaths>>\n\n try {\n paths = await getInfraKitConfigPaths()\n } catch {\n // No resolvable project (e.g. init run outside a repo) \u2014 nothing to rename.\n return\n }\n\n const dir = path.dirname(paths.userGlobal)\n // The config.json \u2192 infra-kit.json rename carries forward a user's REAL config and\n // is load-bearing. The example-file rename is now largely redundant: seedUserGlobalConfig\n // rewrites infra-kit.example.jsonc unconditionally moments later, so a renamed legacy\n // example is overwritten anyway (and if infra-kit.example.jsonc already exists, the\n // no-overwrite guard simply leaves an orphaned config.example.jsonc behind \u2014 harmless).\n const pairs: { from: string; to: string }[] = [\n { from: path.join(dir, 'config.json'), to: paths.userGlobal },\n { from: path.join(dir, 'config.example.jsonc'), to: path.join(dir, 'infra-kit.example.jsonc') },\n ]\n\n let renamed = 0\n\n for (const { from, to } of pairs) {\n const [fromExists, toExists] = await Promise.all([fileExists(from), fileExists(to)])\n\n if (!fromExists) continue\n\n if (toExists) {\n logger.info(`\u26A0 Skipped ${tildify(from)} \u2014 ${tildify(to)} already exists (remove the stale file manually)`)\n\n continue\n }\n\n // Same-directory rename is atomic; per-file try/catch keeps a TOCTOU race or\n // I/O error (EACCES, read-only FS) non-fatal so one bad file never aborts init.\n try {\n await fs.mkdir(path.dirname(to), { recursive: true })\n await fs.rename(from, to)\n\n logger.info(`\u2713 Renamed ${tildify(from)} \u2192 ${tildify(to)}`)\n renamed++\n } catch (err) {\n logger.info(`\u26A0 Skipped ${tildify(from)} \u2014 ${(err as Error).message}`)\n }\n }\n\n if (renamed > 0) {\n resetInfraKitConfigCache()\n }\n}\n\n/**\n * Convert a legacy machine-local factory config from executable TypeScript\n * (`~/.infra-kit/vendor.config.ts`) to static JSON (`~/.infra-kit/vendor.json`) as\n * part of `infra-kit init`. The factory config used to be a `.ts` module loaded via\n * dynamic `import()`; it is now strict JSON read with `JSON.parse`. This one-shot\n * migration dynamic-imports the old `.ts` (the ONLY remaining dynamic import of the\n * factory file), resolves a factory-function or object default export, validates it,\n * writes `vendor.json`, and removes the old `.ts`.\n *\n * Best-effort, non-fatal, idempotent (no old `.ts` \u2192 no-op), and never overwrites an\n * existing `vendor.json`. When the old export was a FUNCTION, its resolved output is\n * frozen into static JSON \u2014 so a DISTINCT warning is emitted (not a plain\n * `\u2713 Migrated`) because dynamic re-evaluation (env vars, directory globbing) no\n * longer happens. No `resetInfraKitConfigCache()` \u2014 the factory loader shares no\n * cache with the `infra-kit.json` layers.\n *\n * @example\n * await migrateFactoryConfigToJson()\n * // \u2713 Migrated ~/.infra-kit/vendor.config.ts \u2192 ~/.infra-kit/vendor.json\n * // (no output when there is nothing legacy to convert)\n */\nexport const migrateFactoryConfigToJson = async (): Promise<void> => {\n const newJson = getFactoryConfigPath() // ~/.infra-kit/vendor.json\n const dir = path.dirname(newJson)\n const oldTs = path.join(dir, 'vendor.config.ts')\n\n const [oldExists, newExists] = await Promise.all([fileExists(oldTs), fileExists(newJson)])\n\n if (!oldExists) return // idempotent: nothing to migrate (incl. after a prior successful run)\n\n if (newExists) {\n logger.info(\n `\u26A0 Skipped ${tildify(oldTs)} \u2014 ${tildify(newJson)} already exists (remove the stale vendor.config.ts manually)`,\n )\n\n return\n }\n\n // The factory `.ts` is executable; read it once via dynamic import (Node native\n // type stripping) with an mtime cache-bust, resolving a factory-function or object\n // default export. Per-file try/catch keeps a malformed module or I/O error\n // non-fatal so one bad file never aborts init.\n try {\n const stat = await fs.stat(oldTs)\n const moduleUrl = `${pathToFileURL(oldTs).href}?mtime=${Number(stat.mtimeMs)}`\n const imported = (await import(moduleUrl)) as { default?: unknown }\n const raw = imported.default\n const wasFunction = typeof raw === 'function'\n const resolved = wasFunction ? await (raw as () => unknown)() : raw\n\n const result = factoryConfigSchema.safeParse(resolved)\n\n if (!result.success) {\n logger.info(`\u26A0 Skipped ${tildify(oldTs)} \u2014 invalid factory config: ${z.prettifyError(result.error)}`)\n\n return // leave the old .ts in place so the user can fix and re-run; do not delete unconverted data\n }\n\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(newJson, `${JSON.stringify(result.data, null, 2)}\\n`, 'utf-8')\n await fs.rm(oldTs, { force: true })\n\n if (wasFunction) {\n // The old export was a function: its values were resolved ONCE and frozen into\n // static JSON. Disclose the lost dynamism instead of reporting a clean success.\n logger.info(\n `\u26A0 Migrated ${tildify(oldTs)} \u2192 ${tildify(newJson)} as a STATIC SNAPSHOT \u2014 the old file was a ` +\n `function (export default () => ({...})); computed values (env vars, directory globbing) were ` +\n `frozen at their current values and no longer re-evaluate. Edit ${tildify(newJson)} directly to change them.`,\n )\n } else {\n logger.info(`\u2713 Migrated ${tildify(oldTs)} \u2192 ${tildify(newJson)}`)\n }\n } catch (err) {\n logger.info(`\u26A0 Skipped ${tildify(oldTs)} \u2014 ${(err as Error).message}`)\n }\n}\n\n/**\n * Surgically strip the removed `ide.config.mode` field from a parsed config\n * object (single-provider or array form). The windows-removal made `mode` a dead\n * key \u2014 the loader already ignores it, so this only matters for keeping the\n * on-disk file clean. Returns `changed: false` (and the input untouched) when\n * there is no `mode` to remove, so callers can skip rewriting clean files.\n *\n * Only `mode` is removed \u2014 every other key is preserved verbatim (no full-schema\n * re-validation), so a config that is otherwise invalid or carries forward-compat\n * keys is never altered beyond the dead field.\n */\nconst stripLegacyIdeMode = (parsed: Record<string, unknown>): { changed: boolean; result: Record<string, unknown> } => {\n const ide = parsed.ide\n\n if (ide === null || typeof ide !== 'object') {\n return { changed: false, result: parsed }\n }\n\n let changed = false\n\n const stripEntry = (entry: unknown): unknown => {\n if (entry === null || typeof entry !== 'object' || !('config' in entry)) {\n return entry\n }\n\n const config = (entry as { config: unknown }).config\n\n if (config === null || typeof config !== 'object' || !('mode' in config)) {\n return entry\n }\n\n changed = true\n\n const restConfig = Object.fromEntries(\n Object.entries(config as Record<string, unknown>).filter(([key]) => {\n return key !== 'mode'\n }),\n )\n\n return { ...(entry as Record<string, unknown>), config: restConfig }\n }\n\n const nextIde = Array.isArray(ide) ? ide.map(stripEntry) : stripEntry(ide)\n\n if (!changed) {\n return { changed: false, result: parsed }\n }\n\n return { changed: true, result: { ...parsed, ide: nextIde } }\n}\n\n/**\n * Normalize any existing `infra-kit.json` config layers (project, user-global,\n * user-project) from the old IDE structure to the new one by removing the\n * removed `ide.config.mode` field. Run by `infra-kit init` after the YAML\u2192JSON\n * migration. Best-effort and non-fatal per layer; only rewrites a file when its\n * `ide` config actually carries the dead key, so clean configs are left byte-for-\n * byte untouched (idempotent). Resets the config cache when anything changed.\n *\n * @example\n * await normalizeLegacyIdeStructures()\n * // \u2713 Normalized ide config in infra-kit.json (removed legacy \"mode\")\n * // (no output when no config carries a legacy \"mode\")\n */\nexport const normalizeLegacyIdeStructures = async (): Promise<void> => {\n let paths: Awaited<ReturnType<typeof getInfraKitConfigPaths>>\n\n try {\n paths = await getInfraKitConfigPaths()\n } catch {\n return\n }\n\n const jsonPaths = [paths.main, paths.userGlobal, paths.userProject]\n\n let normalized = 0\n\n for (const jsonPath of jsonPaths) {\n if (!(await fileExists(jsonPath))) continue\n\n try {\n const raw = await fs.readFile(jsonPath, 'utf-8')\n\n if (raw.trim() === '') continue\n\n const parsed = JSON.parse(raw) as Record<string, unknown>\n const { changed, result } = stripLegacyIdeMode(parsed)\n\n if (!changed) continue\n\n await fs.writeFile(jsonPath, `${JSON.stringify(result, null, 2)}\\n`, 'utf-8')\n\n logger.info(`\u2713 Normalized ide config in ${tildify(jsonPath)} (removed legacy \"mode\")`)\n normalized++\n } catch (err) {\n logger.info(`\u26A0 Skipped normalizing ${tildify(jsonPath)} \u2014 ${(err as Error).message}`)\n }\n }\n\n if (normalized > 0) {\n resetInfraKitConfigCache()\n }\n}\n", "import fs from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { z } from 'zod'\n\nimport { USER_CONFIG_DIR_NAME } from 'src/lib/infra-kit-config'\n\nimport { FACTORY_CONFIG_FILE, factoryConfigSchema } from './factory-config-schema'\nimport type { FactoryConfig } from './factory-config-schema'\n\n/**\n * Absolute path to the machine-local factory config (`~/.infra-kit/vendor.json`).\n * Always returned, even when the file does not exist, so callers can surface a\n * \"where would it go?\" prompt.\n *\n * @example\n * getFactoryConfigPath() // => '/Users/arthur/.infra-kit/vendor.json'\n */\nexport const getFactoryConfigPath = (): string => {\n return path.join(os.homedir(), USER_CONFIG_DIR_NAME, FACTORY_CONFIG_FILE)\n}\n\n/**\n * Expand a leading `~` to the home dir. Absolute paths pass through unchanged.\n * A non-absolute, non-`~` value throws \u2014 cwd-relative resolution is ambiguous\n * across CLI vs long-running MCP-server invocations, so we reject it loudly.\n *\n * @example\n * expandTilde('~') // => '/Users/arthur'\n * expandTilde('~/projects') // => '/Users/arthur/projects'\n * expandTilde('/abs/dir') // => '/abs/dir'\n * expandTilde('rel/dir') // throws\n */\nexport const expandTilde = (p: string): string => {\n if (p === '~') return os.homedir()\n if (p.startsWith('~/')) return path.join(os.homedir(), p.slice(2))\n if (path.isAbsolute(p)) return p\n\n throw new Error(`workspaceDir must be absolute or ~-prefixed, got: ${p}`)\n}\n\n/**\n * Load and validate the user-global factory config at `~/.infra-kit/vendor.json`.\n * Reads the static JSON with `JSON.parse` (an empty/whitespace-only file is treated\n * as `{}`), then validates against {@link factoryConfigSchema}.\n *\n * Reads the file on every call (no module cache to bust \u2014 unlike a `.ts`\n * `import()`, `JSON.parse` always sees the current bytes), so an absent \u2192 present\n * transition works within one process: a `--init` scaffold followed by a load\n * succeeds, and a long-running MCP server picks up edits without a restart.\n * Throws an actionable error pointing at `infra-kit vendor config --init` when the\n * file is absent.\n *\n * @example\n * const { workspaceDir, targets } = await loadFactoryConfig()\n */\nexport const loadFactoryConfig = async (): Promise<FactoryConfig> => {\n const configPath = getFactoryConfigPath()\n\n let raw: string\n\n try {\n raw = await fs.readFile(configPath, 'utf-8')\n } catch {\n throw new Error(\n `Factory config not found at ${configPath}. infra-kit needs a machine-local factory ` +\n `registry to know where your project repos live and which to stamp. Run ` +\n `\\`infra-kit vendor config --init\\` to scaffold it, or create it manually as JSON:\\n\\n` +\n ` {\\n` +\n ` \"workspaceDir\": \"~/projects\",\\n` +\n ` \"targets\": [\"my-repo\"]\\n` +\n ` }\\n`,\n )\n }\n\n let data: unknown\n\n try {\n data = raw.trim() === '' ? {} : JSON.parse(raw)\n } catch (err) {\n throw new Error(`Invalid JSON in vendor.json at ${configPath}: ${(err as Error).message}`)\n }\n\n const parsed = factoryConfigSchema.safeParse(data)\n\n if (!parsed.success) {\n throw new Error(`Invalid factory config at ${configPath}: ${z.prettifyError(parsed.error)}`)\n }\n\n return parsed.data\n}\n", "import { z } from 'zod'\n\n/**\n * Pure (node-free) factory config schema. Kept separate from `factory-config.ts`\n * (which imports node builtins for the runtime loader) so the public lib entry can\n * re-export the `FactoryConfig` type without dragging node types into the emitted\n * `.d.ts` \u2014 same split as `config-schema.ts` / `config.ts`.\n */\n\n/**\n * Basename of the machine-local factory config, which lives at\n * `~/.infra-kit/vendor.json`. It is static JSON (no longer an executable `.ts`\n * module): the loader reads it with `JSON.parse`. The committed SOURCE-repo config\n * keeps the `vendor.config.ts` basename \u2014 always refer to this one as \"the\n * user-global factory config\".\n */\nexport const FACTORY_CONFIG_FILE = 'vendor.json'\n\n/**\n * The machine-local factory registry. Answers \"where do my project repos live\"\n * (`workspaceDir`) and \"which ones does the factory stamp\" (`targets`). The\n * portable \"what to vendor\" definition (`copy[]`) lives in the committed SOURCE\n * `vendor.config.ts`, never here. `.strict()` rejects a stray `copy` key so a\n * misplaced source config produces a clear error.\n */\nexport const factoryConfigSchema = z\n .object({\n /** Absolute (or `~`-prefixed) dir where the target project repos are cloned. */\n workspaceDir: z.string().min(1),\n /** Target repo directory names, resolved under `workspaceDir`. */\n targets: z.array(z.string()).min(1),\n })\n .strict()\n\nexport type FactoryConfig = z.infer<typeof factoryConfigSchema>\n", "import { $ } from 'zx'\n\n/**\n * Hard upper bound for the Doppler auth probes. `doppler me` is a network call;\n * without a timeout a hung VPN/connection would stall every caller indefinitely\n * (notably the env auto-load preAction hook, which runs before each CLI command).\n */\nconst DOPPLER_AUTH_TIMEOUT_MS = 5_000\n\n/**\n * Validate Doppler CLI installation and authentication status. Throws on failure\n * so callers (CLI entry, MCP tool handler) can translate to the right surface \u2014\n * CLI exits non-zero; MCP returns a structured tool error instead of tearing\n * down the server. Both probes are time-bounded so a hung Doppler/network never\n * blocks the caller (a timeout surfaces as the same \"not authenticated\" path).\n */\nexport const validateDopplerCliAndAuth = async (): Promise<void> => {\n try {\n await $`doppler --version`.timeout(DOPPLER_AUTH_TIMEOUT_MS)\n } catch (error: unknown) {\n throw new Error('Doppler CLI is not installed. Install it from: https://docs.doppler.com/docs/install-cli', {\n cause: error,\n })\n }\n\n try {\n await $`doppler me`.timeout(DOPPLER_AUTH_TIMEOUT_MS)\n } catch (error: unknown) {\n throw new Error('Doppler CLI is not authenticated (or timed out). Run: doppler login', { cause: error })\n }\n}\n", "/**\n * Translate Doppler `secrets download` not-found failures into actionable,\n * point-at-the-fix guidance. Pure (no I/O) so the classification + message policy\n * is unit-testable without spawning the Doppler CLI \u2014 mirroring the pure-helper\n * pattern in `commands/env-load/env-load.ts`.\n *\n * Observed Doppler output on a not-found download (verified 2026-06-27, doppler\n * v3.76.0), exit 1, written to STDERR (stdout empty):\n * Unable to download secrets\n * Doppler Error: Could not find requested project 'name'\n * Doppler Error: Could not find requested config 'name'\n * Only the \"Doppler Error:\" prefix is ANSI-colored; the \"Could not find requested\n * ...\" text is plain, so substring matching is color-safe. zx's\n * `ProcessOutput.message` also concatenates stdout+stderr, so the same markers are\n * matchable off `.message` when `.stderr` is unavailable.\n */\n\n/** Which kind of Doppler not-found a download failure represents (`unknown` = not a not-found error). */\nexport type DopplerNotFoundKind = 'project' | 'config' | 'unknown'\n\nconst PROJECT_NOT_FOUND_MARKER = 'Could not find requested project'\nconst CONFIG_NOT_FOUND_MARKER = 'Could not find requested config'\n\n/**\n * Classify a Doppler download failure from its stderr/message text. Returns\n * `'unknown'` for anything that isn't a recognized not-found error so the caller\n * rethrows the original untouched (degrades to status quo, never worse).\n *\n * @example\n * classifyDopplerDownloadError(\"Doppler Error: Could not find requested project 'x'\") // => 'project'\n * classifyDopplerDownloadError(\"Doppler Error: Could not find requested config 'dev'\") // => 'config'\n * classifyDopplerDownloadError('network unreachable') // => 'unknown'\n */\nexport const classifyDopplerDownloadError = (stderr: string): DopplerNotFoundKind => {\n if (stderr.includes(PROJECT_NOT_FOUND_MARKER)) return 'project'\n\n if (stderr.includes(CONFIG_NOT_FOUND_MARKER)) return 'config'\n\n return 'unknown'\n}\n\ninterface DopplerNotFoundMessageArgs {\n /** Whether the missing entity is the project or the config within it. */\n kind: 'project' | 'config'\n project: string\n config: string\n /**\n * Names to suggest. `null` = lookup failed/unavailable, `[]` = none exist; both\n * omit the suggestion line so a failed enrichment probe is never misreported as\n * a definitive \"none available\".\n */\n available?: string[] | null\n}\n\n/**\n * Build the actionable, multi-line error body for a Doppler not-found failure:\n * states what's missing, points at the exact infra-kit.json field, lists available\n * names when known, and gives a concrete fix.\n *\n * @example\n * buildDopplerNotFoundMessage({ kind: 'project', project: 'infra-kit', config: 'dev', available: ['nomadream'] })\n * // => 'Doppler project \"infra-kit\" not found (set in infra-kit.json \u2192 envManagement.config.name).\\nAvailable projects: nomadream.\\nFix: ...'\n */\nexport const buildDopplerNotFoundMessage = ({\n kind,\n project,\n config,\n available,\n}: DopplerNotFoundMessageArgs): string => {\n const lines: string[] =\n kind === 'project'\n ? [`Doppler project \"${project}\" not found (set in infra-kit.json \u2192 envManagement.config.name).`]\n : [`Doppler config \"${config}\" not found in project \"${project}\" (set in infra-kit.json \u2192 environments).`]\n\n if (available && available.length > 0) {\n const label = kind === 'project' ? 'Available projects' : `Available configs in \"${project}\"`\n\n lines.push(`${label}: ${available.join(', ')}.`)\n }\n\n lines.push(\n kind === 'project'\n ? 'Fix: update envManagement.config.name to an existing project, or create it in Doppler.'\n : 'Fix: update environments to an existing config, or create it in Doppler.',\n )\n\n return lines.join('\\n')\n}\n", "import { $ } from 'zx'\nimport type { ProcessPromise } from 'zx'\n\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\n\n/**\n * Hard upper bound for the Doppler list probes. These run only on error/diagnostic\n * paths (download-failure enrichment, `doctor`), but a hung VPN/connection must not\n * stall them \u2014 mirrors the auth-probe budget in `doppler-cli-auth.ts`.\n */\nconst DOPPLER_LIST_TIMEOUT_MS = 5_000\n\n/**\n * Resolve Doppler project name from infra-kit.json at the project root\n */\nexport const getDopplerProject = async (): Promise<string> => {\n const { envManagement } = await getInfraKitConfig()\n\n return envManagement.config.name\n}\n\n/**\n * List the Doppler project names for the authenticated account, or `null` when the\n * lookup itself fails (CLI missing, unauthenticated, network/timeout, malformed\n * JSON). NEVER throws. Callers MUST distinguish `null` (\"couldn't tell\") from `[]`\n * (\"told: none exist\") so an enrichment or diagnostic message never misreports a\n * failed probe as \"no projects available\".\n *\n * @example\n * await listDopplerProjects() // => ['example-project', 'nomadream'] (or null on failure)\n */\nexport const listDopplerProjects = async (): Promise<string[] | null> => {\n return runDopplerNameList(() => {\n return $`doppler projects --json`\n })\n}\n\n/**\n * List the Doppler config names for a project, or `null` when the lookup itself\n * fails (see {@link listDopplerProjects} for the `null` vs `[]` contract).\n *\n * @example\n * await listDopplerConfigs('nomadream') // => ['dev', 'oleg', 'prod', 'arthur'] (or null on failure)\n */\nexport const listDopplerConfigs = async (project: string): Promise<string[] | null> => {\n return runDopplerNameList(() => {\n return $`doppler configs --project ${project} --json`\n })\n}\n\n/**\n * Run a `doppler ... --json` listing quietly + time-bounded and extract the `name`\n * field of each entry. Returns `null` on any failure (subprocess error, timeout,\n * non-array/malformed JSON) so the caller can tell a failed probe from an empty set.\n */\nconst runDopplerNameList = async (run: () => ProcessPromise): Promise<string[] | null> => {\n const prevQuiet = $.quiet\n\n $.quiet = true\n try {\n const result = await run().timeout(DOPPLER_LIST_TIMEOUT_MS)\n\n return parseDopplerNames(result.stdout)\n } catch {\n return null\n } finally {\n $.quiet = prevQuiet\n }\n}\n\n/** Parse a Doppler `--json` array into its `name` strings, or `null` if the shape is unexpected. */\nconst parseDopplerNames = (stdout: string): string[] | null => {\n let parsed: unknown\n\n try {\n parsed = JSON.parse(stdout)\n } catch {\n return null\n }\n\n if (!Array.isArray(parsed)) return null\n\n return parsed\n .map((entry) => {\n return entry !== null && typeof entry === 'object' ? (entry as { name?: unknown }).name : undefined\n })\n .filter((name): name is string => {\n return typeof name === 'string'\n })\n}\n", "import fs from 'node:fs/promises'\n\nexport interface OverrideSummary {\n /** True only when the layer-3 file parses AND carries at least one top-level key. */\n hasOverrides: boolean\n /** Raw top-level keys, in file order. Empty when absent, empty-object, or unreadable. */\n overrideKeys: string[]\n /** True when the file exists but could not be read or parsed as a JSON object. */\n unreadable: boolean\n}\n\n/**\n * Summarize the layer-3 user-project override file for the read-only diagnostics (`config path`,\n * `doctor`). ONE JSON-tolerance policy, shared \u2014 the two diagnostics must never disagree about what\n * a given file on disk means.\n *\n * RAW keys, deliberately \u2014 `Object.keys(JSON.parse(raw))`, never the config loader's `loadLayer`.\n * That one throws on malformed JSON and on schema failure, and the schema object is strict, so a\n * single typo'd top-level key would invalidate the whole layer and turn these diagnostic commands\n * into throwers \u2014 precisely when the user reaches for them. Raw keys also surface the typo (a\n * misspelled `devServersPreset` shows up in the list) instead of swallowing it.\n *\n * Any read/parse failure degrades to \"no overrides\" plus `unreadable: true`. An empty or\n * whitespace-only file is treated as `{}` (the same tolerance `loadLayer` has). A file that parses\n * but is NOT a plain object (`[1,2,3]`, `\"x\"`, `null`, `7`) is also `unreadable` \u2014 it cannot be a\n * merge layer, and reporting an array's indices as `(3 override(s): 0, 1, 2)` would be a lie.\n *\n * @example\n * await readOverrideSummary('/u/.infra-kit/projects/api/infra-kit.json', true)\n * // => { hasOverrides: true, overrideKeys: ['ide', 'dev'], unreadable: false }\n */\nexport const readOverrideSummary = async (filePath: string, exists: boolean): Promise<OverrideSummary> => {\n if (!exists) {\n return { hasOverrides: false, overrideKeys: [], unreadable: false }\n }\n\n try {\n const raw = await fs.readFile(filePath, 'utf-8')\n const parsed: unknown = raw.trim() === '' ? {} : JSON.parse(raw)\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { hasOverrides: false, overrideKeys: [], unreadable: true }\n }\n\n const keys = Object.keys(parsed)\n\n return { hasOverrides: keys.length > 0, overrideKeys: keys, unreadable: false }\n } catch {\n return { hasOverrides: false, overrideKeys: [], unreadable: true }\n }\n}\n\n/**\n * The human-readable suffix for a layer-3 file that EXISTS. Callers own the absent case: `config\n * path` already prints a `[ ]` marker for it, while `doctor` must call it out as a possible seed\n * failure (every command seeds layer 3, so an absent file is no longer \"not yet created\").\n *\n * @example\n * describeOverrides({ hasOverrides: true, overrideKeys: ['ide', 'dev'], unreadable: false })\n * // => '(2 override(s): ide, dev)'\n */\nexport const describeOverrides = (summary: OverrideSummary): string => {\n if (summary.unreadable) {\n return '(unreadable \u2014 invalid JSON)'\n }\n\n if (summary.overrideKeys.length === 0) {\n return '(empty \u2014 no overrides)'\n }\n\n return `(${summary.overrideKeys.length} override(s): ${summary.overrideKeys.join(', ')})`\n}\n", "import fs from 'node:fs'\nimport path from 'node:path'\n\nimport {\n DEFAULT_WARM_TTL_SECONDS,\n ENV_CLEAR_FILE,\n ENV_LOAD_FILE,\n atomicWriteFileSync,\n getProjectWarmCacheDir,\n getWarmCacheRoot,\n} from 'src/lib/constants'\nimport { logger } from 'src/lib/logger'\n\n/** Inputs for {@link shouldWriteWarm}. All derivable without I/O so the gate is pure. */\nexport interface ShouldWriteWarmInput {\n /** Was this an automatic load? Manual loads must never become the project default. */\n autoLoaded: boolean\n /** The canonical project dir the SHELL passed via `--project-dir` (undefined for cli-invocation / non-startup callers). */\n projectDir: string | undefined\n /** `realpath(git top-level)` for this load, or '' when non-git / unresolvable (see {@link canonicalizeProjectRoot}). */\n canonicalProjectRoot: string\n}\n\n/**\n * Decide whether an auto-load should ALSO write a project-scoped warm copy. Pure\n * (no I/O) so the full gate matrix is unit-testable. Warm requires ALL of:\n * - the load was automatic (a manual `env-load prod` must not warm the project);\n * - the shell passed a canonical `--project-dir` (warm is a shell-startup-only\n * optimization; the cli-invocation trigger passes none and gets no warm file);\n * - a non-empty git root (else every non-git project would collapse to one key\n * and cross-leak secrets);\n * - the realpath'd git root EQUALS the shell's canonical dir \u2014 the byte-identical\n * key contract. On any mismatch (symlink/nested-config monorepo) we skip warm\n * and degrade to the already-correct poll rather than write under a key the\n * shell will never look up.\n */\nexport const shouldWriteWarm = ({ autoLoaded, projectDir, canonicalProjectRoot }: ShouldWriteWarmInput): boolean => {\n if (!autoLoaded) return false\n if (!projectDir) return false\n if (!canonicalProjectRoot) return false\n\n return canonicalProjectRoot === projectDir\n}\n\n/** `realpath(projectRoot)`, or '' when projectRoot is empty or unresolvable. Never throws. */\nexport const canonicalizeProjectRoot = (projectRoot: string): string => {\n if (!projectRoot) return ''\n\n try {\n return fs.realpathSync(projectRoot)\n } catch {\n return ''\n }\n}\n\n/**\n * Write the warm copy of env-load.sh for a project (0o600). Best-effort: a warm\n * write must never break the real (session) load, so failures are swallowed to\n * debug. `contents` is the SAME bytes as the session file so a warm-source is\n * indistinguishable from a fresh one.\n */\nexport const writeWarmCache = (canonicalProjectDir: string, contents: string): void => {\n try {\n const dir = getProjectWarmCacheDir(canonicalProjectDir)\n\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 })\n atomicWriteFileSync(path.join(dir, ENV_LOAD_FILE), contents, 0o600)\n } catch (error) {\n logger.debug(`warm-cache write skipped: ${(error as Error).message}`)\n }\n}\n\n/**\n * Delete every project's warm dir whose env-load.sh is older than the TTL. The\n * codebase has no cache reaper, so warm files (plaintext secrets) would otherwise\n * accumulate indefinitely at a stable path; each successful warm write sweeps the\n * whole `projects/` tree. The just-written project is fresh (mtime\u2248now) so it is\n * never swept. Best-effort, concurrency-tolerant (force), never throws.\n */\nexport const evictStaleWarmCaches = (\n nowMs: number = Date.now(),\n ttlSeconds: number = DEFAULT_WARM_TTL_SECONDS,\n): void => {\n const root = getWarmCacheRoot()\n\n let entries: fs.Dirent[]\n\n try {\n entries = fs.readdirSync(root, { withFileTypes: true })\n } catch {\n return // no warm root yet \u2014 nothing to sweep\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n\n const projectDir = path.join(root, entry.name)\n\n try {\n const { mtimeMs } = fs.statSync(path.join(projectDir, ENV_LOAD_FILE))\n\n if (nowMs - mtimeMs >= ttlSeconds * 1000) {\n fs.rmSync(projectDir, { recursive: true, force: true })\n }\n } catch {\n // missing/unreadable warm file \u2014 leave the dir (a lone clear marker is harmless)\n }\n }\n}\n\n/**\n * Invalidate a project's warm cache on `env-clear`. `purge` removes the project's\n * warm dir outright (durable disable). Otherwise, when a warm file exists, write a\n * clear-marker (`env-clear.sh`) so the NEXT shell's startup gate skips warm-source\n * (it compares marker mtime vs warm mtime, clear wins). The marker is TRANSIENT: a\n * later successful auto-load writes a newer warm file and warm resumes \u2014 durable\n * disable requires `purge`. Best-effort, never throws.\n */\nexport const invalidateProjectWarmCache = (canonicalProjectDir: string, purge: boolean): void => {\n try {\n const dir = getProjectWarmCacheDir(canonicalProjectDir)\n\n if (purge) {\n fs.rmSync(dir, { recursive: true, force: true })\n\n return\n }\n\n // No warm file \u21D2 nothing to suppress; don't create a dir just to hold a marker.\n if (!fs.existsSync(path.join(dir, ENV_LOAD_FILE))) return\n\n atomicWriteFileSync(path.join(dir, ENV_CLEAR_FILE), '# infra-kit warm cache cleared\\n', 0o600)\n } catch (error) {\n logger.debug(`warm-cache invalidate skipped: ${(error as Error).message}`)\n }\n}\n", "import fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { z } from 'zod'\n\nimport {\n ENV_CLEAR_FILE,\n ENV_LOAD_FILE,\n INFRA_KIT_ENV_AUTOLOADED_VAR,\n INFRA_KIT_ENV_CLEARED_VAR,\n INFRA_KIT_ENV_CONFIG_VAR,\n INFRA_KIT_ENV_LOADED_AT_VAR,\n INFRA_KIT_ENV_PROJECT_ROOT_VAR,\n INFRA_KIT_ENV_PROJECT_VAR,\n INFRA_KIT_ENV_VAR,\n atomicWriteFileSync,\n getSessionCacheDir,\n parseVarNamesFromEnvFile,\n} from 'src/lib/constants'\nimport { getProjectRoot } from 'src/lib/git-utils'\nimport { canonicalizeProjectRoot, invalidateProjectWarmCache } from 'src/lib/warm-cache'\nimport { defineMcpTool, textContent } from 'src/types'\n\nexport interface EnvClearArgs {\n /**\n * Also invalidate this project's WARM cache. `false` (default) writes a transient\n * clear-marker so the next shell skips warm-source once; `true` deletes the warm\n * dir outright (durable \u2014 warm won't resume until the next auto-load rewrites it).\n */\n purge?: boolean\n}\n\n/**\n * Invalidate the current project's warm cache alongside the session clear, so a\n * NEW shell doesn't warm-load what the user just cleared. Resolves the project root\n * itself (git top-level, realpath'd) to match the warm key; a non-git project never\n * had a warm file, so it is a no-op. Best-effort \u2014 never blocks the session clear.\n */\nconst clearProjectWarmCache = async (purge: boolean): Promise<void> => {\n let projectRoot = ''\n\n try {\n projectRoot = await getProjectRoot()\n } catch {\n return // non-git: no warm cache to invalidate\n }\n\n const canon = canonicalizeProjectRoot(projectRoot)\n\n if (canon) invalidateProjectWarmCache(canon, purge)\n}\n\n/**\n * Build the lines for env-clear.sh: `unset` every loaded var plus the session\n * metadata (config/project/loadedAt) and the auto-load marker, then EXPORT the\n * clear sentinel so cli-invocation auto-load stays suppressed in this shell until\n * an explicit `env-load` (which unsets it) or a new shell. Pure for testability.\n */\nexport const buildEnvClearLines = (varNames: string[]): string[] => {\n return [\n ...varNames.map((v) => {\n return `unset ${v}`\n }),\n `unset ${INFRA_KIT_ENV_VAR}`,\n `unset ${INFRA_KIT_ENV_CONFIG_VAR}`,\n `unset ${INFRA_KIT_ENV_PROJECT_VAR}`,\n `unset ${INFRA_KIT_ENV_PROJECT_ROOT_VAR}`,\n `unset ${INFRA_KIT_ENV_LOADED_AT_VAR}`,\n `unset ${INFRA_KIT_ENV_AUTOLOADED_VAR}`,\n `export ${INFRA_KIT_ENV_CLEARED_VAR}='1'`,\n ]\n}\n\n/**\n * Clear loaded env vars. Prints a file path to stdout that must be sourced to apply.\n * The env-clear shell alias does this automatically. Also invalidates the project's\n * WARM cache so a new shell doesn't re-load what was just cleared. Throws when no env\n * is loaded AND `--purge` was not passed (a bare clear needs something to clear; a\n * purge is a standalone warm-cache wipe that works regardless of session state).\n */\nexport const envClear = async ({ purge = false }: EnvClearArgs = {}) => {\n const cacheDir = getSessionCacheDir()\n const envLoadPath = path.join(cacheDir, ENV_LOAD_FILE)\n const hasLoadedEnv = fs.existsSync(envLoadPath)\n\n if (!hasLoadedEnv && !purge) {\n throw new Error('No loaded environment found. Run `env-load` first.')\n }\n\n // Warm invalidation is independent of the current session's load state.\n await clearProjectWarmCache(purge)\n\n const varNames = hasLoadedEnv ? parseVarNamesFromEnvFile(envLoadPath) : []\n\n const unsetLines = buildEnvClearLines(varNames)\n\n const clearFilePath = path.resolve(cacheDir, ENV_CLEAR_FILE)\n\n fs.mkdirSync(cacheDir, { recursive: true, mode: 0o700 })\n\n atomicWriteFileSync(clearFilePath, `${unsetLines.join('\\n')}\\n`, 0o600)\n\n // REQUIRED\n process.stdout.write(`${clearFilePath}\\n`)\n\n // Remove env load file so the next env-clear call correctly reports \"no env loaded\".\n // `force` so concurrent clears don't throw ENOENT when another already removed it.\n if (hasLoadedEnv) fs.rmSync(envLoadPath, { force: true })\n\n const structuredContent = {\n filePath: clearFilePath,\n variableCount: varNames.length,\n unsetStatements: unsetLines,\n purged: purge,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\n// MCP Tool Registration\nexport const envClearMcpTool = defineMcpTool({\n name: 'env-clear',\n description:\n 'Generate a shell script that unsets every env var previously loaded by env-load for this session, plus the infra-kit session metadata vars. Does NOT mutate the calling process. When `infra-kit init` has installed the zsh shell integration, the user\\'s terminal auto-sources the unset script on its next prompt (precmd hook) \u2014 so calling this via MCP will clear the vars in the shell that launched Claude Code automatically. Other callers must source \"<filePath>\" themselves or surface it to the user. Errors if no env is currently loaded.',\n inputSchema: {},\n outputSchema: {\n filePath: z.string().describe('Path to the file that must be sourced to apply'),\n variableCount: z.number().describe('Number of variables cleared'),\n unsetStatements: z.array(z.string()).describe('Unset statements generated'),\n purged: z.boolean().describe('Whether the project warm cache was purged outright'),\n },\n handler: () => {\n return envClear()\n },\n})\n", "import { z } from 'zod'\n\nimport { getDopplerProject } from 'src/integrations/doppler/doppler-project'\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { defineMcpTool, textContent } from 'src/types'\n\n/**\n * List available Doppler configs for the detected project.\n *\n * Purely local: reads infra-kit.json and does not call Doppler. We intentionally\n * do not run validateDopplerCliAndAuth here \u2014 users listing envs often do so\n * before `doppler login`, and a spurious auth error would be misleading.\n */\nexport const envList = async () => {\n const project = await getDopplerProject()\n const { environments } = await getInfraKitConfig()\n\n logger.info(`Doppler project: ${project}\\n`)\n logger.info('Available configs:')\n\n for (const env of environments) {\n logger.info(` - ${env}`)\n }\n\n const structuredContent = {\n project,\n configs: environments,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\n// MCP Tool Registration\nexport const envListMcpTool = defineMcpTool({\n name: 'env-list',\n description:\n 'List the environments the project is configured to support. Returns the `environments` list declared in infra-kit.json at the project root (not a live fetch from Doppler) plus the Doppler project name resolved from the same file. Read-only.',\n inputSchema: {},\n outputSchema: {\n project: z.string().describe('Detected Doppler project name'),\n configs: z.array(z.string()).describe('Available environment configs'),\n },\n handler: envList,\n})\n", "import select from '@inquirer/select'\nimport { Buffer } from 'node:buffer'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { z } from 'zod'\nimport { $ } from 'zx'\n\nimport {\n buildDopplerNotFoundMessage,\n classifyDopplerDownloadError,\n getDopplerProject,\n listDopplerConfigs,\n listDopplerProjects,\n validateDopplerCliAndAuth,\n} from 'src/integrations/doppler'\nimport { commandEcho } from 'src/lib/command-echo'\nimport {\n ENV_LOAD_FILE,\n INFRA_KIT_ENV_AUTOLOADED_VAR,\n INFRA_KIT_ENV_CLEARED_VAR,\n INFRA_KIT_ENV_CONFIG_VAR,\n INFRA_KIT_ENV_LOADED_AT_VAR,\n INFRA_KIT_ENV_PROJECT_ROOT_VAR,\n INFRA_KIT_ENV_PROJECT_VAR,\n INFRA_KIT_ENV_VAR,\n atomicWriteFileSync,\n getSessionCacheDir,\n} from 'src/lib/constants'\nimport { extractStderr } from 'src/lib/errors/operation-error'\nimport { getProjectRoot } from 'src/lib/git-utils'\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\nimport { canonicalizeProjectRoot, evictStaleWarmCaches, shouldWriteWarm, writeWarmCache } from 'src/lib/warm-cache'\nimport { defineMcpTool, textContent } from 'src/types'\n\ninterface EnvLoadArgs {\n config?: string\n}\n\ninterface WriteEnvLoadFileArgs {\n /** Resolved Doppler config / environment name (no interactive picker here). */\n config: string\n /**\n * Marks the produced file as auto-loaded. `true` writes the\n * INFRA_KIT_ENV_AUTOLOADED marker; `false` (a manual load) instead unsets the\n * marker and lifts any clear suppression so a deliberate load wins.\n */\n autoLoaded?: boolean\n /**\n * Auto-load only: re-checked AFTER the Doppler download, immediately before the\n * atomic write. Return false to abort (a clear or a manual load landed during the\n * slow download). Manual loads omit it and always write.\n */\n beforeWrite?: () => boolean\n /**\n * Canonical (realpath'd) project dir the SHELL passed via `--project-dir` on the\n * shell-startup auto-load spawn. Its presence (with `autoLoaded`) is what enables\n * the project-scoped WARM cache write; see {@link shouldWriteWarm}. Undefined for\n * manual loads and the cli-invocation trigger \u2014 those write no warm copy.\n */\n projectDir?: string\n}\n\nexport interface EnvLoadFileResult {\n filePath: string\n variableCount: number\n project: string\n config: string\n}\n\ninterface EnvLoadFileLinesArgs {\n pairs: Array<[string, string]>\n config: string\n project: string\n projectRoot: string\n loadedAt: string\n autoLoaded: boolean\n}\n\n/**\n * The auto-load marker lines appended to env-load.sh. Pure so the marker policy\n * is unit-testable without touching Doppler or the filesystem.\n *\n * @example\n * buildAutoLoadMarkerLines(true) // => [\"INFRA_KIT_ENV_AUTOLOADED='1'\"]\n * buildAutoLoadMarkerLines(false) // => ['unset INFRA_KIT_ENV_AUTOLOADED', 'unset INFRA_KIT_ENV_CLEARED']\n */\nconst buildAutoLoadMarkerLines = (autoLoaded: boolean): string[] => {\n if (autoLoaded) {\n return [`${INFRA_KIT_ENV_AUTOLOADED_VAR}=${shellSingleQuote('1')}`]\n }\n\n // Manual load: drop any auto marker so auto-load never re-clobbers a deliberate\n // choice, and lift a prior clear suppression so the manual load takes effect.\n return [`unset ${INFRA_KIT_ENV_AUTOLOADED_VAR}`, `unset ${INFRA_KIT_ENV_CLEARED_VAR}`]\n}\n\n/**\n * Build the dotenv-format shell lines for env-load.sh. Pure (no I/O) so callers\n * can assert the exact marker behavior. `set -a`/`set +a` auto-export every\n * assignment when the file is sourced.\n */\nexport const buildEnvLoadFileLines = ({\n pairs,\n config,\n project,\n projectRoot,\n loadedAt,\n autoLoaded,\n}: EnvLoadFileLinesArgs): string[] => {\n return [\n 'set -a',\n ...pairs.map(([key, value]) => {\n return `${key}=${shellSingleQuote(value)}`\n }),\n // Purpose-named env-name handle for non-shell tooling (e.g. infra-kit/vite's\n // `<env>` proxy interpolation). Single-quoted like every sourced value.\n `${INFRA_KIT_ENV_VAR}=${shellSingleQuote(config)}`,\n `${INFRA_KIT_ENV_CONFIG_VAR}=${shellSingleQuote(config)}`,\n `${INFRA_KIT_ENV_PROJECT_VAR}=${shellSingleQuote(project)}`,\n `${INFRA_KIT_ENV_PROJECT_ROOT_VAR}=${shellSingleQuote(projectRoot)}`,\n `${INFRA_KIT_ENV_LOADED_AT_VAR}=${shellSingleQuote(loadedAt)}`,\n ...buildAutoLoadMarkerLines(autoLoaded),\n 'set +a',\n ]\n}\n\n/**\n * Project root (git top-level) the loaded env belongs to, for the cross-project\n * shell gate. Returns '' when this isn't a git checkout so a non-git infra-kit\n * project still loads \u2014 the shell gate then fails open (spawns rather than skips)\n * instead of breaking the whole load on `git rev-parse` throwing.\n */\nconst resolveProjectRootSafe = async (): Promise<string> => {\n try {\n return await getProjectRoot()\n } catch {\n return ''\n }\n}\n\n/**\n * Download Doppler secrets for a resolved config and atomically write env-load.sh\n * to the session cache dir. Does NOT print to stdout \u2014 shared by the CLI/MCP\n * `envLoad` entry (which prints the path) and the auto-load path (which lets the\n * shell precmd hook source the file).\n */\nexport const writeEnvLoadFile = async ({\n config,\n autoLoaded = false,\n beforeWrite,\n projectDir,\n}: WriteEnvLoadFileArgs): Promise<EnvLoadFileResult | null> => {\n await validateDopplerCliAndAuth()\n\n const project = await getDopplerProject()\n const projectRoot = await resolveProjectRootSafe()\n\n const pairs = await downloadDopplerSecrets(project, config)\n\n const loadedAt = new Date().toISOString()\n const envFileLines = buildEnvLoadFileLines({ pairs, config, project, projectRoot, loadedAt, autoLoaded })\n const fileContents = `${envFileLines.join('\\n')}\\n`\n\n const cacheDir = getSessionCacheDir()\n const envFilePath = path.resolve(cacheDir, ENV_LOAD_FILE)\n\n // Re-check suppression immediately before the atomic write: a clear or a manual\n // load may have landed during the slow Doppler download (auto-load path only).\n if (beforeWrite && !beforeWrite()) return null\n\n fs.mkdirSync(cacheDir, { recursive: true, mode: 0o700 })\n atomicWriteFileSync(envFilePath, fileContents, 0o600)\n\n // Project-scoped WARM copy: lets the NEXT shell source these vars instantly at\n // startup (pure zsh, no Doppler) before this session's fresh file lands. Gated so\n // it only fires for a shell-startup auto-load whose realpath'd git root matches the\n // dir the shell keyed on (see shouldWriteWarm). Each warm write also sweeps stale\n // warm dirs \u2014 we own the only cache reaper in the codebase.\n const canonicalProjectRoot = canonicalizeProjectRoot(projectRoot)\n\n if (shouldWriteWarm({ autoLoaded, projectDir, canonicalProjectRoot })) {\n writeWarmCache(projectDir!, fileContents)\n evictStaleWarmCaches()\n }\n\n return {\n filePath: envFilePath,\n variableCount: pairs.length,\n project,\n config,\n }\n}\n\n/**\n * Load environment variables from Doppler for the given config\n */\nexport const envLoad = async (args: EnvLoadArgs) => {\n const { config } = args\n\n let selectedConfig = ''\n\n if (config) {\n selectedConfig = config\n } else {\n // Validate auth before the interactive picker so an unauthenticated user fails\n // fast instead of choosing an env first. writeEnvLoadFile re-checks (cheap) for\n // the non-interactive path where this branch is skipped.\n await validateDopplerCliAndAuth()\n\n const { environments } = await getInfraKitConfig()\n\n commandEcho.setInteractive()\n selectedConfig = await select(\n {\n message: 'Select environment config',\n choices: environments.map((env) => {\n return { name: env, value: env }\n }),\n },\n // Render to stderr so the prompt is visible when stdout is captured via $() in the shell function.\n // Only env-load and env-clear use the $() stdout-capture shell pattern.\n { output: process.stderr },\n )\n }\n\n commandEcho.addOption('--config', selectedConfig)\n\n // A manual load is authoritative: autoLoaded=false drops the auto marker.\n const result = await writeEnvLoadFile({ config: selectedConfig, autoLoaded: false })\n\n // A manual load passes no beforeWrite, so it always writes \u2014 this guards the\n // invariant (and narrows the nullable result) rather than handling a real path.\n if (!result) throw new Error('env-load: write was unexpectedly aborted')\n\n // REQUIRED\n process.stdout.write(`${result.filePath}\\n`)\n\n // Logs to stderr (pino \u2192 pretty-print), so it doesn't pollute the captured\n // file path that the shell wrapper reads from stdout.\n commandEcho.print()\n\n const structuredContent = {\n filePath: result.filePath,\n variableCount: result.variableCount,\n project: result.project,\n config: result.config,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\n/**\n * Cap the Doppler stdout we're willing to accept. A well-formed env bundle is\n * O(10 KB); megabytes would indicate a service regression or the wrong stream\n * being captured, and we don't want to write that to disk or source it.\n */\nexport const DOPPLER_MAX_OUTPUT_BYTES = 1024 * 1024\n\n/**\n * Hard upper bound for the Doppler subprocess. Well under zx's default so a\n * hung call surfaces quickly instead of blocking an interactive shell or an\n * MCP tool handler.\n */\nconst DOPPLER_DOWNLOAD_TIMEOUT_MS = 30_000\n\nconst downloadDopplerSecrets = async (project: string, config: string): Promise<Array<[string, string]>> => {\n const prevQuiet = $.quiet\n\n $.quiet = true\n try {\n let result\n\n try {\n result =\n await $`doppler secrets download --no-file --format json --project ${project} --config ${config}`.timeout(\n DOPPLER_DOWNLOAD_TIMEOUT_MS,\n )\n } catch (error: unknown) {\n throw await translateDopplerDownloadError(error, project, config)\n }\n\n assertDopplerOutputSize(result.stdout)\n\n return parseDopplerSecretsJson(result.stdout)\n } finally {\n $.quiet = prevQuiet\n }\n}\n\n/**\n * Turn a raw `doppler secrets download` failure into an actionable error. A\n * recognized project/config not-found is enriched (on this error path only) with\n * the list of available names and rethrown as a clean `Error` pointing at the\n * exact infra-kit.json field. Anything else \u2014 auth, network, timeout \u2014 is\n * rethrown untouched so it degrades to the existing behavior, never worse.\n */\nconst translateDopplerDownloadError = async (error: unknown, project: string, config: string): Promise<Error> => {\n const stderr = extractStderr(error) ?? (error instanceof Error ? error.message : String(error))\n const kind = classifyDopplerDownloadError(stderr)\n\n if (kind === 'unknown') {\n return error instanceof Error ? error : new Error(String(error))\n }\n\n const available = kind === 'project' ? await listDopplerProjects() : await listDopplerConfigs(project)\n\n return new Error(buildDopplerNotFoundMessage({ kind, project, config, available }))\n}\n\nexport const assertDopplerOutputSize = (stdout: string): void => {\n const bytes = Buffer.byteLength(stdout, 'utf-8')\n\n if (bytes > DOPPLER_MAX_OUTPUT_BYTES) {\n throw new Error(\n `doppler returned unexpectedly large output (${bytes} bytes > ${DOPPLER_MAX_OUTPUT_BYTES}) \u2014 refusing to write to disk`,\n )\n }\n}\n\nexport const shellSingleQuote = (value: string): string => {\n const escaped = value.replaceAll(\"'\", \"'\\\\''\")\n\n return `'${escaped}'`\n}\n\n/** A valid POSIX env-var name: a letter or underscore, then word characters. */\nconst ENV_VAR_NAME_PATTERN = /^[A-Z_]\\w*$/i\n\n/**\n * Parse the `doppler secrets download --format json` output (a flat\n * `{\"KEY\":\"value\"}` map) into ordered `[key, value]` pairs, validating the shape\n * and every name/value. Values are returned RAW \u2014 callers must single-quote them\n * before emitting shell (an unquoted `a$(cmd)b` would execute on `source`).\n *\n * @example\n * parseDopplerSecretsJson('{\"FOO\":\"bar\",\"BAZ\":\"a$(x)\"}')\n * // => [['FOO', 'bar'], ['BAZ', 'a$(x)']]\n */\nexport const parseDopplerSecretsJson = (stdout: string): Array<[string, string]> => {\n let parsed: unknown\n\n try {\n parsed = JSON.parse(stdout)\n } catch {\n throw new Error(`doppler returned non-JSON output for env-load (got: ${JSON.stringify(stdout.slice(0, 80))})`)\n }\n\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('doppler returned unexpected JSON shape for env-load (expected an object of KEY: value pairs)')\n }\n\n const pairs: Array<[string, string]> = []\n\n for (const [key, value] of Object.entries(parsed)) {\n if (!ENV_VAR_NAME_PATTERN.test(key)) {\n throw new Error(\n `doppler returned an invalid env var name for env-load (got: ${JSON.stringify(key.slice(0, 80))})`,\n )\n }\n\n if (typeof value !== 'string') {\n throw new TypeError(`doppler returned a non-string value for \"${key}\" (got ${typeof value})`)\n }\n\n pairs.push([key, value])\n }\n\n if (pairs.length === 0) {\n throw new Error('doppler returned empty output for env-load')\n }\n\n return pairs\n}\n\n// MCP Tool Registration\nexport const envLoadMcpTool = defineMcpTool({\n name: 'env-load',\n description:\n 'Download the env vars for a Doppler config and write them to a temporary shell script. Does NOT mutate the calling process \u2014 returns the path to a script that must be sourced (\"source <filePath>\") for the vars to take effect. The infra-kit shell wrapper auto-sources; direct MCP callers must handle sourcing themselves or surface filePath to the user. \"config\" is required when invoked via MCP (the CLI interactive picker is unreachable without a TTY).',\n inputSchema: {\n config: z\n .string()\n .describe('Doppler config / environment name to load (e.g. \"dev\", \"arthur\", \"renana\"). Required for MCP calls.'),\n },\n outputSchema: {\n filePath: z.string().describe('Path to the file that must be sourced to apply variables'),\n variableCount: z.number().describe('Number of variables loaded'),\n project: z.string().describe('Doppler project name'),\n config: z.string().describe('Doppler config name'),\n },\n handler: envLoad,\n})\n", "const STDERR_EXCERPT_MAX_BYTES = 200\n\nexport interface OperationErrorContext {\n operation: string\n remediation?: string\n stderrExcerpt?: string\n}\n\n/**\n * Duck-typed read of zx's `ProcessOutput.stderr` (and similar shapes) without\n * importing zx types just for an `instanceof` check.\n *\n * @example\n * extractStderr(new Error('x')) // undefined\n * extractStderr({ stderr: 'fatal: ...' }) // 'fatal: ...'\n * extractStderr({ stderr: '' }) // undefined (empty treated as missing)\n */\nexport const extractStderr = (cause: unknown): string | undefined => {\n if (cause === null || typeof cause !== 'object') return undefined\n const stderr = (cause as { stderr?: unknown }).stderr\n\n return typeof stderr === 'string' && stderr.length > 0 ? stderr : undefined\n}\n\n/**\n * Compose the human-and-agent-readable message body for an `OperationError`:\n * `\"failed to <operation> [\u2014 stderr: <excerpt>] [\u2014 try: <remediation>]\"`.\n * `stderrExcerpt` overrides anything duck-typed off `cause`; both are trimmed\n * and capped at {@link STDERR_EXCERPT_MAX_BYTES} so a runaway subprocess can't\n * blow up the message.\n */\nconst buildMessage = (cause: unknown, ctx: OperationErrorContext): string => {\n const stderr = ctx.stderrExcerpt ?? extractStderr(cause)\n const parts = [`failed to ${ctx.operation}`]\n\n if (stderr) parts.push(`stderr: ${stderr.slice(0, STDERR_EXCERPT_MAX_BYTES).trim()}`)\n if (ctx.remediation) parts.push(`try: ${ctx.remediation}`)\n\n return parts.join(' \u2014 ')\n}\n\n/**\n * Error type for any handler-level failure that should surface to the caller\n * (CLI user or MCP-connected agent) with a remediation hint. Wraps an\n * underlying cause and renders a single-line, structured message so logs and\n * agent tool-result text stay scannable.\n *\n * Pattern modeled on the exemplary Doppler errors in\n * `src/integrations/doppler/doppler-cli-auth.ts`.\n *\n * @example\n * // wrap a zx subprocess failure\n * try {\n * await $`git worktree add ${path} ${branch}`\n * } catch (err) {\n * throw new OperationError(err, {\n * operation: `git worktree add for ${branch}`,\n * remediation: 'check the branch name and that the parent dir is writable',\n * })\n * }\n *\n * @example\n * // validation failure with no underlying cause\n * throw new OperationError(undefined, {\n * operation: 'launch deploy-all workflow',\n * remediation: `pass one of: ${environments.join(', ')}`,\n * stderrExcerpt: `invalid environment: ${selectedEnv}`,\n * })\n */\nexport class OperationError extends Error {\n readonly operation: string\n readonly remediation?: string\n\n constructor(cause: unknown, ctx: OperationErrorContext) {\n super(buildMessage(cause, ctx), { cause })\n this.name = 'OperationError'\n this.operation = ctx.operation\n this.remediation = ctx.remediation\n }\n}\n", "import path from 'node:path'\nimport process from 'node:process'\nimport { z } from 'zod'\n\nimport {\n ENV_LOAD_FILE,\n INFRA_KIT_ENV_AUTOLOADED_VAR,\n INFRA_KIT_ENV_CLEARED_VAR,\n INFRA_KIT_ENV_CONFIG_VAR,\n INFRA_KIT_ENV_LOADED_AT_VAR,\n INFRA_KIT_ENV_PROJECT_VAR,\n INFRA_KIT_SESSION_VAR,\n getSessionCacheDir,\n parseVarNamesFromEnvFile,\n} from 'src/lib/constants'\nimport { logger } from 'src/lib/logger'\nimport { defineMcpTool, textContent } from 'src/types'\n\n/**\n * Report which env is currently loaded in the terminal session. Pure local\n * introspection: reads only `process.env` + the cached env-load.sh \u2014 it makes NO\n * Doppler call (use `doctor` for auth/CLI checks), so it works offline and never\n * hangs. Surfaces whether the env was auto-loaded vs manually loaded and whether a\n * clear is currently suppressing auto-load.\n */\nexport const envStatus = async () => {\n logger.info('Environment session status:')\n\n // Check session-loaded vars \u2014 getSessionCacheDir() throws if INFRA_KIT_SESSION is unset\n const cacheDir = getSessionCacheDir()\n\n const sessionId = process.env[INFRA_KIT_SESSION_VAR]!\n const envLoadPath = path.join(cacheDir, ENV_LOAD_FILE)\n\n let sessionLoadedCount = 0\n let sessionTotalCount = 0\n\n const sessionConfig = process.env[INFRA_KIT_ENV_CONFIG_VAR] ?? null\n const sessionProject = process.env[INFRA_KIT_ENV_PROJECT_VAR] ?? null\n const sessionLoadedAt = process.env[INFRA_KIT_ENV_LOADED_AT_VAR] ?? null\n const autoLoaded = process.env[INFRA_KIT_ENV_AUTOLOADED_VAR] === '1'\n const cleared = Boolean(process.env[INFRA_KIT_ENV_CLEARED_VAR])\n\n if (sessionConfig) {\n const varNames = parseVarNamesFromEnvFile(envLoadPath)\n\n if (varNames.length > 0) {\n sessionTotalCount = varNames.length\n sessionLoadedCount = varNames.filter((v) => {\n return v in process.env\n }).length\n }\n\n const loadedAtDisplay = sessionLoadedAt?.replace(/\\.\\d{3}Z$/, '') ?? null\n const origin = autoLoaded ? 'auto-loaded' : 'manually loaded'\n\n logger.info(\n ` ${sessionConfig}: ${sessionLoadedCount} of ${sessionTotalCount} vars loaded (${origin}, project: ${sessionProject}, loadedAt: ${loadedAtDisplay}, session: ${sessionId})\\n`,\n )\n\n if (sessionTotalCount > 0 && sessionLoadedCount < sessionTotalCount) {\n const missing = sessionTotalCount - sessionLoadedCount\n\n logger.warn(\n ` ${missing} cached var(s) are not present in the current process \u2014 env-load needs to be re-sourced, or vars were unset manually.`,\n )\n }\n } else {\n const clearedNote = cleared ? ' (cleared \u2014 auto-load suppressed until a new shell or explicit env-load)' : ''\n\n logger.info(` Session ${sessionId}: no env loaded${clearedNote}\\n`)\n }\n\n const structuredContent = {\n sessionId,\n sessionLoadedCount,\n sessionTotalCount,\n sessionConfig,\n sessionProject,\n sessionLoadedAt,\n autoLoaded,\n cleared,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\n// MCP Tool Registration\nexport const envStatusMcpTool = defineMcpTool({\n name: 'env-status',\n description:\n 'Report which Doppler project/config is currently loaded in the terminal session, when it was loaded, how many variables are cached, whether it was auto-loaded, and whether a clear is suppressing auto-load. Pure local introspection \u2014 makes NO Doppler call (use doctor for auth). Read-only \u2014 use env-load / env-clear to change the terminal session.',\n inputSchema: {},\n outputSchema: {\n sessionId: z.string().describe('Current terminal session ID'),\n sessionLoadedCount: z.number().describe('Number of cached vars active in the current session'),\n sessionTotalCount: z.number().describe('Total number of cached var names'),\n sessionConfig: z.string().nullable().describe('Doppler config name of the loaded session (environment name)'),\n sessionProject: z.string().nullable().describe('Doppler project name of the loaded session'),\n sessionLoadedAt: z.string().nullable().describe('ISO 8601 timestamp of when the env was loaded'),\n autoLoaded: z.boolean().describe('True when the loaded env was applied by env auto-load (not a manual env-load)'),\n cleared: z.boolean().describe('True when env-clear is currently suppressing auto-load in this shell'),\n },\n handler: envStatus,\n})\n", "import { z } from 'zod'\nimport { $ } from 'zx'\n\nimport { getReleasePRsWithInfo } from 'src/integrations/gh'\nimport { commandEcho, confirmOrExit } from 'src/lib/command-echo'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { assertManagementContext } from 'src/lib/git-guard'\nimport { logger } from 'src/lib/logger'\nimport { pickReleaseBranches } from 'src/lib/prompts/release-picker'\nimport {\n detectReleaseType,\n formatBranchPickerItems,\n getJiraDescriptions,\n releaseBranchLabels,\n} from 'src/lib/release-utils'\nimport { defineMcpTool, textContent } from 'src/types'\nimport type { RequiredConfirmedOptionArg } from 'src/types'\n\ninterface GhMergeDevArgs extends RequiredConfirmedOptionArg {\n all?: boolean\n}\n\n/**\n * Merge dev into every release branch\n */\nexport const ghMergeDev = async (args: GhMergeDevArgs) => {\n const { all, confirmedCommand } = args\n\n await assertManagementContext({ operation: 'merge dev into release branches', requiredBranch: 'dev' })\n\n // Only merge dev into regular releases (not hotfixes, which target main)\n const allPRs = await getReleasePRsWithInfo()\n const releasePRsList = allPRs\n .filter((pr) => {\n return detectReleaseType(pr.title) === 'regular'\n })\n .map((pr) => {\n return pr.branch\n })\n\n if (releasePRsList.length === 0) {\n logger.info('\u2139\uFE0F No open release branches found')\n\n commandEcho.print()\n\n return {\n content: textContent(\n JSON.stringify({ successfulMerges: 0, failedMerges: 0, failedBranches: [], totalBranches: 0 }, null, 2),\n ),\n structuredContent: { successfulMerges: 0, failedMerges: 0, failedBranches: [], totalBranches: 0 },\n }\n }\n\n let selectedReleaseBranches: string[] = []\n\n if (all) {\n selectedReleaseBranches = releasePRsList\n } else {\n commandEcho.setInteractive()\n\n const descriptions = await getJiraDescriptions()\n\n selectedReleaseBranches = await pickReleaseBranches(\n formatBranchPickerItems({ branches: releasePRsList, descriptions }),\n { required: true },\n )\n }\n\n // Track --all flag if all branches were selected (either via flag or interactively)\n const allSelected = selectedReleaseBranches.length === releasePRsList.length\n\n if (allSelected) {\n commandEcho.addOption('--all', true)\n } else {\n commandEcho.addOption('--versions', releaseBranchLabels(selectedReleaseBranches))\n }\n\n await confirmOrExit(\n confirmedCommand,\n `Are you sure you want to merge dev into these branches: ${selectedReleaseBranches.join(', ')}?`,\n )\n\n // Track --yes flag if confirmation was interactive (user confirmed)\n if (!confirmedCommand) {\n commandEcho.addOption('--yes', true)\n }\n\n $.quiet = true\n\n await $`git fetch origin`\n await $`git switch dev`\n await $`git pull origin dev`\n\n const failedBranches: string[] = []\n\n // Merge dev into each branch\n for (const branch of selectedReleaseBranches) {\n const success = await mergeDev(branch)\n\n if (!success) {\n failedBranches.push(branch)\n }\n }\n\n $.quiet = false\n\n if (failedBranches.length > 0) {\n logger.info(`\\n\u26A0\uFE0F ${failedBranches.length} branch(es) failed to merge automatically.\\n`)\n logger.info('\uD83D\uDCCB Manual merge script for failed branches:')\n for (const branch of failedBranches) {\n logger.info(\n `# Merge dev into ${branch} and resolve conflicts if any \\n\\ngit switch ${branch} && git pull origin ${branch} && git merge origin/dev\\ngit push origin ${branch} && git switch dev\\n`,\n )\n }\n logger.info(\n `\u2705 ${selectedReleaseBranches.length - failedBranches.length}/${selectedReleaseBranches.length} merges completed successfully.`,\n )\n } else {\n logger.info('\u2705 All merges completed successfully!\\n')\n }\n\n commandEcho.print()\n\n const structuredContent = {\n successfulMerges: selectedReleaseBranches.length - failedBranches.length,\n failedMerges: failedBranches.length,\n failedBranches,\n totalBranches: selectedReleaseBranches.length,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\nconst mergeDev = async (branch: string): Promise<boolean> => {\n try {\n await $`git switch ${branch}`\n\n await $`git pull origin ${branch}`\n\n await $`git merge origin/dev --no-edit`\n\n await $`git push origin ${branch}`\n\n await $`git switch dev`\n\n logger.info(`Successfully merged dev into ${branch}`)\n\n return true\n } catch (error: unknown) {\n const err = new OperationError(error, {\n operation: `merge dev into ${branch}`,\n remediation: \"resolve conflicts manually or rerun after 'git fetch origin'\",\n })\n\n logger.error({ error, branch, msg: err.message })\n\n await $`git reset --merge HEAD~1`\n\n return false\n }\n}\n\n// MCP Tool Registration\nexport const ghMergeDevMcpTool = defineMcpTool({\n name: 'gh-merge-dev',\n description:\n 'Merge origin/dev into every open regular (non-hotfix) release branch and push the result. Mutates local git state and the remote release branches. When invoked via MCP, pass all=true \u2014 the branch picker is unreachable without a TTY, and the confirmation prompt is auto-skipped for MCP calls, so the caller is responsible for gating. Irreversible once pushed.',\n inputSchema: {\n all: z\n .boolean()\n .optional()\n .describe(\n 'Target every open regular release branch. Must be true for MCP calls (the interactive picker is unavailable without a TTY).',\n ),\n },\n outputSchema: {\n successfulMerges: z.number().describe('Number of successful merges'),\n failedMerges: z.number().describe('Number of failed merges'),\n failedBranches: z.array(z.string()).describe('List of branches that failed to merge'),\n totalBranches: z.number().describe('Total number of branches processed'),\n },\n handler: ghMergeDev,\n})\n", "import { $ } from 'zx'\n\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { logger } from 'src/lib/logger'\n\n/**\n * Validate GitHub CLI installation and authentication status and throw an error if not valid\n */\nexport const validateGitHubCliAndAuth = async () => {\n try {\n await $`gh --version`\n } catch (error: unknown) {\n logger.error({ error }, 'Error: GitHub CLI (gh) is not installed.')\n throw new OperationError(error, {\n operation: 'verify GitHub CLI is installed',\n remediation: 'install gh from https://cli.github.com/',\n })\n }\n\n try {\n await $`gh auth status`\n } catch (error: unknown) {\n logger.error({ error }, 'Error: GitHub CLI (gh) is not authenticated.')\n throw new OperationError(error, {\n operation: 'verify GitHub CLI authentication',\n remediation: 'run \"gh auth login\" (https://cli.github.com/manual/gh_auth_login)',\n })\n }\n}\n", "import { $ } from 'zx'\n\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { logger } from 'src/lib/logger'\nimport { compareReleaseIds, formatBranchName, formatPrTitle, parseBranchName } from 'src/lib/release-id'\nimport type { ReleaseId } from 'src/lib/release-id'\nimport { getBaseBranch } from 'src/lib/release-utils'\nimport type { ReleaseType } from 'src/lib/release-utils'\n\ninterface ReleasePR {\n headRefName: string\n number: number\n state: string\n title: string\n baseRefName: string\n createdAt: string\n}\n\nexport interface ReleasePRInfo {\n branch: string\n title: string\n createdAt: string\n}\n\n/**\n * Sort release head refs in the locked deterministic order (versions block\n * first by semver ascending, then names by PR creation date). Head refs that\n * are not valid release branches (parseBranchName \u2192 null) are filtered out\n * rather than throwing or NaN-sorting, so a stray junk branch can never break\n * discovery. Carries each PR's createdAt for name ordering.\n */\nconst sortReleasePRs = (prs: ReleasePR[]): ReleasePR[] => {\n return prs\n .map((pr) => {\n return { pr, id: parseBranchName(pr.headRefName) }\n })\n .filter((entry): entry is { pr: ReleasePR; id: NonNullable<typeof entry.id> } => {\n return entry.id !== null\n })\n .sort((a, b) => {\n return compareReleaseIds(a.id, b.id, { a: a.pr.createdAt, b: b.pr.createdAt })\n })\n .map((entry) => {\n return entry.pr\n })\n}\n\n/**\n * Fetch all open release/hotfix PRs from GitHub.\n * Searches both dev (regular) and main (hotfix) base branches.\n * Returns deduplicated ReleasePR objects.\n */\nconst fetchAllReleasePRs = async (): Promise<ReleasePR[]> => {\n const releasePRs =\n await $`gh pr list --search \"Release in:title\" --base dev --json number,title,headRefName,state,baseRefName,createdAt`\n\n const hotfixPRs =\n await $`gh pr list --search \"Hotfix in:title\" --base main --json number,title,headRefName,state,baseRefName,createdAt`\n\n const all: ReleasePR[] = [...JSON.parse(releasePRs.stdout), ...JSON.parse(hotfixPRs.stdout)]\n\n // Deduplicate by headRefName\n const seen = new Set<string>()\n\n return all.filter((pr) => {\n if (seen.has(pr.headRefName)) return false\n\n seen.add(pr.headRefName)\n\n return true\n })\n}\n\n/**\n * Fetch, guard against empty, and sort all open release PRs in the locked\n * deterministic order. Shared core of the two public variants below; wraps the\n * gh calls so a fetch failure surfaces as an OperationError.\n */\nconst loadSortedReleasePRs = async (): Promise<ReleasePR[]> => {\n try {\n const prs = await fetchAllReleasePRs()\n\n if (prs.length === 0) {\n throw new OperationError(undefined, {\n operation: 'find open release PRs',\n remediation: 'open a release PR first, or check you are in the right repo',\n })\n }\n\n return sortReleasePRs(prs)\n } catch (error) {\n if (error instanceof OperationError) throw error\n\n logger.error({ error }, '\u274C Error fetching release PRs')\n\n throw new OperationError(error, { operation: 'fetch release PRs' })\n }\n}\n\n/**\n * Fetch open release PRs from GitHub with 'Release' or 'Hotfix' in the title.\n * Returns an array of headRefName strings in the locked deterministic order\n * (version branches first by semver ascending, then named branches by PR\n * creation date). Unparseable head refs are filtered out.\n *\n * @returns [release/v1.18.22, release/v1.18.23, release/checkout-redesign]\n */\nexport const getReleasePRs = async (): Promise<string[]> => {\n const prs = await loadSortedReleasePRs()\n\n return prs.map((pr) => {\n return pr.headRefName\n })\n}\n\n/**\n * Fetch open release PRs with title info (for detecting release type).\n * Returns ReleasePRInfo objects in the locked deterministic order (version\n * branches first by semver ascending, then named branches by PR creation\n * date). Unparseable head refs are filtered out.\n */\nexport const getReleasePRsWithInfo = async (): Promise<ReleasePRInfo[]> => {\n const prs = await loadSortedReleasePRs()\n\n return prs.map((pr) => {\n return {\n branch: pr.headRefName,\n title: pr.title,\n createdAt: pr.createdAt,\n }\n })\n}\n\ninterface UpdateReleasePRBodyArgs {\n branch: string\n body: string\n}\n\n/**\n * Update the body of an open release PR identified by its head branch.\n */\nexport const updateReleasePRBody = async (args: UpdateReleasePRBodyArgs): Promise<void> => {\n const { branch, body } = args\n\n try {\n $.quiet = true\n await $`gh pr edit ${branch} --body ${body}`\n $.quiet = false\n } catch (error: unknown) {\n logger.error({ error, branch }, `Error updating release PR body for ${branch}`)\n throw error\n }\n}\n\ninterface CreateReleaseBranchArgs {\n id: ReleaseId\n jiraVersionUrl: string\n type: ReleaseType\n description?: string\n}\n\n// Function to create a release branch\nexport const createReleaseBranch = async (\n args: CreateReleaseBranchArgs,\n): Promise<{ branchName: string; prUrl: string }> => {\n const { id, jiraVersionUrl, type, description } = args\n const prTitle = formatPrTitle(id, type)\n const baseBranch = getBaseBranch(type)\n\n const branchName = formatBranchName(id)\n\n const body = description && description.trim() !== '' ? `${jiraVersionUrl}\\n\\n${description}` : `${jiraVersionUrl} \\n`\n\n try {\n $.quiet = true\n\n await $`git switch ${baseBranch}`\n await $`git pull origin ${baseBranch}`\n await $`git checkout -b ${branchName}`\n await $`git push -u origin ${branchName}`\n await $`git commit --allow-empty-message --allow-empty --message ''`\n await $`git push origin ${branchName}`\n\n // Create PR and capture URL\n const prResult = await $`gh pr create --title ${prTitle} --body ${body} --base ${baseBranch} --head ${branchName}`\n\n const prLink = prResult.stdout.trim()\n\n await $`git switch ${baseBranch}`\n\n $.quiet = false\n\n return {\n branchName,\n prUrl: prLink,\n }\n } catch (error: unknown) {\n logger.error({ error, branchName }, `Error creating release branch ${branchName}`)\n\n throw error\n }\n}\n", "import { $ } from 'zx'\n\nimport { createReleaseBranch } from 'src/integrations/gh'\nimport { createJiraVersion, getProjectVersions, loadJiraConfigOptional } from 'src/integrations/jira'\nimport type { JiraConfig } from 'src/integrations/jira'\nimport { OperationError } from 'src/lib/errors/operation-error'\n// Type-only cross-layer import: src/lib/prompts/types.ts is intentionally a\n// zero-import leaf so it can be imported from any layer (including this one)\n// without creating a cycle.\nimport type { BranchPickerItem } from 'src/lib/prompts/types'\nimport { displayLabel, formatBranchName, formatJiraName, parseBranchName, parseReleaseRef } from 'src/lib/release-id'\nimport type { ReleaseId } from 'src/lib/release-id'\n\n/** Sentinel ref for deploying from the `dev` branch instead of a release branch. */\nexport const DEV_REF = 'dev'\n\nexport type ReleaseType = 'regular' | 'hotfix'\n\n/**\n * Get the base branch for a release type.\n * Regular releases branch from/to dev, hotfixes branch from/to main.\n */\nexport const getBaseBranch = (type: ReleaseType): string => {\n return type === 'hotfix' ? 'main' : 'dev'\n}\n\nexport interface ReleaseCreationResult {\n version: string\n type: ReleaseType\n branchName: string\n prUrl: string\n jiraVersionUrl: string\n}\n\n/**\n * Prepare git repository for release creation\n * Fetches latest changes, switches to base branch, and pulls latest\n */\nexport const prepareGitForRelease = async (type: ReleaseType = 'regular'): Promise<void> => {\n const baseBranch = getBaseBranch(type)\n\n $.quiet = true\n\n await $`git fetch origin`\n await $`git switch ${baseBranch}`\n await $`git pull origin ${baseBranch}`\n\n $.quiet = false\n}\n\ninterface CreateSingleReleaseArgs {\n id: ReleaseId\n jiraConfig: JiraConfig\n description?: string\n type?: ReleaseType\n}\n\n/**\n * Create a single release by creating both Jira version and GitHub release branch\n */\nexport const createSingleRelease = async (args: CreateSingleReleaseArgs): Promise<ReleaseCreationResult> => {\n const { id, jiraConfig, description, type = 'regular' } = args\n // 1. Create Jira version (mandatory). For versioned releases this is\n // \"v1.2.3\" (byte-identical to before); for named releases it is \"<name>\".\n const versionName = formatJiraName(id)\n\n const result = await createJiraVersion(\n {\n name: versionName,\n projectId: jiraConfig.projectId,\n description: description || '',\n released: false,\n archived: false,\n },\n jiraConfig,\n )\n\n // Construct user-friendly Jira URL using project key from API response\n const jiraVersionUrl = `${jiraConfig.baseUrl}/projects/${result.version!.projectId}/versions/${result.version!.id}/tab/release-report-all-issues`\n\n // 2. Create GitHub release branch\n const releaseInfo = await createReleaseBranch({ id, jiraVersionUrl, type, description })\n\n return {\n version: displayLabel(id),\n type,\n branchName: releaseInfo.branchName,\n prUrl: releaseInfo.prUrl,\n jiraVersionUrl,\n }\n}\n\n/**\n * Fetch Jira version descriptions mapped by version name (e.g., \"v1.2.5\" \u2192 \"Some description\")\n * Gracefully returns empty map if Jira is unavailable\n */\nexport const getJiraDescriptions = async (): Promise<Map<string, string>> => {\n const descriptions = new Map<string, string>()\n\n const jiraConfig = await loadJiraConfigOptional()\n\n if (!jiraConfig) return descriptions\n\n try {\n const versions = await getProjectVersions(jiraConfig)\n\n for (const version of versions) {\n if (version.description) {\n descriptions.set(version.name, version.description)\n }\n }\n } catch {\n // Jira fetch failed, continue without descriptions\n }\n\n return descriptions\n}\n\n/**\n * Format a version string with its release type tag, e.g. \"1.2.5 [regular]\"\n * When maxVersionLength is provided, pads the version for alignment.\n */\nexport const formatVersionLabel = (version: string, type: ReleaseType, maxVersionLength?: number): string => {\n const padding = maxVersionLength ? ' '.repeat(maxVersionLength - version.length + 3) : ' '\n const tag = `[${type}]`.padEnd(11)\n\n return `${version}${padding}${tag}`\n}\n\n/**\n * Detect release type from PR title.\n * PRs titled \"Hotfix v...\" are hotfix, everything else is regular.\n */\nexport const detectReleaseType = (title: string): ReleaseType => {\n return title.toLowerCase().startsWith('hotfix') ? 'hotfix' : 'regular'\n}\n\ninterface FormatBranchChoicesArgs {\n branches: string[]\n descriptions: Map<string, string>\n types?: Map<string, ReleaseType>\n}\n\ninterface ParsedBranchChoice {\n branch: string\n id: ReleaseId\n /** Human display label: `1.2.3` | `<name>`. */\n label: string\n}\n\n/**\n * Parse branches into release ids, dropping any that do not parse (lenient\n * discovery source). Exported for unit testing the version/name/junk split.\n */\nexport const parseBranchChoices = (branches: string[]): ParsedBranchChoice[] => {\n return branches.flatMap((branch) => {\n const id = parseBranchName(branch)\n\n if (!id) return []\n\n return [{ branch, id, label: displayLabel(id) }]\n })\n}\n\n/**\n * Resolve an operator-supplied release ref (version `1.2.3` / `v1.2.3` or name\n * `checkout-redesign`) to its branch name (`release/v1.2.3` | `release/<name>`).\n * Strict: surfaces a parse failure as an OperationError with remediation text.\n */\nexport const resolveReleaseBranch = (versionArg: string): string => {\n try {\n return formatBranchName(parseReleaseRef(versionArg))\n } catch (error) {\n throw new OperationError(error, {\n operation: `resolve release ref \"${versionArg}\"`,\n remediation: 'pass a version (e.g. \"1.2.5\") or a release name (e.g. \"checkout-redesign\")',\n })\n }\n}\n\n/**\n * Render the human display label for a release branch. Returns the `dev`\n * sentinel unchanged; otherwise derives `1.2.3` | `<name>` from the branch.\n * Falls back to the raw branch when it does not parse as a release id.\n */\nexport const releaseLabelFromBranch = (branch: string): string => {\n if (branch === DEV_REF) return DEV_REF\n\n const id = parseBranchName(branch)\n\n return id ? displayLabel(id) : branch\n}\n\n/**\n * Render human display labels for a list of release branches, dropping any\n * branch that does not parse as a release id (lenient discovery contract).\n */\nexport const releaseBranchLabels = (branches: string[]): string[] => {\n return branches.flatMap((branch) => {\n const id = parseBranchName(branch)\n\n return id ? [displayLabel(id)] : []\n })\n}\n\nconst deriveBranchType = (branch: string, types?: Map<string, ReleaseType>): ReleaseType | undefined => {\n return types ? types.get(branch) || 'regular' : undefined\n}\n\n/**\n * Format release branch names as Ink picker items. Labels and descriptions\n * are returned as separate fields \u2014 the picker renders and pads them itself.\n */\nexport const formatBranchPickerItems = (args: FormatBranchChoicesArgs): BranchPickerItem[] => {\n const { branches, descriptions, types } = args\n\n return parseBranchChoices(branches).map(({ branch, id, label }) => {\n return {\n value: branch,\n label,\n // Jira-descriptions map is keyed by the Jira version NAME (`v1.2.3` | `<name>`).\n description: descriptions.get(formatJiraName(id)),\n type: deriveBranchType(branch, types),\n }\n })\n}\n", "import process from 'node:process'\n\nimport { logger } from 'src/lib/logger'\n\nimport type {\n CreateJiraVersionParams,\n CreateJiraVersionResult,\n DeliverJiraReleaseParams,\n DeliverJiraReleaseResult,\n JiraConfig,\n JiraVersion,\n UpdateJiraVersionParams,\n UpdateJiraVersionResult,\n} from './types.js'\n\n/**\n * Throws a normalized error when a Jira API response is not OK, logging the\n * status, statusText, and response body under the given context message.\n * @param response - The fetch Response to check\n * @param context - Log message describing the failed operation\n */\nconst assertJiraOk = async (response: Response, context: string): Promise<void> => {\n if (response.ok) {\n return\n }\n\n const errorText = await response.text()\n\n logger.error(\n {\n status: response.status,\n statusText: response.statusText,\n error: errorText,\n },\n context,\n )\n\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n}\n\n/**\n * Creates a new version in Jira using the REST API\n * @param params - Version creation parameters\n * @param config - Jira configuration (baseUrl, token, projectId)\n * @returns Result containing created version or error\n */\nexport const createJiraVersion = async (\n params: CreateJiraVersionParams,\n config: JiraConfig,\n): Promise<CreateJiraVersionResult> => {\n try {\n const { baseUrl, token, email, projectId } = config\n\n const requestBody = {\n name: params.name,\n projectId: params.projectId || projectId,\n description: params.description || '',\n released: params.released || false,\n archived: params.archived || false,\n }\n\n const url = `${baseUrl}/rest/api/3/version`\n\n // Create Basic auth credentials\n const credentials = btoa(`${email}:${token}`)\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n Authorization: `Basic ${credentials}`,\n },\n body: JSON.stringify(requestBody),\n })\n\n await assertJiraOk(response, 'Failed to create Jira version')\n\n const version = (await response.json()) as JiraVersion\n\n return {\n success: true,\n version,\n }\n } catch (error) {\n logger.error({ error }, 'Error creating Jira version')\n\n throw error\n }\n}\n\n/**\n * Gets all versions for a project from Jira\n * @param config - Jira configuration\n * @returns Array of JiraVersion objects\n */\nexport const getProjectVersions = async (config: JiraConfig): Promise<JiraVersion[]> => {\n try {\n const { baseUrl, token, email, projectId } = config\n\n const url = `${baseUrl}/rest/api/3/project/${projectId}/versions`\n const credentials = btoa(`${email}:${token}`)\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n Authorization: `Basic ${credentials}`,\n },\n })\n\n await assertJiraOk(response, 'Failed to get Jira project versions')\n\n const versions = (await response.json()) as JiraVersion[]\n\n return versions\n } catch (error) {\n logger.error({ error }, 'Error getting Jira project versions')\n\n throw error\n }\n}\n\n/**\n * Finds a Jira version by name in the project\n * @param versionName - Name of the version to find (e.g., \"v1.33.10\")\n * @param config - Jira configuration\n * @returns JiraVersion if found, null otherwise\n */\nexport const findVersionByName = async (versionName: string, config: JiraConfig): Promise<JiraVersion | null> => {\n try {\n const versions = await getProjectVersions(config)\n const version = versions.find((v) => {\n return v.name === versionName\n })\n\n return version || null\n } catch (error) {\n logger.error({ error, versionName }, 'Error finding Jira version by name')\n\n throw error\n }\n}\n\n/**\n * Updates an existing Jira version\n * @param params - Update parameters\n * @param config - Jira configuration\n * @returns Result containing updated version or error\n */\nexport const updateJiraVersion = async (\n params: UpdateJiraVersionParams,\n config: JiraConfig,\n): Promise<UpdateJiraVersionResult> => {\n try {\n const { baseUrl, token, email } = config\n\n // Only include fields the caller explicitly passed.\n const requestBody: Record<string, any> = {}\n\n if (params.released !== undefined) requestBody.released = params.released\n if (params.archived !== undefined) requestBody.archived = params.archived\n if (params.releaseDate !== undefined) requestBody.releaseDate = params.releaseDate\n if (params.description !== undefined) requestBody.description = params.description\n\n const url = `${baseUrl}/rest/api/3/version/${params.versionId}`\n const credentials = btoa(`${email}:${token}`)\n\n const response = await fetch(url, {\n method: 'PUT',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n Authorization: `Basic ${credentials}`,\n },\n body: JSON.stringify(requestBody),\n })\n\n await assertJiraOk(response, 'Failed to update Jira version')\n\n const version = (await response.json()) as JiraVersion\n\n return {\n success: true,\n version,\n }\n } catch (error) {\n logger.error({ error }, 'Error updating Jira version')\n\n throw error\n }\n}\n\n/**\n * Delivers a Jira release by marking it as released with the current date\n * @param params - Parameters containing the version name\n * @param config - Jira configuration\n * @returns Result containing updated version\n * @throws Error if version not found or update fails\n */\nexport const deliverJiraRelease = async (\n params: DeliverJiraReleaseParams,\n config: JiraConfig,\n): Promise<DeliverJiraReleaseResult> => {\n try {\n const { versionName } = params\n\n // Find the version by name\n const version = await findVersionByName(versionName, config)\n\n if (!version) {\n logger.error({ versionName }, 'Jira version not found')\n throw new Error(`Version \"${versionName}\" not found in Jira project`)\n }\n\n // Update the version to mark it as released\n const result = await updateJiraVersion(\n {\n versionId: version.id,\n released: true,\n releaseDate: new Date().toISOString().split('T')[0], // Current date in YYYY-MM-DD format\n },\n config,\n )\n\n return result\n } catch (error) {\n logger.error({ error }, 'Error delivering Jira release')\n throw error\n }\n}\n\n/**\n * Loads Jira configuration from environment variables\n * @throws Error with detailed message if configuration is missing or invalid\n * @returns Promise<JiraConfig>\n */\nexport const loadJiraConfig = async (): Promise<JiraConfig> => {\n const baseUrl = process.env.JIRA_BASE_URL\n const token = process.env.JIRA_TOKEN || process.env.JIRA_API_TOKEN\n const projectIdStr = process.env.JIRA_PROJECT_ID\n const email = process.env.JIRA_EMAIL\n\n const missingVars: string[] = []\n\n if (!baseUrl) missingVars.push('JIRA_BASE_URL (e.g., https://your-domain.atlassian.net)')\n if (!token) missingVars.push('JIRA_TOKEN or JIRA_API_TOKEN (your Jira API token)')\n if (!projectIdStr) missingVars.push('JIRA_PROJECT_ID (numeric project ID)')\n if (!email) missingVars.push('JIRA_EMAIL (your Jira email address)')\n\n if (missingVars.length > 0) {\n const errorMessage = [\n 'Jira configuration is required but incomplete.',\n 'Please configure the following environment variables:',\n ...missingVars.map((v) => {\n return ` - ${v}`\n }),\n '',\n 'You can set these in your .env file or as environment variables.',\n ].join('\\n')\n\n throw new Error(errorMessage)\n }\n\n const projectId = Number.parseInt(projectIdStr!, 10)\n\n if (Number.isNaN(projectId)) {\n throw new TypeError(`Invalid JIRA_PROJECT_ID: \"${projectIdStr}\" must be a numeric value (e.g., 10001)`)\n }\n\n return {\n baseUrl: baseUrl!.replace(/\\/$/, ''), // Remove trailing slash\n token: token!,\n projectId,\n email: email!,\n }\n}\n\n/**\n * Attempts to load Jira configuration from environment variables\n * Returns null if configuration is missing or invalid (for optional Jira integration)\n * @returns Promise<JiraConfig | null>\n */\nexport const loadJiraConfigOptional = async (): Promise<JiraConfig | null> => {\n try {\n const config = await loadJiraConfig()\n\n return config\n } catch (error) {\n logger.warn({ error }, 'Jira configuration not available, skipping Jira integration')\n\n return null\n }\n}\n", "import { OperationError } from 'src/lib/errors/operation-error'\nimport { getCurrentBranch, isInsideLinkedWorktree, isWorkingTreeClean } from 'src/lib/git-utils'\n\nexport interface AssertManagementContextArgs {\n /** Operation name surfaced in the failure message, e.g. 'create release'. */\n operation: string\n /**\n * Canonical branch the command must run from (e.g. 'dev' / 'main'). Omit to\n * skip the branch check for commands that are branch-agnostic or self-switch.\n */\n requiredBranch?: string\n}\n\n/**\n * Guard release- and worktree-management commands so they run only from the\n * main repository checkout, with a clean working tree, and (where applicable)\n * on the canonical branch.\n *\n * Checks run most-structural-first \u2014 worktree, then branch, then clean \u2014 so the\n * operator fixes the most fundamental problem first. The worktree and clean-tree\n * checks always run; the branch check runs only when `requiredBranch` is set.\n * Throws {@link OperationError} on the first violation, which surfaces uniformly\n * to both CLI users and MCP-connected agents.\n */\nexport const assertManagementContext = async (args: AssertManagementContextArgs): Promise<void> => {\n const { operation, requiredBranch } = args\n\n if (await isInsideLinkedWorktree()) {\n throw new OperationError(undefined, {\n operation,\n remediation: 'run this from the main repository checkout, not a linked git worktree',\n stderrExcerpt: 'command run from inside a linked worktree',\n })\n }\n\n if (requiredBranch) {\n const currentBranch = await getCurrentBranch()\n\n if (currentBranch !== requiredBranch) {\n throw new OperationError(undefined, {\n operation,\n remediation: `switch to ${requiredBranch} first (git switch ${requiredBranch})`,\n stderrExcerpt: `current branch is \"${currentBranch}\", expected \"${requiredBranch}\"`,\n })\n }\n }\n\n if (!(await isWorkingTreeClean())) {\n throw new OperationError(undefined, {\n operation,\n remediation: 'commit or stash your changes, then retry',\n stderrExcerpt: 'working tree has uncommitted changes',\n })\n }\n}\n", "import process from 'node:process'\n\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { PromptCancelledError } from 'src/lib/errors/prompt-cancelled-error'\nimport { jsonOutput } from 'src/lib/json-output'\n\nimport type { BranchPickerItem } from './types'\n\n/**\n * Gate the interactive Ink picker. Bails (throwing a clear `OperationError`,\n * never loading React) when there is no interactive input OR when structured\n * output is requested \u2014 `--json` is registered on every command and sets\n * `jsonOutput.enabled`, so `--json` on a TTY without an explicit arg would\n * otherwise pass a TTY-only gate and dynamically import the TUI (leaking React\n * onto the `--json` path).\n *\n * The predicate is `stdin.isTTY` only \u2014 deliberately NOT the palette's\n * `stdout && stdin`. These prompts render to stderr, so an interactive run whose\n * STDOUT is redirected (e.g. `worktrees-remove > log.txt`) must still prompt;\n * requiring `stdout.isTTY` would regress that. Input-side interactivity is what\n * the picker actually needs.\n *\n * The `!process.stdin.isTTY` clause is LOAD-BEARING, not defensive:\n * `worktrees-add` and `gh-merge-dev` are `mcpExposed: true` with ALL of their\n * branch inputs `.optional()`, so an MCP call with those args omitted DOES enter\n * the interactive branch \u2014 only the absent TTY on the MCP server stops the\n * prompt (and stops React from being loaded there). Do not remove it.\n */\nconst assertInteractive = () => {\n if (!process.stdin.isTTY || jsonOutput.enabled) {\n throw new OperationError(undefined, {\n operation: 'interactive branch selection',\n remediation:\n 'pass the branch selection explicitly (CLI: `--version`/`--versions`/`--all`; MCP: the `version`/`versions`/`all` fields) for non-interactive, --json, or MCP runs',\n })\n }\n}\n\n/**\n * Prompt for a single release branch via the searchable Ink picker. Throws an\n * `OperationError` on non-interactive/`--json`/MCP runs (see `assertInteractive`)\n * and a `PromptCancelledError` if the user backs out with Esc/Ctrl-C. The Ink\n * TUI is reached only through a dynamic import so React never loads on the\n * bail paths.\n */\nexport const pickReleaseBranch = async (items: BranchPickerItem[]): Promise<string> => {\n assertInteractive()\n const { runBranchPicker } = await import('src/tui/boot')\n const value = await runBranchPicker(items)\n\n if (value === null) throw new PromptCancelledError()\n\n return value\n}\n\n/**\n * Prompt for one or more release branches via the searchable Ink multi-picker.\n * Same guard and cancellation semantics as {@link pickReleaseBranch}; returns\n * the selected values.\n */\nexport const pickReleaseBranches = async (\n items: BranchPickerItem[],\n opts?: { required?: boolean; allowSelectAll?: boolean },\n): Promise<string[]> => {\n assertInteractive()\n const { runBranchMultiPicker } = await import('src/tui/boot')\n const values = await runBranchMultiPicker(items, opts)\n\n if (values === null) throw new PromptCancelledError()\n\n return values\n}\n", "/**\n * Thrown by the Ink branch pickers (via the `release-picker` shim) when the user\n * presses Esc or Ctrl-C.\n *\n * The name is honest \u2014 `PromptCancelledError`, not a borrowed `@inquirer/core`\n * class name. It is registered in `CANCELLATION_ERROR_NAMES` so\n * {@link ./is-prompt-cancellation.isPromptCancellation} recognises it through the\n * same by-name mechanism it already uses for the `@inquirer` cancel errors. That\n * keeps the contract honest if `@inquirer` is ever dropped, and survives anyone\n * adding an `instanceof` fast-path for the real inquirer classes.\n *\n * @example\n * try {\n * await pickReleaseBranch(items)\n * } catch (err) {\n * if (isPromptCancellation(err)) process.exit(0) // user backed out\n * throw err\n * }\n */\nexport class PromptCancelledError extends Error {\n override name = 'PromptCancelledError'\n\n constructor() {\n super('Prompt cancelled')\n }\n}\n", "import type { Command } from 'commander'\nimport process from 'node:process'\n\n/**\n * `--json` output mode for the CLI. This is a presentation concern only: every\n * command handler already returns a `structuredContent` payload (the same one\n * the MCP surface consumes). Human/log output goes to stderr (see lib/logger),\n * so writing the structured payload to stdout never collides with it.\n */\n\nexport interface CommandResult {\n structuredContent?: unknown\n}\n\n/** Mutable holder (object so `prefer-const` holds while the flag toggles per run). */\nexport const jsonOutput = { enabled: false }\n\n/**\n * In `--json` mode, write a command result's `structuredContent` to stdout as\n * pretty JSON and return the result unchanged. Outside `--json` mode it is a\n * no-op pass-through. The writer is injectable for testing.\n */\nexport const emit = <T extends CommandResult | void>(\n result: T,\n write: (text: string) => void = (text) => {\n process.stdout.write(text)\n },\n): T => {\n if (jsonOutput.enabled && result && result.structuredContent != null) {\n write(`${JSON.stringify(result.structuredContent, null, 2)}\\n`)\n }\n\n return result\n}\n\n/** Register `--json` on a command and every nested subcommand (idempotent). */\nexport const addJsonOption = (cmd: Command): void => {\n const alreadyHasJson = cmd.options.some((option) => {\n return option.long === '--json'\n })\n\n if (!alreadyHasJson) {\n cmd.option('--json', 'Output the structured result as JSON on stdout (human logs stay on stderr)')\n }\n\n cmd.commands.forEach(addJsonOption)\n}\n", "import { z } from 'zod'\nimport { $ } from 'zx'\n\nimport { getReleasePRsWithInfo } from 'src/integrations/gh'\nimport { deliverJiraRelease, loadJiraConfigOptional } from 'src/integrations/jira'\nimport { commandEcho, confirmOrExit } from 'src/lib/command-echo'\nimport { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'\nimport { formatZxError } from 'src/lib/errors/format-zx-error'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { assertManagementContext } from 'src/lib/git-guard'\nimport {\n deleteLocalBranch,\n deleteRemoteBranch,\n getCurrentWorktrees,\n getProjectRoot,\n getRepoName,\n} from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { pickReleaseBranch } from 'src/lib/prompts/release-picker'\nimport { displayLabel, formatJiraName, formatRcTitle, parseBranchName } from 'src/lib/release-id'\nimport type { ReleaseId } from 'src/lib/release-id'\nimport {\n detectReleaseType,\n formatBranchPickerItems,\n getJiraDescriptions,\n resolveReleaseBranch,\n} from 'src/lib/release-utils'\nimport type { ReleaseType } from 'src/lib/release-utils'\nimport { removeWorktrees } from 'src/lib/worktrees'\nimport { defineMcpTool, textContent } from 'src/types'\nimport type { RequiredConfirmedOptionArg } from 'src/types'\n\ninterface GhReleaseDeliverArgs extends RequiredConfirmedOptionArg {\n version: string\n}\n\ntype PRState = 'OPEN' | 'MERGED' | 'CLOSED'\n\ninterface PRStatus {\n number: number\n state: PRState\n title: string\n}\n\n/**\n * Wrap a delivery step so its failure logs structured zx fields and surfaces\n * an `OperationError` whose message names the actual step that failed \u2014\n * instead of the previous blanket \"merging release branch into dev\" message.\n */\nconst runStep = async <T>(operation: string, remediation: string, fn: () => Promise<T>): Promise<T> => {\n try {\n return await fn()\n } catch (error) {\n logger.error({ err: formatZxError(error) }, `\u274C Failed to ${operation}`)\n throw new OperationError(error, { operation, remediation })\n }\n}\n\n/**\n * Fetch the (most-recent) PR for the given head branch, across all states, so\n * we can resume a partially-completed delivery: a PR merged on a prior attempt\n * still appears here as `state: 'MERGED'`, letting the caller skip the merge.\n */\nconst fetchPRByHead = async (head: string): Promise<PRStatus | null> => {\n const result = await $`gh pr list --head ${head} --state all --json number,state,title --limit 1`\n const prs = JSON.parse(result.stdout) as PRStatus[]\n\n return prs[0] ?? null\n}\n\n/**\n * Find a MERGED RC PR (dev \u2192 main) whose title matches the given version, used\n * to detect that a prior delivery run already merged the RC and we should skip\n * the merge step on resume. Title-matched on purpose: an older MERGED RC PR\n * from a different release must not short-circuit this version's flow.\n */\nconst fetchMergedRcPRForVersion = async (id: ReleaseId): Promise<PRStatus | null> => {\n const expectedTitle = formatRcTitle(id)\n const result = await $`gh pr list --head dev --base main --state merged --json number,state,title --limit 20`\n const prs = JSON.parse(result.stdout) as PRStatus[]\n const match = prs.find((pr) => {\n return pr.title === expectedTitle\n })\n\n return match ?? null\n}\n\n/**\n * Find an open dev \u2192 main PR, if any. GitHub allows at most one open PR per\n * head/base pair, so an existing open PR here is the RC PR for this release \u2014\n * even if its title was set by a previous (failed) delivery run. Adopting it\n * is what makes the flow recoverable after a mid-run failure.\n */\nconst fetchOpenDevToMainPR = async (): Promise<PRStatus | null> => {\n const result = await $`gh pr list --head dev --base main --state open --json number,state,title --limit 5`\n const prs = JSON.parse(result.stdout) as PRStatus[]\n\n return prs[0] ?? null\n}\n\ninterface ResolvedTarget {\n selectedReleaseBranch: string\n releasePrTitle: string\n}\n\nconst resolveTargetFromVersion = async (version: string): Promise<ResolvedTarget> => {\n const selectedReleaseBranch = resolveReleaseBranch(version)\n const pr = await fetchPRByHead(selectedReleaseBranch)\n\n if (!pr) {\n logger.error(`\u274C No PR found for branch ${selectedReleaseBranch}.`)\n throw new OperationError(undefined, {\n operation: `deliver release ${selectedReleaseBranch}`,\n remediation: `confirm a PR exists ('gh pr list --head ${selectedReleaseBranch} --state all')`,\n })\n }\n\n return { selectedReleaseBranch, releasePrTitle: pr.title }\n}\n\nconst resolveTargetInteractively = async (): Promise<ResolvedTarget> => {\n const releasePRsInfo = await getReleasePRsWithInfo()\n\n const branches = releasePRsInfo.map((pr) => {\n return pr.branch\n })\n\n const releaseTypes = new Map<string, ReleaseType>(\n releasePRsInfo.map((pr) => {\n return [pr.branch, detectReleaseType(pr.title)]\n }),\n )\n\n commandEcho.setInteractive()\n\n const descriptions = await getJiraDescriptions()\n\n const selectedReleaseBranch = await pickReleaseBranch(\n formatBranchPickerItems({ branches, descriptions, types: releaseTypes }),\n )\n\n const prInfo = releasePRsInfo.find((pr) => {\n return pr.branch === selectedReleaseBranch\n })\n\n if (!prInfo) {\n logger.error(`\u274C Release branch ${selectedReleaseBranch} not found in open PRs.`)\n throw new OperationError(undefined, {\n operation: `deliver release ${selectedReleaseBranch}`,\n remediation: `confirm an open PR exists for ${selectedReleaseBranch} ('gh pr list')`,\n })\n }\n\n return { selectedReleaseBranch, releasePrTitle: prInfo.title }\n}\n\n/**\n * `gh pr merge --delete-branch` also deletes the local branch, which fails if a\n * worktree has it checked out (the actual root cause of the \"Failed to merge\n * release PR\" surface error). Pre-remove any worktree for the release branch\n * so the local delete can succeed.\n */\nconst removeReleaseWorktreeIfPresent = async (releaseBranch: string): Promise<void> => {\n const worktreeBranches = await getCurrentWorktrees('release')\n\n if (!worktreeBranches.includes(releaseBranch)) return\n\n const [projectRoot, repoName] = await Promise.all([getProjectRoot(), getRepoName()])\n const worktreeDir = `${projectRoot}${WORKTREES_DIR_SUFFIX}`\n\n const removed = await removeWorktrees({ branches: [releaseBranch], worktreeDir, repoName })\n\n if (removed.length === 0) {\n throw new OperationError(undefined, {\n operation: `remove worktree for ${releaseBranch} before merge`,\n remediation: `run manually: git worktree remove ${worktreeDir}/${releaseBranch} (use --force if uncommitted changes)`,\n })\n }\n}\n\ninterface MergeReleasePRArgs {\n selectedReleaseBranch: string\n releaseType: ReleaseType\n}\n\nconst mergeReleasePR = async (args: MergeReleasePRArgs): Promise<void> => {\n const { selectedReleaseBranch, releaseType } = args\n\n const mergeTarget = releaseType === 'hotfix' ? 'main' : 'dev'\n\n const releasePr = await fetchPRByHead(selectedReleaseBranch)\n\n if (!releasePr) {\n throw new OperationError(undefined, {\n operation: `look up release PR for ${selectedReleaseBranch}`,\n remediation: `verify the PR exists in GitHub`,\n })\n }\n\n if (releasePr.state === 'MERGED') {\n logger.info(`\u2713 Release PR ${selectedReleaseBranch} already merged \u2014 skipping`)\n\n return\n }\n\n if (releasePr.state === 'CLOSED') {\n throw new OperationError(undefined, {\n operation: `merge release PR ${selectedReleaseBranch} into ${mergeTarget}`,\n remediation: `the PR is closed without merge; reopen it or create a new release`,\n })\n }\n\n await runStep(\n `merge release PR ${selectedReleaseBranch} into ${mergeTarget}`,\n `check 'gh pr view ${selectedReleaseBranch}' for mergeability and required reviews`,\n async () => {\n await $`gh pr merge ${selectedReleaseBranch} --squash --admin --delete-branch`\n },\n )\n}\n\nconst resolveRcPRNumber = async (id: ReleaseId): Promise<number> => {\n const selectedLabel = displayLabel(id)\n const expectedTitle = formatRcTitle(id)\n const existingOpen = await fetchOpenDevToMainPR()\n\n // Adopt any existing open dev\u2192main PR. GitHub permits only one open PR per\n // head/base pair, so a stale open RC PR (left behind by a prior failed run\n // \u2014 the single most common cause of \"Error merging release branch into\n // dev\") blocks `gh pr create`. Retitle it instead of fighting it.\n if (existingOpen) {\n const rcNumber = existingOpen.number\n\n if (existingOpen.title !== expectedTitle) {\n logger.info(\n `Adopting open dev \u2192 main PR #${rcNumber} (\"${existingOpen.title}\") and retitling for ${selectedLabel}`,\n )\n await runStep(\n `retitle dev \u2192 main PR #${rcNumber} to \"${expectedTitle}\"`,\n `update manually: gh pr edit ${rcNumber} --title \"${expectedTitle}\"`,\n async () => {\n await $`gh pr edit ${rcNumber} --title ${expectedTitle}`\n },\n )\n }\n\n return rcNumber\n }\n\n await runStep(\n `create RC PR (dev \u2192 main) for ${selectedLabel}`,\n `run 'gh pr create --base main --head dev' manually to surface the underlying error (e.g. no commits between dev and main)`,\n async () => {\n await $`gh pr create --base main --head dev --title ${expectedTitle} --body \"\"`\n },\n )\n\n const created = await fetchOpenDevToMainPR()\n\n if (!created) {\n throw new OperationError(undefined, {\n operation: `look up RC PR for ${selectedLabel}`,\n remediation: `verify the RC PR was created ('gh pr list --head dev --base main')`,\n })\n }\n\n return created.number\n}\n\nconst ensureRcPRMerged = async (id: ReleaseId): Promise<void> => {\n const selectedLabel = displayLabel(id)\n const alreadyMerged = await fetchMergedRcPRForVersion(id)\n\n if (alreadyMerged) {\n logger.info(`\u2713 RC PR for ${selectedLabel} already merged into main \u2014 skipping`)\n\n return\n }\n\n const rcNumber = await resolveRcPRNumber(id)\n\n await runStep(\n `merge RC PR #${rcNumber} (dev \u2192 main) for ${selectedLabel}`,\n `check 'gh pr view ${rcNumber}' for mergeability and required reviews`,\n async () => {\n await $`gh pr merge ${rcNumber} --squash --admin`\n },\n )\n}\n\nconst dispatchDeployWorkflow = async (): Promise<void> => {\n $.quiet = false\n\n await runStep(\n `dispatch deploy-all workflow on main`,\n `check 'gh workflow list' and that you have permission to dispatch deploy-all.yml`,\n async () => {\n await $`gh workflow run deploy-all.yml --ref main -f environment=prod`\n },\n )\n\n $.quiet = true\n}\n\nconst syncMainIntoDev = async (): Promise<void> => {\n await runStep(\n `sync main back into dev`,\n `run manually: git switch main && git pull && git switch dev && git pull && git merge main --no-edit && git push`,\n async () => {\n await $`git switch main && git pull && git switch dev && git pull && git merge main --no-edit && git push`\n },\n )\n}\n\n/**\n * Best-effort cleanup of the delivered release branch, locally and on `origin`.\n *\n * Idempotent backstop to `gh pr merge --delete-branch` (in `mergeReleasePR`):\n * that flag is skipped when the release PR was already merged on a prior\n * attempt, so an idempotent re-run would otherwise leave the branch behind.\n * Each delete is isolated \u2014 a failure (e.g. the branch checked out in a stray\n * worktree, or a remote hiccup) logs a warning naming the branch and never\n * aborts delivery, since the irreversible merge and deploy already happened.\n */\nconst removeDeliveredReleaseBranch = async (branch: string): Promise<void> => {\n try {\n await deleteLocalBranch(branch)\n } catch (error) {\n logger.warn({ err: formatZxError(error) }, `Failed to delete local branch ${branch} (non-blocking)`)\n }\n\n try {\n await deleteRemoteBranch(branch)\n } catch (error) {\n logger.warn({ err: formatZxError(error) }, `Failed to delete remote branch ${branch} (non-blocking)`)\n }\n}\n\nconst deliverJiraReleaseSafely = async (id: ReleaseId): Promise<void> => {\n const jiraConfig = await loadJiraConfigOptional()\n\n if (!jiraConfig) {\n logger.info('\uD83D\uDD14 Jira is not configured, skipping Jira release delivery')\n\n return\n }\n\n try {\n // Jira fix version name: `v1.2.3` | `<name>` \u2014 must match create-time formatJiraName.\n const versionName = formatJiraName(id)\n\n await deliverJiraRelease({ versionName }, jiraConfig)\n } catch (error) {\n logger.error({ err: formatZxError(error) }, 'Failed to deliver Jira release (non-blocking)')\n }\n}\n\n/**\n * Deliver a release branch to production. Each network/git step is run inside\n * `runStep` so the surfaced error names the failing operation and includes the\n * subprocess stderr. PR-merge steps are idempotent: if the release PR or RC PR\n * is already MERGED, the step is skipped, so re-running after a mid-flight\n * failure picks up where it stopped.\n */\nexport const ghReleaseDeliver = async (args: GhReleaseDeliverArgs) => {\n const { version, confirmedCommand } = args\n\n // Branch-agnostic (operates on release/RC PRs via gh and self-switches), so\n // only the worktree + clean-tree legs apply.\n await assertManagementContext({ operation: 'deliver release' })\n\n const { selectedReleaseBranch, releasePrTitle } = version\n ? await resolveTargetFromVersion(version)\n : await resolveTargetInteractively()\n\n // selectedReleaseBranch is always a release branch (operator ref strictly\n // parsed, or picked from discovery-filtered choices) so this cannot be null.\n const releaseId = parseBranchName(selectedReleaseBranch)\n\n if (!releaseId) {\n throw new OperationError(undefined, {\n operation: `deliver release ${selectedReleaseBranch}`,\n remediation: 'pass a version (e.g. \"1.2.5\") or a release name (e.g. \"checkout-redesign\")',\n })\n }\n\n const selectedVersion = displayLabel(releaseId)\n\n commandEcho.addOption('--version', selectedVersion)\n logger.info(`Delivering ${releaseId.kind === 'name' ? 'named release' : 'version'} ${selectedReleaseBranch}`)\n\n const releaseType: ReleaseType = detectReleaseType(releasePrTitle)\n\n await confirmOrExit(\n confirmedCommand,\n `Are you sure you want to deliver version ${selectedReleaseBranch} to production?`,\n )\n\n // Track --yes flag if confirmation was interactive (user confirmed)\n commandEcho.addOption('--yes', true)\n\n $.quiet = true\n\n await removeReleaseWorktreeIfPresent(selectedReleaseBranch)\n await mergeReleasePR({ selectedReleaseBranch, releaseType })\n\n if (releaseType !== 'hotfix') {\n await ensureRcPRMerged(releaseId)\n }\n\n await dispatchDeployWorkflow()\n await syncMainIntoDev()\n await removeDeliveredReleaseBranch(selectedReleaseBranch)\n\n $.quiet = false\n\n await deliverJiraReleaseSafely(releaseId)\n\n logger.info(`Successfully delivered ${selectedReleaseBranch} to production!`)\n\n commandEcho.print()\n\n const structuredContent = {\n releaseBranch: selectedReleaseBranch,\n version: selectedVersion,\n type: releaseType,\n success: true,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\n// MCP Tool Registration\nexport const ghReleaseDeliverMcpTool = defineMcpTool({\n name: 'gh-release-deliver',\n description:\n 'Deliver a release to production. For hotfixes: squash-merges the release branch to main and dispatches the deploy-all workflow. For regular releases: squash-merges to dev, opens an RC PR, merges dev into main, dispatches the deploy-all workflow, then syncs main back to dev. Also releases the matching Jira fix version if Jira is configured. Dispatches the deploy workflow fire-and-forget \u2014 the tool returns once the workflow is accepted by GitHub, not when the deployment finishes. PR-merge steps are idempotent: re-running after a partial failure skips PRs that are already merged. Irreversible production operation: the confirmation prompt is auto-skipped for MCP calls, so the caller is responsible for gating. \"version\" is required when invoked via MCP (the picker is unreachable without a TTY).',\n inputSchema: {\n version: z\n .string()\n .describe(\n 'Accepts a release version (e.g. \"1.2.5\") OR a release name (e.g. \"checkout-redesign\") to deliver to production. Required for MCP calls.',\n ),\n },\n outputSchema: {\n releaseBranch: z.string().describe('The release branch that was delivered'),\n version: z.string().describe('The version that was delivered'),\n type: z.enum(['regular', 'hotfix']).describe('Release type'),\n success: z.boolean().describe('Whether the delivery was successful'),\n },\n handler: ghReleaseDeliver,\n})\n", "const STDERR_EXCERPT_MAX_BYTES = 500\nconst STDOUT_EXCERPT_MAX_BYTES = 200\n\nexport interface ZxErrorFields {\n exitCode?: number | null\n stderr?: string\n stdout?: string\n message?: string\n name?: string\n}\n\nconst readTrimmedString = (value: unknown, max: number): string | undefined => {\n if (typeof value !== 'string' || value.length === 0) return undefined\n\n return value.slice(-max).trim()\n}\n\n/**\n * Extract loggable fields from an error for `logger.error({ err: formatZxError(e) }, ...)`.\n *\n * pino's default `err` serializer handles `Error` subclasses but renders zx's\n * `ProcessOutput` as `{}` because its informative fields (`stderr`, `stdout`,\n * `exitCode`) are non-enumerable / on the prototype. This helper duck-types\n * those fields so subprocess failures surface in logs instead of vanishing.\n */\nexport const formatZxError = (error: unknown): ZxErrorFields => {\n if (error === null || typeof error !== 'object') {\n return { message: String(error) }\n }\n\n const rec = error as Record<string, unknown>\n const fields: ZxErrorFields = {}\n\n if (error instanceof Error) {\n fields.name = error.name\n fields.message = error.message\n } else if (typeof rec.message === 'string') {\n fields.message = rec.message\n }\n\n const exitCode = rec.exitCode\n\n if (typeof exitCode === 'number' || exitCode === null) fields.exitCode = exitCode\n\n const stderr = readTrimmedString(rec.stderr, STDERR_EXCERPT_MAX_BYTES)\n\n if (stderr) fields.stderr = stderr\n\n const stdout = readTrimmedString(rec.stdout, STDOUT_EXCERPT_MAX_BYTES)\n\n if (stdout) fields.stdout = stdout\n\n return fields\n}\n", "import { $ } from 'zx'\n\nimport { buildCmuxWorkspaceTitle, closeCmuxWorkspaceByTitle } from 'src/integrations/cmux'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { logger } from 'src/lib/logger'\n\ninterface RemoveWorktreesArgs {\n branches: string[]\n worktreeDir: string\n repoName: string\n pruneFolder?: boolean\n}\n\n/**\n * Close any cmux workspace for each branch and run `git worktree remove`,\n * returning the branches that were removed cleanly. Failures are logged but\n * never thrown, so a single bad worktree doesn't poison a batch removal.\n *\n * When `pruneFolder` is true and every branch was removed, also run\n * `git worktree prune` to clear stale worktree metadata. The `<repo>-worktrees`\n * container directory and its `release/`/`feature/` subfolders are deliberately\n * left in place so the per-repo worktree scaffold persists even when empty.\n */\nexport const removeWorktrees = async (args: RemoveWorktreesArgs): Promise<string[]> => {\n const { branches, worktreeDir, repoName, pruneFolder = false } = args\n\n const results = await Promise.allSettled(\n branches.map(async (branch) => {\n const worktreePath = `${worktreeDir}/${branch}`\n\n const title = buildCmuxWorkspaceTitle({ repoName, branch })\n\n await closeCmuxWorkspaceByTitle(title)\n\n await $`git worktree remove ${worktreePath}`\n\n return branch\n }),\n )\n\n const removed: string[] = []\n\n for (const [index, result] of results.entries()) {\n if (result.status === 'fulfilled') {\n removed.push(result.value)\n } else {\n const branch = branches[index]\n const err = new OperationError(result.reason, {\n operation: `remove worktree for ${branch}`,\n remediation: \"check 'git worktree list' for the path; uncommitted changes block removal\",\n })\n\n logger.error({ error: result.reason, msg: err.message })\n }\n }\n\n // `git worktree remove` deletes only the leaf worktree. We intentionally leave\n // the `<repo>-worktrees` container and its `release/`/`feature/` group folders\n // in place so the worktree scaffold survives an empty state (it is recreated\n // lazily by `worktrees-add` via `mkdir -p` regardless).\n if (pruneFolder && removed.length === branches.length) {\n await $`git worktree prune`\n }\n\n return removed\n}\n", "import confirm from '@inquirer/confirm'\nimport select from '@inquirer/select'\nimport { z } from 'zod'\nimport { $ } from 'zx'\n\nimport { getReleasePRsWithInfo } from 'src/integrations/gh'\nimport { commandEcho } from 'src/lib/command-echo'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { pickReleaseBranch } from 'src/lib/prompts/release-picker'\nimport {\n detectReleaseType,\n formatBranchPickerItems,\n getJiraDescriptions,\n releaseLabelFromBranch,\n resolveReleaseBranch,\n} from 'src/lib/release-utils'\nimport type { ReleaseType } from 'src/lib/release-utils'\nimport { defineMcpTool, textContent } from 'src/types'\n\ninterface GhReleaseDeployAllArgs {\n version: string\n env: string\n skipTerraform?: boolean\n confirmedCommand?: boolean\n}\n\ninterface ConfirmDeployArgs {\n confirmedCommand?: boolean\n branch: string\n env: string\n}\n\n/**\n * Gate the workflow dispatch behind an interactive confirmation. Returns true to\n * proceed; `confirmedCommand` (CLI `--yes`) skips the prompt. Mirrors the\n * confirm+commandEcho pattern used by worktrees-remove.\n */\nconst confirmDeploy = async (args: ConfirmDeployArgs): Promise<boolean> => {\n const { confirmedCommand, branch, env } = args\n\n if (confirmedCommand) return true\n\n commandEcho.setInteractive()\n\n const answer = await confirm({ message: `Deploy ${branch} \u2192 ${env}?`, default: false })\n\n if (answer) commandEcho.addOption('--yes', true)\n\n return answer\n}\n\n/**\n * Deploy a release branch to an environment\n */\nexport const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs) => {\n const { version, env, skipTerraform, confirmedCommand } = args\n\n let selectedReleaseBranch = '' // \"release/v1.8.0\" | \"release/checkout-redesign\" | \"dev\"\n\n if (version) {\n selectedReleaseBranch = version === 'dev' ? 'dev' : resolveReleaseBranch(version)\n } else {\n commandEcho.setInteractive()\n\n const releasePRsInfo = await getReleasePRsWithInfo()\n\n const branches = releasePRsInfo.map((pr) => {\n return pr.branch\n })\n\n const releaseTypes = new Map<string, ReleaseType>(\n releasePRsInfo.map((pr) => {\n return [pr.branch, detectReleaseType(pr.title)]\n }),\n )\n\n const descriptions = await getJiraDescriptions()\n\n selectedReleaseBranch = await pickReleaseBranch([\n { value: 'dev', label: 'dev' },\n ...formatBranchPickerItems({ branches, descriptions, types: releaseTypes }),\n ])\n }\n\n const selectedVersion = releaseLabelFromBranch(selectedReleaseBranch)\n\n commandEcho.addOption('--version', selectedVersion)\n\n const { environments } = await getInfraKitConfig()\n\n let selectedEnv = ''\n\n if (env) {\n selectedEnv = env\n } else {\n commandEcho.setInteractive()\n\n selectedEnv = await select({\n message: '\uD83E\uDDEA Select environment',\n choices: environments.map((env) => {\n return {\n name: env,\n value: env,\n }\n }),\n })\n }\n\n commandEcho.addOption('--env', selectedEnv)\n\n if (!environments.includes(selectedEnv)) {\n throw new OperationError(undefined, {\n operation: 'launch deploy-all workflow',\n remediation: `pass one of: ${environments.join(', ')}`,\n stderrExcerpt: `invalid environment: ${selectedEnv}`,\n })\n }\n\n const shouldSkipTerraform = skipTerraform ?? false\n\n if (shouldSkipTerraform) {\n commandEcho.addOption('--skip-terraform', true)\n }\n\n if (!(await confirmDeploy({ confirmedCommand, branch: selectedReleaseBranch, env: selectedEnv }))) {\n logger.info('Deployment cancelled')\n\n const structuredContent = {\n releaseBranch: selectedReleaseBranch,\n version: selectedVersion,\n environment: selectedEnv,\n skipTerraformDeploy: shouldSkipTerraform,\n success: false,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n }\n\n try {\n $.quiet = true\n\n const skipTerraformFlag = shouldSkipTerraform ? ['-f', 'skip_terraform_deploy=true'] : []\n\n await $`gh workflow run deploy-all.yml --ref ${selectedReleaseBranch} -f environment=${selectedEnv} ${skipTerraformFlag}`\n\n $.quiet = false\n\n logger.info(\n `Successfully launched deploy-all workflow_dispatch for release branch: ${selectedReleaseBranch} and environment: ${selectedEnv}`,\n )\n\n commandEcho.print()\n\n const structuredContent = {\n releaseBranch: selectedReleaseBranch,\n version: selectedVersion,\n environment: selectedEnv,\n skipTerraformDeploy: shouldSkipTerraform,\n success: true,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n } catch (error: unknown) {\n logger.error({ error }, '\u274C Error launching workflow')\n throw new OperationError(error, {\n operation: 'launch deploy-all workflow',\n remediation: \"check 'gh workflow list' and that deploy-all.yml exists on the target ref\",\n })\n }\n}\n\n// MCP Tool Registration\nexport const ghReleaseDeployAllMcpTool = defineMcpTool({\n name: 'gh-release-deploy-all',\n description:\n 'Dispatch the deploy-all.yml GitHub Actions workflow to deploy every service from a release branch to the given environment. Fire-and-forget \u2014 returns once GitHub accepts the workflow_dispatch, NOT when the deployment finishes; watch the workflow run for completion status. Use gh-release-deploy-selected for a subset of services. Pass version=\"dev\" to deploy from the dev branch instead of a release branch. Both \"version\" and \"env\" are required when invoked via MCP (interactive pickers are unavailable without a TTY).',\n inputSchema: {\n version: z\n .string()\n .describe(\n 'Accepts a release version (e.g. \"1.2.5\") OR a release name (e.g. \"checkout-redesign\") \u2014 resolves to the release/vX.Y.Z or release/<name> branch. Pass \"dev\" to deploy from the dev branch instead. Required for MCP calls.',\n ),\n env: z\n .string()\n .describe(\n 'Target environment name \u2014 must match an env configured for the project (e.g. \"dev\", \"renana\", \"oriana\"). Required for MCP calls.',\n ),\n skipTerraform: z.boolean().optional().describe('Skip the terraform deployment stage.'),\n },\n outputSchema: {\n releaseBranch: z.string().describe('The release branch that was deployed'),\n version: z.string().describe('The version that was deployed'),\n environment: z.string().describe('The environment deployed to'),\n skipTerraformDeploy: z.boolean().describe('Whether terraform deployment was skipped'),\n success: z.boolean().describe('Whether the deployment was successful'),\n },\n handler: ghReleaseDeployAll,\n})\n", "import checkbox from '@inquirer/checkbox'\nimport confirm from '@inquirer/confirm'\nimport select from '@inquirer/select'\nimport fs from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport yaml from 'yaml'\nimport { z } from 'zod'\nimport { $ } from 'zx'\n\nimport { getReleasePRsWithInfo } from 'src/integrations/gh'\nimport { commandEcho } from 'src/lib/command-echo'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { getProjectRoot } from 'src/lib/git-utils'\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { pickReleaseBranch } from 'src/lib/prompts/release-picker'\nimport {\n detectReleaseType,\n formatBranchPickerItems,\n getJiraDescriptions,\n releaseLabelFromBranch,\n resolveReleaseBranch,\n} from 'src/lib/release-utils'\nimport type { ReleaseType } from 'src/lib/release-utils'\nimport { defineMcpTool, textContent } from 'src/types'\n\ninterface GhReleaseDeploySelectedArgs {\n version: string\n env: string\n services: string[]\n skipTerraform?: boolean\n confirmedCommand?: boolean\n}\n\ninterface ConfirmDeployArgs {\n confirmedCommand?: boolean\n branch: string\n env: string\n}\n\n/**\n * Gate the workflow dispatch behind an interactive confirmation. Returns true to\n * proceed; `confirmedCommand` (CLI `--yes`) skips the prompt. Mirrors the\n * confirm+commandEcho pattern used by worktrees-remove.\n */\nconst confirmDeploy = async (args: ConfirmDeployArgs): Promise<boolean> => {\n const { confirmedCommand, branch, env } = args\n\n if (confirmedCommand) return true\n\n commandEcho.setInteractive()\n\n const answer = await confirm({ message: `Deploy ${branch} \u2192 ${env}?`, default: false })\n\n if (answer) commandEcho.addOption('--yes', true)\n\n return answer\n}\n\n/**\n * Deploy selected services from a release branch to an environment\n */\n// eslint-disable-next-line sonarjs/cognitive-complexity\nexport const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs) => {\n const { version, env, services, skipTerraform, confirmedCommand } = args\n\n let selectedReleaseBranch = ''\n\n if (version) {\n selectedReleaseBranch = version === 'dev' ? 'dev' : resolveReleaseBranch(version)\n } else {\n commandEcho.setInteractive()\n\n const releasePRsInfo = await getReleasePRsWithInfo()\n\n const branches = releasePRsInfo.map((pr) => {\n return pr.branch\n })\n\n const releaseTypes = new Map<string, ReleaseType>(\n releasePRsInfo.map((pr) => {\n return [pr.branch, detectReleaseType(pr.title)]\n }),\n )\n\n const descriptions = await getJiraDescriptions()\n\n selectedReleaseBranch = await pickReleaseBranch([\n { value: 'dev', label: 'dev' },\n ...formatBranchPickerItems({ branches, descriptions, types: releaseTypes }),\n ])\n }\n\n const selectedVersion = releaseLabelFromBranch(selectedReleaseBranch)\n\n commandEcho.addOption('--version', selectedVersion)\n\n const { environments } = await getInfraKitConfig()\n\n let selectedEnv = ''\n\n if (env) {\n selectedEnv = env\n } else {\n commandEcho.setInteractive()\n\n selectedEnv = await select({\n message: '\uD83E\uDDEA Select environment',\n choices: environments.map((env) => {\n return {\n name: env,\n value: env,\n }\n }),\n })\n }\n\n commandEcho.addOption('--env', selectedEnv)\n\n if (!environments.includes(selectedEnv)) {\n throw new OperationError(undefined, {\n operation: 'launch deploy-selected workflow',\n remediation: `pass one of: ${environments.join(', ')}`,\n stderrExcerpt: `invalid environment: ${selectedEnv}`,\n })\n }\n\n // Parse available services from workflow file\n const availableServices = await parseServicesFromWorkflow()\n\n if (availableServices.length === 0) {\n throw new OperationError(undefined, {\n operation: 'launch deploy-selected workflow',\n remediation: 'check .github/workflows/deploy-selected-services.yml for boolean service inputs',\n stderrExcerpt: 'no services found in workflow file',\n })\n }\n\n let selectedServices: string[] = []\n\n if (services && services.length > 0) {\n selectedServices = services\n } else {\n commandEcho.setInteractive()\n\n selectedServices = await checkbox({\n message: '\uD83D\uDE80 Select services to deploy (space to select, enter to confirm)',\n choices: availableServices.map((svc) => {\n return {\n name: svc,\n value: svc,\n }\n }),\n })\n }\n\n commandEcho.addOption('--services', selectedServices)\n\n if (selectedServices.length === 0) {\n throw new OperationError(undefined, {\n operation: 'launch deploy-selected workflow',\n remediation: `pass at least one service from: ${availableServices.join(', ')}`,\n stderrExcerpt: 'no services selected',\n })\n }\n\n // Validate all selected services\n const invalidServices = selectedServices.filter((svc) => {\n return !availableServices.includes(svc)\n })\n\n if (invalidServices.length > 0) {\n throw new OperationError(undefined, {\n operation: 'launch deploy-selected workflow',\n remediation: `pass services from: ${availableServices.join(', ')}`,\n stderrExcerpt: `invalid services: ${invalidServices.join(', ')}`,\n })\n }\n\n const shouldSkipTerraform = skipTerraform ?? false\n\n if (shouldSkipTerraform) {\n commandEcho.addOption('--skip-terraform', true)\n }\n\n const buildResult = (success: boolean) => {\n const structuredContent = {\n releaseBranch: selectedReleaseBranch,\n version: selectedVersion,\n environment: selectedEnv,\n services: selectedServices,\n skipTerraformDeploy: shouldSkipTerraform,\n success,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n }\n\n if (!(await confirmDeploy({ confirmedCommand, branch: selectedReleaseBranch, env: selectedEnv }))) {\n logger.info('Deployment cancelled')\n\n return buildResult(false)\n }\n\n try {\n $.quiet = true\n\n // Build the workflow command with boolean flags for each selected service\n const serviceFlags = selectedServices.flatMap((svc) => {\n return ['-f', `${svc}=true`]\n })\n const skipTerraformFlag = shouldSkipTerraform ? ['-f', 'skip_terraform_deploy=true'] : []\n\n await $`gh workflow run deploy-selected-services.yml --ref ${selectedReleaseBranch} -f environment=${selectedEnv} ${serviceFlags} ${skipTerraformFlag}`\n\n $.quiet = false\n\n logger.info(\n `Successfully launched deploy-selected-services workflow_dispatch for release branch: ${selectedReleaseBranch}, environment: ${selectedEnv}, services: ${selectedServices.join(', ')}`,\n )\n\n commandEcho.print()\n\n return buildResult(true)\n } catch (error: unknown) {\n logger.error({ error }, '\u274C Error launching workflow')\n throw new OperationError(error, {\n operation: 'launch deploy-selected workflow',\n remediation: \"check 'gh workflow list' and that deploy-selected-services.yml exists on the target ref\",\n })\n }\n}\n\n/**\n * Parse available services from the workflow file\n * Services are defined as boolean inputs (excluding skip_terraform_deploy)\n */\nconst parseServicesFromWorkflow = async (): Promise<string[]> => {\n const projectRoot = await getProjectRoot()\n\n const workflowPath = resolve(projectRoot, '.github/workflows/deploy-selected-services.yml')\n\n const content = await fs.readFile(workflowPath, 'utf-8')\n const parsed = yaml.parse(content)\n\n const inputs = parsed.on.workflow_dispatch.inputs\n const services: string[] = []\n\n for (const [key, value] of Object.entries(inputs)) {\n // Filter for boolean type inputs, excluding non-service flags\n if ((value as { type: string }).type === 'boolean' && key !== 'skip_terraform_deploy') {\n services.push(key)\n }\n }\n\n return services\n}\n\n// MCP Tool Registration\nexport const ghReleaseDeploySelectedMcpTool = defineMcpTool({\n name: 'gh-release-deploy-selected',\n description:\n 'Dispatch the deploy-selected-services.yml GitHub Actions workflow to deploy a chosen subset of services from a release branch to the given environment. Fire-and-forget \u2014 returns once GitHub accepts the workflow_dispatch, NOT when the deployment finishes; watch the workflow run for completion status. Service names are validated against the boolean inputs declared in the workflow. Use gh-release-deploy-all for every service. \"version\", \"env\", and \"services\" are all required when invoked via MCP (interactive pickers are unavailable without a TTY).',\n inputSchema: {\n version: z\n .string()\n .describe(\n 'Accepts a release version (e.g. \"1.2.5\") OR a release name (e.g. \"checkout-redesign\") \u2014 resolves to the release/vX.Y.Z or release/<name> branch. Pass \"dev\" to deploy from the dev branch instead. Required for MCP calls.',\n ),\n env: z\n .string()\n .describe(\n 'Target environment name \u2014 must match an env configured for the project (e.g. \"dev\", \"renana\", \"oriana\"). Required for MCP calls.',\n ),\n services: z\n .array(z.string())\n .describe(\n 'Service names to deploy. Each must match a boolean input declared in .github/workflows/deploy-selected-services.yml (e.g. \"client-be\", \"client-fe\"). Required for MCP calls.',\n ),\n skipTerraform: z.boolean().optional().describe('Skip the terraform deployment stage.'),\n },\n outputSchema: {\n releaseBranch: z.string().describe('The release branch that was deployed'),\n version: z.string().describe('The version that was deployed'),\n environment: z.string().describe('The environment deployed to'),\n services: z.array(z.string()).describe('The services that were deployed'),\n skipTerraformDeploy: z.boolean().describe('Whether terraform deployment was skipped'),\n success: z.boolean().describe('Whether the deployment was successful'),\n },\n handler: ghReleaseDeploySelected,\n})\n", "import { z } from 'zod'\n\nimport { getReleasePRsWithInfo } from 'src/integrations/gh'\nimport { logger } from 'src/lib/logger'\nimport { displayLabel, formatJiraName, parseBranchName } from 'src/lib/release-id'\nimport { detectReleaseType, formatVersionLabel, getJiraDescriptions } from 'src/lib/release-utils'\nimport { defineMcpTool, textContent } from 'src/types'\n\n/**\n * List all open release branches\n */\nexport const ghReleaseList = async () => {\n const releasePRs = await getReleasePRsWithInfo()\n\n // Skip branches that do not parse as release ids (lenient discovery source).\n const releases = releasePRs.flatMap((pr) => {\n const id = parseBranchName(pr.branch)\n\n if (!id) return []\n\n return [\n {\n // Human display label: `1.2.3` | `<name>`.\n version: displayLabel(id),\n // Jira-descriptions map is keyed by the Jira version NAME (`v1.2.3` | `<name>`).\n jiraKey: formatJiraName(id),\n type: detectReleaseType(pr.title),\n },\n ]\n })\n\n const jiraDescriptions = await getJiraDescriptions()\n\n const maxVersionLength = Math.max(\n ...releases.map((r) => {\n return r.version.length\n }),\n )\n\n const formattedLines = releases.map((release) => {\n const label = formatVersionLabel(release.version, release.type, maxVersionLength)\n const description = jiraDescriptions.get(release.jiraKey)\n\n if (description) {\n return `${label} ${description}`\n }\n\n return label\n })\n\n logger.info('All release branches: \\n')\n logger.info(`\\n${formattedLines.join('\\n')}\\n`)\n\n const structuredContent = {\n releases: releases.map((release) => {\n return {\n version: release.version,\n type: release.type,\n description: jiraDescriptions.get(release.jiraKey) || null,\n }\n }),\n count: releases.length,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\n// MCP Tool Registration\nexport const ghReleaseListMcpTool = defineMcpTool({\n name: 'gh-release-list',\n description:\n 'List every open release PR with its version, type (regular / hotfix), and associated Jira fix-version description. Read-only; sourced from GitHub and Jira.',\n inputSchema: {},\n outputSchema: {\n releases: z\n .array(\n z.object({\n version: z.string().describe('Release version'),\n type: z.enum(['regular', 'hotfix']).describe('Release type'),\n description: z.string().nullable().describe('Jira version description'),\n }),\n )\n .describe('List of all release branches'),\n count: z.number().describe('Number of release branches'),\n },\n handler: ghReleaseList,\n})\n", "import confirm from '@inquirer/confirm'\nimport select from '@inquirer/select'\nimport process from 'node:process'\nimport { z } from 'zod'\nimport { question } from 'zx'\n\nimport { loadJiraConfig } from 'src/integrations/jira'\nimport { commandEcho, confirmOrExit } from 'src/lib/command-echo'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { assertManagementContext } from 'src/lib/git-guard'\nimport { logger } from 'src/lib/logger'\nimport { InvalidReleaseNameError, displayLabel, validateName } from 'src/lib/release-id'\nimport { createSingleRelease, getBaseBranch, prepareGitForRelease } from 'src/lib/release-utils'\nimport type { ReleaseCreationResult, ReleaseType } from 'src/lib/release-utils'\nimport {\n NoPriorVersionsError,\n computeNextVersion,\n formatReleaseSpec,\n hasNextToken,\n loadExistingVersions,\n parseVersion,\n resolveReleaseEntries,\n} from 'src/lib/version-utils'\nimport type { ReleaseEntry, ReleaseInput, SemVer } from 'src/lib/version-utils'\nimport { defineMcpTool, textContent } from 'src/types'\nimport type { RequiredConfirmedOptionArg } from 'src/types'\n\ninterface ReleaseCreateArgs extends RequiredConfirmedOptionArg {\n releases?: ReleaseInput[]\n}\n\nconst VERSION_PROMPT_HINT = '\"1.2.5\" or \"next\"'\n\nconst trySuggestNext = (known: SemVer[], type: ReleaseType): string | null => {\n try {\n return computeNextVersion(known, type)\n } catch (err) {\n if (err instanceof NoPriorVersionsError) return null\n\n throw err\n }\n}\n\nconst resolveOrExit = (entries: ReleaseInput[], known: SemVer[]): ReleaseEntry[] => {\n try {\n return resolveReleaseEntries(entries, known)\n } catch (err) {\n if (err instanceof NoPriorVersionsError) {\n throw new OperationError(err, {\n operation: 'resolve release version',\n remediation: 'pass an explicit version (e.g. \"1.2.5\") instead of \"next\" when there are no prior versions',\n })\n }\n\n if (err instanceof InvalidReleaseNameError) {\n throw new OperationError(err, {\n operation: 'validate release name',\n remediation:\n 'use a kebab-case name like \"checkout-redesign\" (lowercase, digits, single hyphens, not a reserved word)',\n })\n }\n\n throw err\n }\n}\n\nconst promptForVersionInput = async (running: SemVer[], type: ReleaseType): Promise<string> => {\n const suggestion = trySuggestNext(running, type)\n const defaultHint = suggestion ? ` [${suggestion}]` : ''\n const versionAnswer = (await question(` Version (e.g. ${VERSION_PROMPT_HINT})${defaultHint}: `)).trim()\n const versionInput = versionAnswer === '' ? (suggestion ?? '') : versionAnswer\n\n if (versionInput === '') {\n logger.error('No version provided. Exiting...')\n process.exit(1)\n }\n\n return versionInput\n}\n\nconst promptForNameInput = async (): Promise<string> => {\n const name = (await question(' Name (kebab-case, e.g. \"checkout-redesign\"): ')).trim()\n\n if (name === '') {\n logger.error('No name provided. Exiting...')\n process.exit(1)\n }\n\n try {\n validateName(name)\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err)\n\n logger.error(`${reason} Exiting...`)\n process.exit(1)\n }\n\n return name\n}\n\nconst promptForReleasesInteractive = async (ensureKnown: () => Promise<SemVer[]>): Promise<ReleaseEntry[]> => {\n commandEcho.setInteractive()\n\n let baseKnown: SemVer[] | null = null\n const running: SemVer[] = []\n const ensureRunning = async (): Promise<SemVer[]> => {\n if (baseKnown === null) {\n baseKnown = await ensureKnown()\n running.push(...baseKnown)\n }\n\n return running\n }\n\n const entries: ReleaseEntry[] = []\n let addAnother = true\n\n while (addAnother) {\n const ordinal = entries.length + 1\n const kind = await select<'version' | 'name'>({\n message: `Release #${ordinal} \u2014 version or name?`,\n choices: [\n { name: 'version (semver / next)', value: 'version' },\n { name: 'name (free-form)', value: 'name' },\n ],\n default: 'version',\n })\n\n const type = await select<ReleaseType>({\n message: `Release #${ordinal} \u2014 select type:`,\n choices: [\n { name: 'regular', value: 'regular' },\n { name: 'hotfix', value: 'hotfix' },\n ],\n default: 'regular',\n })\n\n let resolved: ReleaseEntry\n\n if (kind === 'name') {\n const name = await promptForNameInput()\n\n resolved = resolveOrExit([{ name, type }], [])[0] as ReleaseEntry\n } else {\n // Versions may need prior versions for \"next\"; load lazily.\n const versionInput = await promptForVersionInput(await ensureRunning(), type)\n\n resolved = resolveOrExit([{ version: versionInput, type }], running)[0] as ReleaseEntry\n\n if (resolved.id.kind === 'version') {\n running.push(parseVersion(`v${resolved.id.raw}`))\n }\n }\n\n const description = (await question(' Description (optional, press Enter to skip): ')).trim()\n\n entries.push({ ...resolved, ...(description !== '' ? { description } : {}) })\n\n addAnother = await confirm({ message: 'Add another release?', default: false })\n }\n\n return entries\n}\n\nconst formatReleaseSummary = (entry: ReleaseEntry): string => {\n const label = entry.id.kind === 'version' ? `v${entry.id.raw}` : entry.id.name\n const parts = [label, entry.type]\n\n if (entry.description) parts.push(entry.description)\n\n return parts.join(' \u00B7 ')\n}\n\nconst echoReleases = (entries: ReleaseEntry[]): void => {\n // Every entry \u2014 versions, \"next\" (resolved to a concrete version), and named\n // releases alike \u2014 echoes through the single --release flag. formatReleaseSpec\n // produces a spec that parseReleaseSpec parses back into the same entry.\n for (const entry of entries) {\n commandEcho.addOption('--release', formatReleaseSpec(entry))\n }\n}\n\ninterface FailedRelease {\n version: string\n error: string\n}\n\nconst collectEntries = async (\n inputReleases: ReleaseInput[] | undefined,\n ensureKnown: () => Promise<SemVer[]>,\n): Promise<ReleaseEntry[]> => {\n if (inputReleases && inputReleases.length > 0) {\n const known = hasNextToken(inputReleases) ? await ensureKnown() : []\n const resolved = resolveOrExit(inputReleases, known)\n\n echoReleases(resolved)\n\n return resolved\n }\n\n const interactive = await promptForReleasesInteractive(ensureKnown)\n\n echoReleases(interactive)\n\n return interactive\n}\n\n/**\n * Reject a batch that mixes regular and hotfix releases. They branch off\n * different bases (dev vs main), so the batch has no single required branch for\n * the management guard \u2014 they must be created in separate invocations.\n * Exported for unit testing without running the side-effecting handler.\n */\nexport const assertHomogeneousReleaseType = (entries: ReleaseEntry[]): void => {\n const types = new Set(\n entries.map((entry) => {\n return entry.type\n }),\n )\n\n if (types.size > 1) {\n throw new OperationError(undefined, {\n operation: 'create release',\n remediation: 'create regular and hotfix releases in separate invocations',\n stderrExcerpt: 'mixed regular and hotfix releases in one batch are not supported',\n })\n }\n}\n\nconst confirmReleases = async (entries: ReleaseEntry[], confirmedCommand: boolean): Promise<void> => {\n const summary = entries.map(formatReleaseSummary).join('\\n - ')\n\n await confirmOrExit(confirmedCommand, `Create the following ${entries.length} release(s)?\\n - ${summary}\\n`)\n\n commandEcho.addOption('--yes', true)\n}\n\ninterface ExecuteOneArgs {\n entry: ReleaseEntry\n jiraConfig: Awaited<ReturnType<typeof loadJiraConfig>>\n}\n\nconst executeOne = async (\n args: ExecuteOneArgs,\n): Promise<{ result?: ReleaseCreationResult; failure?: FailedRelease }> => {\n const { entry, jiraConfig } = args\n const label = displayLabel(entry.id)\n const prTitleLabel = entry.id.kind === 'version' ? `v${entry.id.raw}` : entry.id.name\n\n try {\n await prepareGitForRelease(entry.type)\n\n const result = await createSingleRelease({\n id: entry.id,\n jiraConfig,\n description: entry.description,\n type: entry.type,\n })\n\n logger.info(`\u2705 Successfully created release: ${prTitleLabel} (${entry.type})`)\n logger.info(`\uD83D\uDD17 GitHub PR: ${result.prUrl}`)\n logger.info(`\uD83D\uDD17 Jira Version: ${result.jiraVersionUrl}\\n`)\n\n return { result }\n } catch (error) {\n const err = new OperationError(error, {\n operation: `create release ${prTitleLabel} (${entry.type})`,\n remediation: 'verify the version or name is unique and the base branch is clean',\n })\n\n logger.error(`\u274C ${err.message}\\n`)\n\n return { failure: { version: label, error: err.message } }\n }\n}\n\nconst logFinalSummary = (total: number, successCount: number, failureCount: number): void => {\n if (successCount === total) {\n logger.info(`\u2705 All ${total} release branch(es) were created successfully.`)\n } else if (successCount > 0) {\n logger.warn(`\u26A0\uFE0F ${successCount} of ${total} release branches were created successfully.`)\n logger.warn(`\u274C ${failureCount} release(s) failed.`)\n } else {\n logger.error(`\u274C All ${total} release branch(es) failed to create.`)\n }\n}\n\n/**\n * Create one or more release branches. Each release carries its own type\n * (regular/hotfix) and optional Jira description. All entries in a single\n * invocation must share the same type \u2014 regular and hotfix releases branch off\n * different bases (dev vs main), so mixed batches are rejected and must be\n * created separately.\n */\nexport const releaseCreate = async (args: ReleaseCreateArgs) => {\n const { releases: inputReleases, confirmedCommand } = args\n\n const jiraConfig = await loadJiraConfig()\n\n let known: SemVer[] | null = null\n const ensureKnown = async (): Promise<SemVer[]> => {\n if (known === null) known = await loadExistingVersions()\n\n return known\n }\n\n const entries = await collectEntries(inputReleases, ensureKnown)\n\n if (entries.length === 0) {\n throw new OperationError(undefined, {\n operation: 'create release',\n remediation: 'pass at least one entry in \"releases\" (e.g. [{ version: \"1.2.5\", type: \"regular\" }])',\n stderrExcerpt: 'no releases provided',\n })\n }\n\n assertHomogeneousReleaseType(entries)\n\n await assertManagementContext({\n operation: 'create release',\n requiredBranch: getBaseBranch(entries[0]!.type),\n })\n\n await confirmReleases(entries, Boolean(confirmedCommand))\n\n const created: ReleaseCreationResult[] = []\n const failed: FailedRelease[] = []\n\n for (const entry of entries) {\n const { result, failure } = await executeOne({ entry, jiraConfig })\n\n if (result) created.push(result)\n if (failure) failed.push(failure)\n }\n\n logFinalSummary(entries.length, created.length, failed.length)\n\n commandEcho.print()\n\n const structuredContent = {\n createdBranches: created.map((r) => {\n return r.branchName\n }),\n successCount: created.length,\n failureCount: failed.length,\n releases: created,\n failedReleases: failed,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\n// MCP Tool Registration\nexport const releaseCreateMcpTool = defineMcpTool({\n name: 'release-create',\n description:\n 'Create one or more releases in a single call. Each entry in \"releases\" carries EITHER a \"version\" (semver or the literal token \"next\") OR a \"name\" (free-form kebab-case identifier) \u2014 exactly one is required and they are mutually exclusive. Each entry also has its own type (regular|hotfix, default regular) and optional description; all entries in one call must share the same type \u2014 mixed regular+hotfix batches are rejected (create them in separate invocations). For each release this tool switches to the appropriate base branch (dev for regular, main for hotfix), cuts the release branch (release/v<semver> for versions, release/<name> for names), opens a GitHub release PR, and creates the matching Jira fix version (v<semver> for versions, <name> for names). The literal token \"next\" auto-increments from the union of remote release branches and Jira fix versions (regular bumps minor + resets patch; hotfix bumps patch on the highest minor); multiple \"next\" tokens advance sequentially. Named releases never auto-bump and \"next\" is version-only. Must be run from the main repository checkout (not a linked worktree) on the matching base branch with a clean working tree. Confirmation is auto-skipped for MCP calls, so the caller is responsible for gating. Continues on per-release failure and reports successes/failures.',\n inputSchema: {\n releases: z\n .array(\n z\n .object({\n version: z\n .string()\n .optional()\n .describe(\n 'Version to create (e.g., \"1.2.5\") or the literal token \"next\" for auto-increment. Mutually exclusive with \"name\".',\n ),\n name: z\n .string()\n .optional()\n .describe(\n 'Free-form kebab-case release name (e.g., \"checkout-redesign\"). Mutually exclusive with \"version\". Named releases never auto-bump.',\n ),\n type: z\n .enum(['regular', 'hotfix'])\n .optional()\n .default('regular')\n .describe('Release type: \"regular\" (branches off dev) or \"hotfix\" (branches off main).'),\n description: z.string().optional().describe('Optional description for the Jira version.'),\n })\n .refine(\n (entry) => {\n return (entry.version === undefined) !== (entry.name === undefined)\n },\n {\n message: 'Each release entry must have exactly one of \"version\" or \"name\" (they are mutually exclusive).',\n },\n )\n .transform((entry): ReleaseInput => {\n return entry.name !== undefined\n ? { name: entry.name, type: entry.type, ...(entry.description ? { description: entry.description } : {}) }\n : {\n version: entry.version as string,\n type: entry.type,\n ...(entry.description ? { description: entry.description } : {}),\n }\n }),\n )\n .min(1)\n .describe(\n 'One or more releases to create. Each entry has exactly one of \"version\" or \"name\", plus its own type and optional description.',\n ),\n },\n outputSchema: {\n createdBranches: z.array(z.string()).describe('List of created release branch names'),\n successCount: z.number().describe('Number of releases created successfully'),\n failureCount: z.number().describe('Number of releases that failed'),\n releases: z\n .array(\n z.object({\n version: z.string().describe('Version number'),\n type: z.enum(['regular', 'hotfix']).describe('Release type'),\n branchName: z.string().describe('Release branch name'),\n prUrl: z.string().describe('GitHub PR URL'),\n jiraVersionUrl: z.string().describe('Jira version URL'),\n }),\n )\n .describe('Detailed information for each created release with URLs'),\n failedReleases: z\n .array(\n z.object({\n version: z.string().describe('Version number that failed'),\n error: z.string().describe('Error message'),\n }),\n )\n .describe('List of releases that failed with error messages'),\n },\n handler: releaseCreate,\n})\n", "import { $ } from 'zx'\n\nimport { getProjectVersions, loadJiraConfigOptional } from 'src/integrations/jira'\nimport { logger } from 'src/lib/logger'\nimport { parseBranchName } from 'src/lib/release-id'\n\nimport { collectKnownVersions } from './next-version'\nimport type { SemVer } from './next-version'\n\n/**\n * Extract version-branch tokens from raw `git ls-remote` stdout. Each line is\n * `<sha>\\t<ref>`; refs are routed through release-id's lenient\n * {@link parseBranchName} and only `kind: 'version'` ids are kept (named\n * `release/<name>` branches are irrelevant to `next`-bump math and are dropped).\n * Returns the no-`v` semver tokens (e.g. `1.2.3`) that\n * {@link collectKnownVersions} parses as versions. Pure \u2014 no I/O \u2014 so it is\n * unit-testable without the network.\n */\nexport const extractVersionBranches = (lsRemoteStdout: string): string[] => {\n return lsRemoteStdout\n .split('\\n')\n .map((line) => {\n const tab = line.indexOf('\\t')\n\n if (tab === -1) return null\n\n return parseBranchName(line.slice(tab + 1))\n })\n .filter((id): id is NonNullable<typeof id> => {\n return id !== null && id.kind === 'version'\n })\n .map((id) => {\n return id.raw\n })\n}\n\nconst parseRemoteRefs = async (): Promise<string[]> => {\n const previousQuiet = $.quiet\n\n try {\n $.quiet = true\n const result = await $`git ls-remote --heads origin 'release/v*'`\n\n return extractVersionBranches(result.stdout)\n } finally {\n $.quiet = previousQuiet\n }\n}\n\nconst fetchJiraVersionNames = async (): Promise<string[]> => {\n const config = await loadJiraConfigOptional()\n\n if (!config) return []\n\n const versions = await getProjectVersions(config)\n\n return versions.map((v) => {\n return v.name\n })\n}\n\n/**\n * Load known release versions from the union of:\n * - remote release branches (`release/v*` on origin)\n * - Jira fix versions (when configured)\n *\n * Each source is queried in parallel; if either fails, we log a warning\n * and continue with the other so a transient outage doesn't block release\n * creation.\n */\nexport const loadExistingVersions = async (): Promise<SemVer[]> => {\n const [branchesResult, jiraResult] = await Promise.allSettled([parseRemoteRefs(), fetchJiraVersionNames()])\n\n if (branchesResult.status === 'rejected') {\n logger.warn({ error: branchesResult.reason }, 'Failed to list remote release branches; continuing without them')\n }\n\n if (jiraResult.status === 'rejected') {\n logger.warn({ error: jiraResult.reason }, 'Failed to fetch Jira versions; continuing without them')\n }\n\n return collectKnownVersions({\n remoteBranches: branchesResult.status === 'fulfilled' ? branchesResult.value : [],\n jiraVersions: jiraResult.status === 'fulfilled' ? jiraResult.value : [],\n })\n}\n", "/**\n * Parse a `v<semver>` token into major, minor, patch numbers.\n *\n * SemVer-ONLY: this assumes a `v<semver>` token and will produce `NaN`s for\n * anything else. Callers that may see branch names or named releases\n * (`release/<name>`) MUST use the `release-id` module (`parseBranchName` /\n * `parseReleaseRef` + `compareReleaseIds`) instead. Only provably names-free\n * callers (the `collectKnownVersions` / `next`-bump math path) may use this.\n */\nexport const parseVersion = (versionStr: string): [number, number, number] => {\n return versionStr.slice(1).split('.').map(Number) as [number, number, number]\n}\n\n/**\n * Sort version strings in ascending order.\n * Note: Returns a new sorted array without mutating the original.\n *\n * SemVer-ONLY: relies on {@link parseVersion} and will NaN-sort named releases.\n * Name-aware callers MUST use `compareReleaseIds` from the `release-id` module.\n */\nexport const sortVersions = (versions: string[]): string[] => {\n return [...versions].sort((a, b) => {\n const [majA, minA, patchA] = parseVersion(a)\n const [majB, minB, patchB] = parseVersion(b)\n\n if (majA !== majB) return (majA ?? 0) - (majB ?? 0)\n if (minA !== minB) return (minA ?? 0) - (minB ?? 0)\n\n return (patchA ?? 0) - (patchB ?? 0)\n })\n}\n", "import { parseReleaseRef, validateName } from 'src/lib/release-id'\nimport type { ReleaseId } from 'src/lib/release-id'\nimport type { ReleaseType } from 'src/lib/release-utils'\n\nimport { parseVersion, sortVersions } from './version-utils'\n\nexport const NEXT_TOKEN = 'next'\n\nexport type SemVer = readonly [number, number, number]\n\nexport interface ExistingVersionsSources {\n remoteBranches?: string[]\n jiraVersions?: string[]\n}\n\nconst VERSION_RE = /^v?(\\d+)\\.(\\d+)\\.(\\d+)$/\n\nconst stripBranchPrefix = (raw: string): string => {\n return raw.replace(/^.*release\\//, '')\n}\n\nconst tryParse = (raw: string): SemVer | null => {\n const cleaned = stripBranchPrefix(raw.trim())\n const match = VERSION_RE.exec(cleaned)\n\n if (!match) return null\n\n return [Number(match[1]), Number(match[2]), Number(match[3])]\n}\n\nconst semverKey = (v: SemVer): string => {\n return `${v[0]}.${v[1]}.${v[2]}`\n}\n\nexport const collectKnownVersions = (sources: ExistingVersionsSources): SemVer[] => {\n const all = [...(sources.remoteBranches ?? []), ...(sources.jiraVersions ?? [])]\n const parsed: SemVer[] = []\n const seen = new Set<string>()\n\n for (const raw of all) {\n const v = tryParse(raw)\n\n if (!v) continue\n\n const key = semverKey(v)\n\n if (seen.has(key)) continue\n\n seen.add(key)\n parsed.push(v)\n }\n\n return sortVersions(\n parsed.map((v) => {\n return semverKey(v)\n }),\n ).map((s) => {\n return parseVersion(`v${s}`)\n })\n}\n\nexport class NoPriorVersionsError extends Error {\n constructor() {\n super('No prior release versions found from git or Jira. Specify the version explicitly.')\n this.name = 'NoPriorVersionsError'\n }\n}\n\n/**\n * Compute the next semantic version based on release type.\n * - regular: bump minor, reset patch to 0\n * - hotfix: bump patch on the highest minor (any patch)\n *\n * Returns the version without the leading \"v\" (e.g. \"1.64.0\").\n */\nexport const computeNextVersion = (known: SemVer[], type: ReleaseType): string => {\n if (known.length === 0) throw new NoPriorVersionsError()\n\n const max = known[known.length - 1] as SemVer\n\n if (type === 'hotfix') {\n const [major, minor] = max\n\n const highestPatchOnMinor = known.reduce((acc, v) => {\n if (v[0] === major && v[1] === minor) return Math.max(acc, v[2])\n\n return acc\n }, 0)\n\n return `${major}.${minor}.${highestPatchOnMinor + 1}`\n }\n\n const [major, minor] = max\n\n return `${major}.${minor + 1}.0`\n}\n\nconst isNextToken = (token: string): boolean => {\n return token.trim().toLowerCase() === NEXT_TOKEN\n}\n\n/**\n * A release spec is the parsed form of a versioned release request: a raw\n * version token (`\"1.2.5\"` or `\"next\"`) plus its type and optional\n * description. This is the unchanged output of {@link parseReleaseSpec} and the\n * versioned-input shape consumed by {@link resolveReleaseEntries}.\n */\nexport interface ReleaseSpec {\n version: string\n type: ReleaseType\n description?: string\n}\n\n/**\n * A named release request: a bare kebab-case name plus its type and optional\n * description. Named releases never auto-bump; `\"next\"` is version-only.\n */\nexport interface NamedReleaseInput {\n name: string\n type: ReleaseType\n description?: string\n}\n\n/** Either a versioned spec or a named release request. */\nexport type ReleaseInput = ReleaseSpec | NamedReleaseInput\n\n/**\n * A fully resolved release entry. The {@link ReleaseId} carries the concrete\n * identity (version or name) and is the single source for branch/PR/Jira\n * formatting via the `release-id` module.\n */\nexport interface ReleaseEntry {\n id: ReleaseId\n type: ReleaseType\n description?: string\n}\n\nconst isReleaseType = (value: string): value is ReleaseType => {\n return value === 'regular' || value === 'hotfix'\n}\n\nconst isNamedReleaseInput = (input: ReleaseInput): input is NamedReleaseInput => {\n return 'name' in input\n}\n\n/**\n * Parse a CLI release spec of the form `<token>[:type[:description]]` into a\n * {@link ReleaseInput}. The token determines the kind: a semver (`\"1.2.5\"`) or\n * the literal `\"next\"` yields a versioned {@link ReleaseSpec}; anything else is\n * treated as a named release ({@link NamedReleaseInput}) \u2014 the name is not\n * validated here, {@link resolveReleaseEntries} runs `validateName` later. Type\n * defaults to \"regular\". Description is everything after the second colon, so\n * colons inside descriptions are preserved.\n */\nexport const parseReleaseSpec = (raw: string): ReleaseInput => {\n const spec = raw.trim()\n\n if (spec === '') throw new Error('Release spec is empty')\n\n const firstColon = spec.indexOf(':')\n let token = spec\n let type: ReleaseType = 'regular'\n let description = ''\n\n if (firstColon !== -1) {\n token = spec.slice(0, firstColon).trim()\n const rest = spec.slice(firstColon + 1)\n const secondColon = rest.indexOf(':')\n const typeRaw = secondColon === -1 ? rest.trim() : rest.slice(0, secondColon).trim()\n\n description = secondColon === -1 ? '' : rest.slice(secondColon + 1).trim()\n const typeLower = typeRaw.toLowerCase()\n\n if (!isReleaseType(typeLower)) {\n throw new Error(`Invalid release type \"${typeRaw}\". Expected \"regular\" or \"hotfix\".`)\n }\n\n type = typeLower\n }\n\n // A semver token or the \"next\" token is a versioned release; everything else\n // is a named release. parseReleaseRef applies the same precedence downstream.\n // Note: tryParse strips a leading \"release/\" prefix, so \"release/1.2.3\" reads\n // as the version 1.2.3 (a real name can never contain a slash anyway).\n if (isNextToken(token) || tryParse(token) !== null) {\n const entry: ReleaseSpec = { version: token, type }\n\n if (description !== '') entry.description = description\n\n return entry\n }\n\n const entry: NamedReleaseInput = { name: token, type }\n\n if (description !== '') entry.description = description\n\n return entry\n}\n\n/**\n * Render a resolved {@link ReleaseEntry} as the canonical `--release` spec that\n * reproduces it \u2014 the inverse of {@link parseReleaseSpec} composed with\n * {@link resolveReleaseEntries}. The form is minimal: the bare token when the\n * release is `regular` with no description, `token:hotfix` for a hotfix with no\n * description, and `token:type:description` whenever a description is present\n * (the type segment is required to reach the description segment). The token is\n * the resolved {@link ReleaseId.raw} \u2014 a concrete semver for versions (so a\n * resolved `\"next\"` pins its computed version) or the name for named releases.\n */\nexport const formatReleaseSpec = (entry: ReleaseEntry): string => {\n const token = entry.id.raw\n\n if (entry.description !== undefined && entry.description !== '') {\n return `${token}:${entry.type}:${entry.description}`\n }\n\n if (entry.type === 'hotfix') return `${token}:hotfix`\n\n return token\n}\n\nconst withDescription = (base: { id: ReleaseId; type: ReleaseType }, description?: string): ReleaseEntry => {\n return description !== undefined && description !== '' ? { ...base, description } : base\n}\n\nconst resolveNamedInput = (input: NamedReleaseInput): ReleaseEntry => {\n const name = input.name.trim()\n\n // validateName throws InvalidReleaseNameError with a specific message.\n validateName(name)\n\n return withDescription({ id: { kind: 'name', name, raw: name }, type: input.type }, input.description)\n}\n\n/**\n * Resolve a list of release inputs into entries carrying a concrete\n * {@link ReleaseId}. Versioned inputs use the existing spec format: the\n * `\"next\"` token advances based on the running max so successive `\"next\"`\n * tokens produce sequential versions (even across mixed types), then the\n * concrete version is wrapped in a {@link ReleaseId}. Named inputs are\n * validated via `validateName` and never auto-bump.\n */\nexport const resolveReleaseEntries = (entries: ReleaseInput[], known: SemVer[]): ReleaseEntry[] => {\n const running: SemVer[] = [...known]\n\n return entries.map((entry) => {\n if (isNamedReleaseInput(entry)) {\n return resolveNamedInput(entry)\n }\n\n const trimmed = entry.version.trim()\n\n if (trimmed === '') {\n throw new Error('Release entry has an empty version')\n }\n\n if (isNextToken(trimmed)) {\n const next = computeNextVersion(running, entry.type)\n\n running.push(parseVersion(`v${next}`))\n\n return withDescription({ id: parseReleaseRef(next), type: entry.type }, entry.description)\n }\n\n const parsed = tryParse(trimmed)\n\n if (!parsed) {\n throw new Error(`Invalid version \"${trimmed}\". Expected semver like \"1.2.5\" or the token \"next\".`)\n }\n\n const explicit = `${parsed[0]}.${parsed[1]}.${parsed[2]}`\n\n running.push(parsed)\n\n return withDescription({ id: parseReleaseRef(explicit), type: entry.type }, entry.description)\n })\n}\n\nexport const hasNextToken = (entries: ReleaseInput[]): boolean => {\n return entries.some((e) => {\n return !isNamedReleaseInput(e) && isNextToken(e.version)\n })\n}\n", "import { z } from 'zod'\nimport { question } from 'zx'\n\nimport { getReleasePRsWithInfo, updateReleasePRBody } from 'src/integrations/gh'\nimport { findVersionByName, loadJiraConfig, updateJiraVersion } from 'src/integrations/jira'\nimport type { JiraConfig, JiraVersion } from 'src/integrations/jira'\nimport { commandEcho, confirmOrExit } from 'src/lib/command-echo'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { logger } from 'src/lib/logger'\nimport { pickReleaseBranch as pickReleaseBranchPrompt } from 'src/lib/prompts/release-picker'\nimport { displayLabel, formatJiraName, parseBranchName } from 'src/lib/release-id'\nimport {\n detectReleaseType,\n formatBranchPickerItems,\n getJiraDescriptions,\n resolveReleaseBranch,\n} from 'src/lib/release-utils'\nimport type { ReleaseType } from 'src/lib/release-utils'\nimport { defineMcpTool, textContent } from 'src/types'\nimport type { RequiredConfirmedOptionArg } from 'src/types'\n\ninterface ReleaseDescEditArgs extends RequiredConfirmedOptionArg {\n version?: string\n description?: string\n}\n\nconst buildJiraVersionUrl = (jiraConfig: JiraConfig, version: JiraVersion): string => {\n return `${jiraConfig.baseUrl}/projects/${version.projectId}/versions/${version.id}/tab/release-report-all-issues`\n}\n\nconst buildPRBody = (jiraVersionUrl: string, description: string): string => {\n return description.trim() !== '' ? `${jiraVersionUrl}\\n\\n${description}` : `${jiraVersionUrl} \\n`\n}\n\nconst pickReleaseBranch = async (): Promise<{ branch: string; type: ReleaseType }> => {\n const releasePRsInfo = await getReleasePRsWithInfo()\n const branches = releasePRsInfo.map((pr) => {\n return pr.branch\n })\n const types = new Map<string, ReleaseType>(\n releasePRsInfo.map((pr) => {\n return [pr.branch, detectReleaseType(pr.title)]\n }),\n )\n const descriptions = await getJiraDescriptions()\n\n const branch = await pickReleaseBranchPrompt(formatBranchPickerItems({ branches, descriptions, types }))\n\n return { branch, type: types.get(branch) || 'regular' }\n}\n\nconst verifyReleasePRExists = async (selectedBranch: string): Promise<ReleaseType> => {\n const releasePRsInfo = await getReleasePRsWithInfo()\n const prInfo = releasePRsInfo.find((pr) => {\n return pr.branch === selectedBranch\n })\n\n if (!prInfo) {\n throw new OperationError(undefined, {\n operation: `edit description for ${selectedBranch}`,\n remediation: `confirm an open PR exists for ${selectedBranch} ('gh pr list')`,\n })\n }\n\n return detectReleaseType(prInfo.title)\n}\n\nconst promptDescription = async (current: string): Promise<string> => {\n const hint = current === '' ? '(no current description)' : `current: \"${current}\"`\n const answer = await question(` New description ${hint}\\n (press Enter to keep current): `)\n const trimmed = answer.replace(/\\n$/, '')\n\n return trimmed === '' ? current : trimmed\n}\n\n/**\n * Edit a release's description in Jira (fix version) and in the matching\n * GitHub release PR body. The PR body is rewritten canonically to\n * `<jiraVersionUrl>\\n\\n<description>` (matching `release-create`).\n */\nexport const releaseDescEdit = async (args: ReleaseDescEditArgs) => {\n const { version: versionArg, description: descriptionArg, confirmedCommand } = args\n\n const jiraConfig = await loadJiraConfig()\n\n let selectedBranch: string\n\n if (versionArg) {\n selectedBranch = resolveReleaseBranch(versionArg)\n await verifyReleasePRExists(selectedBranch)\n } else {\n commandEcho.setInteractive()\n const picked = await pickReleaseBranch()\n\n selectedBranch = picked.branch\n }\n\n // selectedBranch is always a release branch here (operator ref strictly parsed,\n // or picked from discovery-filtered choices), so parseBranchName cannot be null.\n const releaseId = parseBranchName(selectedBranch)\n\n if (!releaseId) {\n throw new OperationError(undefined, {\n operation: `edit description for ${selectedBranch}`,\n remediation: 'pass a version (e.g. \"1.2.5\") or a release name (e.g. \"checkout-redesign\")',\n })\n }\n\n const selectedVersion = displayLabel(releaseId)\n\n commandEcho.addOption('--version', selectedVersion)\n\n // Jira fix version is named by the Jira convention: `v1.2.3` | `<name>`.\n const versionName = formatJiraName(releaseId)\n const jiraVersion = await findVersionByName(versionName, jiraConfig)\n\n if (!jiraVersion) {\n throw new OperationError(undefined, {\n operation: `edit description for ${versionName}`,\n remediation: `create the Jira fix version \"${versionName}\" first or pick a different release`,\n })\n }\n\n const previousDescription = jiraVersion.description ?? ''\n\n let newDescription: string\n\n if (descriptionArg !== undefined) {\n newDescription = descriptionArg\n commandEcho.addOption('--description', newDescription)\n } else {\n commandEcho.setInteractive()\n newDescription = await promptDescription(previousDescription)\n }\n\n const buildResult = (changed: boolean) => {\n const structuredContent = {\n version: selectedVersion,\n branch: selectedBranch,\n jiraVersionUrl: buildJiraVersionUrl(jiraConfig, jiraVersion),\n previousDescription,\n newDescription,\n changed,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n }\n\n if (newDescription === previousDescription) {\n logger.info(`No change \u2014 description for ${versionName} is already: \"${previousDescription}\"`)\n commandEcho.print()\n\n return buildResult(false)\n }\n\n await confirmOrExit(\n confirmedCommand,\n `Update description for ${versionName}?\\n from: \"${previousDescription}\"\\n to: \"${newDescription}\"\\n`,\n )\n\n commandEcho.addOption('--yes', true)\n\n await updateJiraVersion({ versionId: jiraVersion.id, description: newDescription }, jiraConfig)\n\n const jiraVersionUrl = buildJiraVersionUrl(jiraConfig, jiraVersion)\n const body = buildPRBody(jiraVersionUrl, newDescription)\n\n await updateReleasePRBody({ branch: selectedBranch, body })\n\n logger.info(`\u2705 Updated description for ${versionName}`)\n logger.info(`\uD83D\uDD17 Jira Version: ${jiraVersionUrl}`)\n logger.info(`\uD83D\uDD17 PR branch: ${selectedBranch}\\n`)\n\n commandEcho.print()\n\n return buildResult(true)\n}\n\n// MCP Tool Registration\nexport const releaseDescEditMcpTool = defineMcpTool({\n name: 'release-desc-edit',\n description:\n \"Edit a release's description in Jira and in the matching GitHub release PR body. Accepts a release version or a release name: targets the Jira fix version named `v<version>` (versioned) or `<name>` (named) and the open PR on branch `release/v<version>` or `release/<name>`. The PR body is rewritten canonically to `<jiraVersionUrl>\\\\n\\\\n<description>` \u2014 any prior manual edits to the body are overwritten. Both `version` and `description` are required for MCP calls (the picker/prompt are unreachable without a TTY). Empty `description` clears the description on both sides. Confirmation is auto-skipped for MCP, so the caller is responsible for gating.\",\n inputSchema: {\n version: z\n .string()\n .describe('Accepts a release version (e.g. \"1.2.5\") OR a release name (e.g. \"checkout-redesign\").'),\n description: z.string().describe('New description. Empty string clears the description.'),\n },\n outputSchema: {\n version: z.string().describe('Release version'),\n branch: z.string().describe('Release branch name (e.g. \"release/v1.2.5\" or \"release/checkout-redesign\")'),\n jiraVersionUrl: z.string().describe('Jira fix version URL'),\n previousDescription: z.string().describe('The description before the update'),\n newDescription: z.string().describe('The description after the update'),\n changed: z.boolean().describe('Whether the description actually changed'),\n },\n handler: releaseDescEdit,\n})\n", "import { existsSync } from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { z } from 'zod'\n\nimport { getProjectRoot } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { MANIFEST_FILE, VENDOR_DIR, compareToManifest, isUnknownSchema, readManifest } from 'src/lib/vendor'\nimport { defineMcpTool, textContent } from 'src/types'\n\n/** Max drifted paths to print per category before truncating. */\nconst MAX_REPORTED = 30\n\ntype VendorCheckStatus = 'clean' | 'drift' | 'skipped' | 'missing-manifest' | 'unknown-schema'\n\ninterface VendorCheckOptions {\n /** Repo root to check. Defaults to the git toplevel. */\n cwd?: string\n}\n\nconst report = (label: string, list: string[]): void => {\n if (list.length === 0) {\n return\n }\n\n logger.info(`\\n${label} (${list.length}):`)\n\n for (const rel of list.slice(0, MAX_REPORTED)) {\n logger.info(` ${rel}`)\n }\n\n if (list.length > MAX_REPORTED) {\n logger.info(` \u2026and ${list.length - MAX_REPORTED} more`)\n }\n}\n\n/**\n * Verify that files under `vendor/` still match the checksums recorded in\n * `vendor/.sync-manifest.json`. Config-free and self-contained (no source repo,\n * no `vendor.config.ts`, no rsync) so it runs in any consumer's CI. Read-only \u2014\n * never calls `process.exit`; the CLI action maps `structuredContent.ok` to the\n * exit code, and the MCP tool reuses the same handler.\n *\n * Exit-code contract (preserved from the legacy `vendor-check.mjs`):\n * clean \u2192 ok; drift \u2192 not ok; missing `vendor/` \u2192 ok (skip);\n * missing manifest \u2192 not ok; unknown (newer) schemaVersion \u2192 not ok (fail-closed).\n *\n * @example\n * await vendorCheck() // checks the current repo's vendor/\n * await vendorCheck({ cwd: '/repo' }) // checks a specific root (tests)\n */\nexport const vendorCheck = async (options: VendorCheckOptions = {}) => {\n const root = options.cwd ?? (await getProjectRoot())\n const vendorRoot = path.join(root, VENDOR_DIR)\n\n const base = { modified: [] as string[], added: [] as string[], removed: [] as string[], fileCount: 0 }\n\n if (!existsSync(vendorRoot)) {\n logger.info(`\u2139\uFE0F No ${VENDOR_DIR}/ folder found \u2014 nothing to check.`)\n const structuredContent = { status: 'skipped' as VendorCheckStatus, ok: true, ...base }\n\n return { content: textContent(JSON.stringify(structuredContent, null, 2)), structuredContent }\n }\n\n if (!existsSync(path.join(vendorRoot, MANIFEST_FILE))) {\n logger.error(`\u274C Missing ${VENDOR_DIR}/${MANIFEST_FILE}. Re-run the vendor sync to generate it.`)\n const structuredContent = { status: 'missing-manifest' as VendorCheckStatus, ok: false, ...base }\n\n return { content: textContent(JSON.stringify(structuredContent, null, 2)), structuredContent }\n }\n\n const manifest = readManifest(vendorRoot)\n\n if (isUnknownSchema(manifest)) {\n logger.error(\n `\u274C ${VENDOR_DIR}/${MANIFEST_FILE} has a newer schemaVersion (${String(manifest.schemaVersion)}) than this CLI supports. Upgrade infra-kit.`,\n )\n const structuredContent = { status: 'unknown-schema' as VendorCheckStatus, ok: false, ...base }\n\n return { content: textContent(JSON.stringify(structuredContent, null, 2)), structuredContent }\n }\n\n const { modified, added, removed } = compareToManifest(vendorRoot, manifest)\n const drifted = modified.length > 0 || added.length > 0 || removed.length > 0\n\n if (drifted) {\n logger.error(`\u274C ${VENDOR_DIR}/ has drifted from ${VENDOR_DIR}/${MANIFEST_FILE}.`)\n logger.error(' These files are mirrored from the source repo \u2014 edit them upstream, not here.')\n report('Modified', modified)\n report('Added (not in manifest)', added)\n report('Removed (in manifest, now missing)', removed)\n } else {\n logger.info(`\u2705 ${VENDOR_DIR}/ matches manifest (${manifest.fileCount} files).`)\n }\n\n const structuredContent = {\n status: (drifted ? 'drift' : 'clean') as VendorCheckStatus,\n ok: !drifted,\n fileCount: manifest.fileCount,\n modified,\n added,\n removed,\n }\n\n return { content: textContent(JSON.stringify(structuredContent, null, 2)), structuredContent }\n}\n\nconst vendorCheckOutputSchema = {\n status: z.enum(['clean', 'drift', 'skipped', 'missing-manifest', 'unknown-schema']),\n ok: z.boolean().describe('Whether the vendor tree matches its manifest (false \u2192 CI should fail)'),\n fileCount: z.number(),\n modified: z.array(z.string()),\n added: z.array(z.string()),\n removed: z.array(z.string()),\n}\n\n// MCP Tool Registration (read-only; safe to expose).\nexport const vendorCheckMcpTool = defineMcpTool({\n name: 'vendor-check',\n description:\n 'Verify that files under vendor/ match the checksums in vendor/.sync-manifest.json. Self-contained (no source repo or config needed). Returns ok=false on drift, missing manifest, or an unknown future schemaVersion; ok=true when clean or when no vendor/ folder exists.',\n inputSchema: {},\n outputSchema: vendorCheckOutputSchema,\n handler: () => {\n return vendorCheck({ cwd: process.cwd() })\n },\n})\n", "import { createHash } from 'node:crypto'\nimport { readFileSync } from 'node:fs'\n\n/**\n * SHA-256 of a file's contents, hex-encoded. Matches the legacy scripts'\n * `createHash('sha256').update(readFileSync(path)).digest('hex')`.\n *\n * `readFileSync` follows symlinks, so a symlinked file is hashed by its target\n * content \u2014 the behavior both legacy scripts already relied on.\n *\n * @example\n * sha256('/repo/vendor/configs/eslint-config/index.js') // => 'a1b2c3...'\n */\nexport const sha256 = (filePath: string): string => {\n return createHash('sha256').update(readFileSync(filePath)).digest('hex')\n}\n", "import { readFileSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\nimport { z } from 'zod'\n\nimport { sha256 } from './hash'\nimport { walkVendorTree } from './walk'\n\n/** Filename of the integrity manifest inside a `vendor/` tree. */\nexport const MANIFEST_FILE = '.sync-manifest.json'\n\n/**\n * Manifest schema version the CLI understands. A manifest with a HIGHER version\n * than this was written by a newer CLI and is treated as drift (fail-closed) \u2014\n * see {@link isUnknownSchema}. Legacy manifests carry no `schemaVersion` field\n * and read as compatible.\n */\nexport const CURRENT_SCHEMA_VERSION = 1\n\n/**\n * `schemaVersion` is OPTIONAL with no default: legacy manifests written by\n * `writeVendorMeta` have no such field, and a required field would make\n * `vendor check` fail-closed on every existing manifest. Absent is read as\n * \"compatible\" (current). `writeManifest` always emits the current version.\n */\nconst manifestSchema = z.object({\n schemaVersion: z.number().optional(),\n source: z.string(),\n commit: z.string(),\n syncedAt: z.string(),\n fileCount: z.number(),\n files: z.record(z.string(), z.string()),\n})\n\nexport type VendorManifest = z.infer<typeof manifestSchema>\n\nexport interface ManifestDiff {\n modified: string[]\n added: string[]\n removed: string[]\n}\n\n/**\n * Whether a manifest's `schemaVersion` is newer than this CLI supports. Absent\n * (legacy) reads as compatible; only a strictly-greater version is unknown.\n */\nexport const isUnknownSchema = (manifest: VendorManifest): boolean => {\n return (manifest.schemaVersion ?? CURRENT_SCHEMA_VERSION) > CURRENT_SCHEMA_VERSION\n}\n\n/**\n * Walk the vendor tree and hash every file into a `{ relativePath: sha256 }`\n * map (sorted by walk order \u2192 stable). A file that cannot be read (e.g. a\n * dangling symlink) surfaces an explicit error rather than crashing with a raw\n * fs stack.\n */\nexport const buildFilesMap = (vendorRoot: string): Record<string, string> => {\n const files: Record<string, string> = {}\n\n for (const rel of walkVendorTree(vendorRoot)) {\n try {\n files[rel] = sha256(path.join(vendorRoot, rel))\n } catch (cause) {\n const reason = cause instanceof Error ? cause.message : String(cause)\n\n throw new Error(`Cannot hash vendor file \"${rel}\" (broken symlink or unreadable file): ${reason}`)\n }\n }\n\n return files\n}\n\n/**\n * Read and validate the manifest at `<vendorRoot>/.sync-manifest.json`. Throws a\n * descriptive error if the file is missing or malformed.\n */\nexport const readManifest = (vendorRoot: string): VendorManifest => {\n const manifestPath = path.join(vendorRoot, MANIFEST_FILE)\n\n let raw: string\n\n try {\n raw = readFileSync(manifestPath, 'utf8')\n } catch {\n throw new Error(`Missing ${MANIFEST_FILE} at ${manifestPath}`)\n }\n\n const parsed = manifestSchema.safeParse(JSON.parse(raw))\n\n if (!parsed.success) {\n throw new Error(`Invalid ${MANIFEST_FILE} at ${manifestPath}: ${z.prettifyError(parsed.error)}`)\n }\n\n return parsed.data\n}\n\n/**\n * Build and write a fresh manifest for the current content of `vendorRoot`.\n * Emits the CURRENT schema version, the supplied `source`/`commit`, an ISO\n * `syncedAt`, and the per-file checksum map. Returns the written manifest.\n *\n * The `files` map and field set (minus `schemaVersion`) match legacy\n * `writeVendorMeta`, so a regenerated manifest differs from a legacy one only by\n * the added `schemaVersion` and the `syncedAt` timestamp.\n */\nexport const writeManifest = (vendorRoot: string, meta: { source: string; commit: string }): VendorManifest => {\n const files = buildFilesMap(vendorRoot)\n\n const manifest: VendorManifest = {\n schemaVersion: CURRENT_SCHEMA_VERSION,\n source: meta.source,\n commit: meta.commit,\n syncedAt: new Date().toISOString(),\n fileCount: Object.keys(files).length,\n files,\n }\n\n writeFileSync(path.join(vendorRoot, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\\n`)\n\n return manifest\n}\n\n/**\n * Compare the live `vendorRoot` tree against a manifest's recorded checksums.\n * Returns the modified / added (present, not in manifest) / removed (in\n * manifest, now missing) relative paths.\n */\nexport const compareToManifest = (vendorRoot: string, manifest: VendorManifest): ManifestDiff => {\n const expected = manifest.files\n const actualFiles = walkVendorTree(vendorRoot)\n\n const modified: string[] = []\n const added: string[] = []\n\n for (const rel of actualFiles) {\n if (!(rel in expected)) {\n added.push(rel)\n continue\n }\n\n if (sha256(path.join(vendorRoot, rel)) !== expected[rel]) {\n modified.push(rel)\n }\n }\n\n const actualSet = new Set(actualFiles)\n const removed = Object.keys(expected).filter((rel) => {\n return !actualSet.has(rel)\n })\n\n return { modified, added, removed }\n}\n", "import { readdirSync } from 'node:fs'\nimport path from 'node:path'\n\nimport { isSkippedDir, isSkippedFile } from './skip-sets'\n\n/**\n * Convert a (possibly Windows) relative path to a POSIX-normalized form so the\n * manifest is byte-identical across operating systems.\n */\nconst toPosix = (relativePath: string): string => {\n return relativePath.split(path.sep).join('/')\n}\n\n/**\n * Walk a `vendor/` tree and return the relative paths of every non-skipped file,\n * POSIX-normalized and sorted byte-wise. The sort makes the output independent\n * of `readdirSync` order, which keeps generated manifests deterministic.\n *\n * Symlink handling matches the legacy scripts: only REAL directories\n * (`entry.isDirectory()`) are recursed; a symlink is treated as a leaf and its\n * relative path is emitted, to be hashed later by following it to target\n * content (`sha256`/`readFileSync`). This preserves legacy behavior exactly \u2014\n * the manifest checksum reflects target content, never the link itself.\n *\n * @example\n * walkVendorTree('/repo/vendor')\n * // => ['README.md', 'configs/eslint-config/index.js', ...]\n */\nexport const walkVendorTree = (vendorRoot: string): string[] => {\n const acc: string[] = []\n\n const walk = (dir: string): void => {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = path.join(dir, entry.name)\n\n if (entry.isDirectory()) {\n if (isSkippedDir(entry.name)) {\n continue\n }\n\n walk(full)\n continue\n }\n\n if (isSkippedFile(entry.name)) {\n continue\n }\n\n acc.push(toPosix(path.relative(vendorRoot, full)))\n }\n }\n\n walk(vendorRoot)\n\n return acc.sort()\n}\n", "/**\n * Skip rules for walking a `vendor/` tree to build or verify its integrity\n * manifest. This is the SINGLE source of truth consumed by both the read path\n * (`vendor check`) and the write path (`vendor sync`/`manifest`).\n *\n * The set below is a strict superset of the legacy read script's\n * `MANIFEST_SKIP_DIRS` (`scripts/vendor-check.mjs`). It additionally skips\n * `.vitest-attachments` \u2014 a git-ignored directory (see repo `.gitignore`) that\n * therefore never appears in a committed vendor tree. Widening the read path's\n * skip set this way is deliberate and behavior-preserving: no existing manifest\n * changes, and the read and write paths share this single set so they cannot\n * diverge.\n */\n/** Root of the mirrored tree inside each repo. Single source for read + write paths. */\nexport const VENDOR_DIR = 'vendor'\n\nexport const MANIFEST_SKIP_DIRS: ReadonlySet<string> = new Set([\n 'node_modules',\n 'dist',\n '.turbo',\n '.omc',\n '__screenshots__',\n '.vitest-attachments',\n '.output',\n '.source',\n '.nitro',\n '.tanstack',\n])\n\nexport const MANIFEST_SKIP_FILES: ReadonlySet<string> = new Set(['.sync-manifest.json', '.eslintcache', 'log.txt'])\n\nexport const MANIFEST_SKIP_SUFFIXES: readonly string[] = ['.tsbuildinfo']\n\n/** Whether a directory name should be skipped when walking the vendor tree. */\nexport const isSkippedDir = (name: string): boolean => {\n return MANIFEST_SKIP_DIRS.has(name)\n}\n\n/** Whether a file name should be skipped when walking the vendor tree. */\nexport const isSkippedFile = (name: string): boolean => {\n if (MANIFEST_SKIP_FILES.has(name)) {\n return true\n }\n\n return MANIFEST_SKIP_SUFFIXES.some((suffix) => {\n return name.endsWith(suffix)\n })\n}\n", "import { z } from 'zod'\n\nimport { getProjectRoot } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { loadVendorConfig } from 'src/lib/vendor/config'\nimport { loadFactoryConfig } from 'src/lib/vendor/factory-config'\nimport { runDiff, selectTargets } from 'src/lib/vendor/sync-ops'\nimport { defineMcpTool, textContent } from 'src/types'\n\n/** Max itemized changes to print per drifted path before truncating. */\nconst MAX_REPORTED = 20\n\ninterface VendorDiffOptions {\n cwd?: string\n repos?: string[]\n}\n\n/**\n * Source-aware drift check: compare each target repo's `vendored:true` subtree\n * against the source via `rsync --dry-run --delete`. Read-only (does NOT extend\n * RequiredConfirmedOptionArg).\n */\nexport const vendorDiff = async (options: VendorDiffOptions = {}) => {\n const sourceRoot = options.cwd ?? (await getProjectRoot())\n\n const config = await loadVendorConfig(sourceRoot)\n const factory = await loadFactoryConfig()\n const targets = selectTargets(factory.targets, options.repos)\n\n const entries = await runDiff(config, sourceRoot, factory.workspaceDir, targets)\n\n if (entries.length === 0) {\n logger.info('\uD83C\uDF89 No drift detected.')\n } else {\n for (const entry of entries) {\n logger.info(`\u274C ${entry.repo} \u2192 ${entry.target} (${entry.changes.length} change(s)):`)\n\n for (const change of entry.changes.slice(0, MAX_REPORTED)) {\n logger.info(` ${change}`)\n }\n\n if (entry.changes.length > MAX_REPORTED) {\n logger.info(` \u2026and ${entry.changes.length - MAX_REPORTED} more`)\n }\n }\n }\n\n const structuredContent = { ok: entries.length === 0, drifted: entries.length, entries }\n\n return { content: textContent(JSON.stringify(structuredContent, null, 2)), structuredContent }\n}\n\nconst vendorDiffOutputSchema = {\n ok: z.boolean().describe('Whether every target is in sync with the source'),\n drifted: z.number(),\n entries: z.array(\n z.object({\n repo: z.string(),\n target: z.string(),\n changes: z.array(z.string()),\n }),\n ),\n}\n\n// MCP Tool Registration (read-only; safe to expose).\nexport const vendorDiffMcpTool = defineMcpTool({\n name: 'vendor-diff',\n description:\n 'Source-aware drift check: compare each target repo vendored subtree against the source via rsync dry-run. Read-only; run from the source repo root. ok=false when any target has drifted.',\n inputSchema: {\n repos: z.array(z.string()).optional().describe('Restrict to these target repo names'),\n },\n outputSchema: vendorDiffOutputSchema,\n handler: (params) => {\n return vendorDiff({ repos: params.repos })\n },\n})\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { z } from 'zod'\n\nimport { VENDOR_CONFIG_FILE, vendorConfigSchema } from './config-schema'\nimport type { VendorConfig } from './config-schema'\n\n// Convenience re-export so importers of the loader can also grab the authoring\n// helper from one module. The node-free definitions live in `./config-schema`\n// (the public `.d.ts` emit imports from there to stay node-type-free).\nexport { defineVendorConfig } from './config-schema'\n\n/**\n * Load, resolve, and validate a source repo's `vendor.config.ts`.\n *\n * Reuses the established loader pattern from\n * `package-validator/loader/config-loader.ts`: dynamic-import the `.ts` file via\n * Node's native type stripping with an mtime cache-bust (so a long-running MCP\n * server picks up edits), resolve a Vite-style factory or object default export,\n * then validate against {@link vendorConfigSchema}. No new dependency (no\n * jiti/tsx). Throws a descriptive error when the file is absent or malformed.\n *\n * @example\n * await loadVendorConfig('/Users/me/projects/starter-workspace')\n */\nexport const loadVendorConfig = async (rootDir: string): Promise<VendorConfig> => {\n const configPath = path.join(rootDir, VENDOR_CONFIG_FILE)\n\n let stat\n\n try {\n stat = await fs.stat(configPath)\n } catch {\n throw new Error(`${VENDOR_CONFIG_FILE} not found at ${configPath}`)\n }\n\n // Cache-bust with the file mtime so repeated loads pick up edits without a\n // process restart. `.ts` configs load via Node's native type stripping.\n const moduleUrl = `${pathToFileURL(configPath).href}?mtime=${Number(stat.mtimeMs)}`\n\n const imported = (await import(moduleUrl)) as { default?: unknown }\n const rawExport = imported.default\n\n if (rawExport === undefined) {\n throw new Error(`${VENDOR_CONFIG_FILE} at ${configPath} has no default export`)\n }\n\n const resolvedExport = typeof rawExport === 'function' ? await (rawExport as () => unknown)() : rawExport\n\n const parsed = vendorConfigSchema.safeParse(resolvedExport)\n\n if (!parsed.success) {\n throw new Error(`Invalid ${VENDOR_CONFIG_FILE} at ${configPath}: ${z.prettifyError(parsed.error)}`)\n }\n\n return parsed.data\n}\n", "import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\nimport { $ } from 'zx'\n\nimport { OperationError } from 'src/lib/errors/operation-error'\n\nimport type { VendorConfig } from './config-schema'\nimport { expandTilde } from './factory-config'\nimport { writeManifest } from './manifest'\nimport { VENDOR_DIR } from './skip-sets'\n\n/**\n * Write-path operations for `vendor sync`/`manifest`/`diff`. This module imports\n * `zx` at the top level and is NEVER re-exported from the read-path barrel\n * (`./index.ts`); `vendor check` never imports it, so its runtime path stays\n * free of `zx`/rsync.\n */\n\n/** rsync excludes mirrored from the legacy sync script. */\nconst EXCLUDE_PATTERNS = [\n 'node_modules',\n 'dist',\n '*.tsbuildinfo',\n '.turbo',\n '.eslintcache',\n '.omc',\n '__screenshots__',\n '.vitest-attachments',\n '.output',\n '.source',\n '.nitro',\n '.tanstack',\n 'log.txt',\n]\n\nconst VENDOR_README = `# vendor/ \u2014 mirrored from the source repo\n\n**DO NOT EDIT files in this folder here.**\n\nEverything under \\`vendor/\\` is the single source of truth maintained in the source\nrepo and copied into this repo by \\`infra-kit vendor sync\\`. Local edits are\noverwritten on the next sync and will fail \\`infra-kit vendor check\\` in CI.\n\nTo change a vendored package, edit it in the source repo and re-run the sync.\n\nSee \\`.sync-manifest.json\\` for the source commit and per-file checksums.\n`\n\nconst excludeFlags = (): string[] => {\n return EXCLUDE_PATTERNS.map((pattern) => {\n return `--exclude=${pattern}`\n })\n}\n\n/** Resolve the source repo's current HEAD commit (or `unknown` if unavailable). */\nexport const getSourceCommit = async (sourceRoot: string): Promise<string> => {\n try {\n const result = await $({ cwd: sourceRoot })`git rev-parse HEAD`\n\n return result.stdout.trim()\n } catch {\n return 'unknown'\n }\n}\n\n/** rsync a directory source\u2192target (target is replaced to avoid nested copies). */\nexport const copyDirectory = async (source: string, target: string): Promise<boolean> => {\n if (!existsSync(source)) {\n return false\n }\n\n if (existsSync(target)) {\n await $`rm -rf ${target}`\n }\n\n const sourceSlash = `${source}/`\n const targetSlash = `${target}/`\n\n await $`rsync -a ${excludeFlags()} ${sourceSlash} ${targetSlash}`\n\n return true\n}\n\n/** Copy a single file source\u2192target, creating the target dir as needed. */\nexport const copyFile = async (source: string, target: string): Promise<boolean> => {\n if (!existsSync(source)) {\n return false\n }\n\n const targetDir = path.dirname(target)\n\n if (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true })\n }\n\n await $`cp ${source} ${target}`\n\n return true\n}\n\n/**\n * Dry-run rsync reporting whether `target` has drifted from `source`. Returns the\n * itemized change list (empty = in sync). `--delete` surfaces consumer-only files.\n */\nexport const diffDirectory = async (source: string, target: string): Promise<string[]> => {\n if (!existsSync(source)) {\n return []\n }\n\n if (!existsSync(target)) {\n return [`missing target: ${target}`]\n }\n\n const sourceSlash = `${source}/`\n const targetSlash = `${target}/`\n const result = await $`rsync -ai --dry-run --delete ${excludeFlags()} ${sourceSlash} ${targetSlash}`\n\n return result.stdout\n .split('\\n')\n .map((line) => {\n return line.trim()\n })\n .filter(Boolean)\n}\n\n/**\n * Write `vendor/README.md` + `vendor/.sync-manifest.json` for a target repo,\n * mirroring the legacy `writeVendorMeta`. The manifest `files` map matches legacy\n * output; it differs only by the added `schemaVersion` and the `syncedAt` stamp.\n */\nexport const writeVendorMeta = (targetRoot: string, meta: { source: string; commit: string }): void => {\n const vendorRoot = path.join(targetRoot, VENDOR_DIR)\n\n if (!existsSync(vendorRoot)) {\n return\n }\n\n writeFileSync(path.join(vendorRoot, 'README.md'), VENDOR_README)\n writeManifest(vendorRoot, meta)\n}\n\n/**\n * Resolve a target repo name to its absolute root under the machine-local\n * `workspaceDir` (from `~/.infra-kit/vendor.json`). Replaces the old\n * \"siblings of the source repo\" assumption \u2014 the working dir is now explicit.\n */\nexport const resolveTargetRoot = (workspaceDir: string, repo: string): string => {\n return path.join(expandTilde(workspaceDir), repo)\n}\n\n/** Narrow the configured target list to an optional `--repos` allowlist. */\nexport const selectTargets = (allTargets: string[], repos?: string[]): string[] => {\n if (!repos) {\n return allTargets\n }\n\n return allTargets.filter((repo) => {\n return repos.includes(repo)\n })\n}\n\nexport interface VendorSyncResult {\n source: string\n commit: string\n repos: { repo: string; copied: number; total: number; skipped: boolean }[]\n}\n\n/**\n * Defense-in-depth path-traversal guard: throw if `child` resolves outside\n * `root`. The schema's `safeRelPath` already rejects `..`/absolute segments at\n * load time; this re-checks the resolved absolute paths so a symlink or odd join\n * can never escape the source/target repo root.\n */\nconst assertContained = (root: string, child: string, label: string, rel: string, itemName: string): void => {\n const resolvedRoot = path.resolve(root)\n const resolved = path.resolve(child)\n\n if (resolved !== resolvedRoot && !resolved.startsWith(resolvedRoot + path.sep)) {\n throw new OperationError(undefined, {\n operation: `sync vendor item ${itemName}`,\n stderrExcerpt: `${label} escapes repo root: ${rel}`,\n })\n }\n}\n\n/** Copy every configured item into each existing target, then write its manifest. */\nexport const runSync = async (\n config: VendorConfig,\n sourceRoot: string,\n workspaceDir: string,\n targets: string[],\n meta: { source: string; commit: string },\n): Promise<VendorSyncResult> => {\n const repos: VendorSyncResult['repos'] = []\n\n for (const repo of targets) {\n const targetRoot = resolveTargetRoot(workspaceDir, repo)\n\n if (!existsSync(targetRoot)) {\n repos.push({ repo, copied: 0, total: config.copy.length, skipped: true })\n continue\n }\n\n let copied = 0\n\n for (const item of config.copy) {\n const source = path.join(sourceRoot, item.source)\n const target = path.join(targetRoot, item.target)\n\n assertContained(sourceRoot, source, 'source', item.source, item.name)\n assertContained(targetRoot, target, 'target', item.target, item.name)\n\n const ok = item.type === 'directory' ? await copyDirectory(source, target) : await copyFile(source, target)\n\n if (ok) {\n copied++\n }\n }\n\n writeVendorMeta(targetRoot, meta)\n repos.push({ repo, copied, total: config.copy.length, skipped: false })\n }\n\n return { source: meta.source, commit: meta.commit, repos }\n}\n\n/**\n * Regenerate the manifest+README in each existing target without copying. Needs\n * neither the source `config` (no `copy[]` involved) nor `sourceRoot` (it writes\n * only into targets) \u2014 just where the targets live and which ones.\n */\nexport const runManifest = async (\n workspaceDir: string,\n targets: string[],\n meta: { source: string; commit: string },\n): Promise<{ repo: string; skipped: boolean }[]> => {\n const repos: { repo: string; skipped: boolean }[] = []\n\n for (const repo of targets) {\n const targetRoot = resolveTargetRoot(workspaceDir, repo)\n const skipped = !existsSync(targetRoot)\n\n if (!skipped) {\n writeVendorMeta(targetRoot, meta)\n }\n\n repos.push({ repo, skipped })\n }\n\n return repos\n}\n\nexport interface VendorDiffEntry {\n repo: string\n target: string\n changes: string[]\n}\n\n/** Source-aware drift across every `vendored:true` item in each target. */\nexport const runDiff = async (\n config: VendorConfig,\n sourceRoot: string,\n workspaceDir: string,\n targets: string[],\n): Promise<VendorDiffEntry[]> => {\n const entries: VendorDiffEntry[] = []\n const vendoredItems = config.copy.filter((item) => {\n return item.vendored === true\n })\n\n for (const repo of targets) {\n const targetRoot = resolveTargetRoot(workspaceDir, repo)\n\n if (!existsSync(targetRoot)) {\n continue\n }\n\n for (const item of vendoredItems) {\n const source = path.join(sourceRoot, item.source)\n const target = path.join(targetRoot, item.target)\n const changes = await diffDirectory(source, target)\n\n if (changes.length > 0) {\n entries.push({ repo, target: item.target, changes })\n }\n }\n }\n\n return entries\n}\n", "import path from 'node:path'\nimport { z } from 'zod'\n\nimport { getProjectRoot } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { loadFactoryConfig } from 'src/lib/vendor/factory-config'\nimport { getSourceCommit, runManifest, selectTargets } from 'src/lib/vendor/sync-ops'\nimport { defineMcpTool, textContent } from 'src/types'\nimport type { RequiredConfirmedOptionArg } from 'src/types'\n\ninterface VendorManifestOptions extends RequiredConfirmedOptionArg {\n cwd?: string\n repos?: string[]\n}\n\n/**\n * Regenerate each target repo's `vendor/.sync-manifest.json` + README from the\n * CURRENT vendor content, without copying anything. Mutating (writes manifests).\n */\nexport const vendorManifest = async (options: VendorManifestOptions) => {\n const sourceRoot = options.cwd ?? (await getProjectRoot())\n\n const factory = await loadFactoryConfig()\n const targets = selectTargets(factory.targets, options.repos)\n\n const commit = await getSourceCommit(sourceRoot)\n const source = path.basename(sourceRoot)\n\n const repos = await runManifest(factory.workspaceDir, targets, { source, commit })\n\n for (const repo of repos) {\n logger.info(\n repo.skipped ? `\u26A0\uFE0F ${repo.repo}: target does not exist \u2014 skipped` : `\uD83D\uDCDD ${repo.repo}: manifest written`,\n )\n }\n\n const structuredContent = { source, commit, repos }\n\n return { content: textContent(JSON.stringify(structuredContent, null, 2)), structuredContent }\n}\n\nconst vendorManifestOutputSchema = {\n source: z.string(),\n commit: z.string(),\n repos: z.array(z.object({ repo: z.string(), skipped: z.boolean() })),\n}\n\n// MCP Tool Registration (mutating).\nexport const vendorManifestMcpTool = defineMcpTool({\n name: 'vendor-manifest',\n description:\n 'Regenerate each target repo vendor/.sync-manifest.json + README from current content without copying. Mutating; run from the source repo root.',\n inputSchema: {\n repos: z.array(z.string()).optional().describe('Restrict to these target repo names'),\n },\n outputSchema: vendorManifestOutputSchema,\n handler: (params) => {\n return vendorManifest({ repos: params.repos, confirmedCommand: params.confirmedCommand })\n },\n})\n", "import confirm from '@inquirer/confirm'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { z } from 'zod'\n\nimport { getProjectRoot } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { loadVendorConfig } from 'src/lib/vendor/config'\nimport { loadFactoryConfig } from 'src/lib/vendor/factory-config'\nimport { getSourceCommit, runSync, selectTargets } from 'src/lib/vendor/sync-ops'\nimport { defineMcpTool, textContent } from 'src/types'\nimport type { RequiredConfirmedOptionArg } from 'src/types'\n\ninterface VendorSyncOptions extends RequiredConfirmedOptionArg {\n /** Source repo root. Defaults to the git toplevel. */\n cwd?: string\n /** Restrict the sync to these target repo names (defaults to all configured targets). */\n repos?: string[]\n}\n\n/**\n * Copy every item declared in the source repo's `vendor.config.ts` into each\n * target repo, then (re)write each target's `vendor/README.md` +\n * `.sync-manifest.json`. Mutating: extends {@link RequiredConfirmedOptionArg} and\n * prompts for confirmation unless `confirmedCommand` is set (MCP injects it).\n *\n * The config loader and `zx`/rsync live in `config`/`sync-ops`. `vendor check`\n * stays free of them because it never imports the write commands \u2014 not because\n * of how they are imported here.\n */\nexport const vendorSync = async (options: VendorSyncOptions) => {\n const sourceRoot = options.cwd ?? (await getProjectRoot())\n\n const config = await loadVendorConfig(sourceRoot)\n const factory = await loadFactoryConfig()\n const targets = selectTargets(factory.targets, options.repos)\n\n if (!options.confirmedCommand) {\n const proceed = await confirm(\n { message: `Sync vendor files from ${path.basename(sourceRoot)} into ${targets.length} repo(s)?` },\n { output: process.stderr },\n )\n\n if (!proceed) {\n process.exit(0)\n }\n }\n\n const commit = await getSourceCommit(sourceRoot)\n const source = path.basename(sourceRoot)\n\n const structuredContent = await runSync(config, sourceRoot, factory.workspaceDir, targets, { source, commit })\n\n for (const repo of structuredContent.repos) {\n if (repo.skipped) {\n logger.info(`\u26A0\uFE0F ${repo.repo}: target does not exist \u2014 skipped`)\n } else {\n logger.info(`\u2705 ${repo.repo}: copied ${repo.copied}/${repo.total} items + manifest`)\n }\n }\n\n return { content: textContent(JSON.stringify(structuredContent, null, 2)), structuredContent }\n}\n\nconst vendorSyncOutputSchema = {\n source: z.string(),\n commit: z.string(),\n repos: z.array(\n z.object({\n repo: z.string(),\n copied: z.number(),\n total: z.number(),\n skipped: z.boolean(),\n }),\n ),\n}\n\n// MCP Tool Registration (mutating \u2014 createToolHandler injects confirmedCommand:true).\nexport const vendorSyncMcpTool = defineMcpTool({\n name: 'vendor-sync',\n description:\n 'Copy vendored files from the source repo (vendor.config.ts) into each target repo and regenerate their vendor/.sync-manifest.json + README. Mutating; run from the source repo root.',\n inputSchema: {\n repos: z.array(z.string()).optional().describe('Restrict to these target repo names'),\n },\n outputSchema: vendorSyncOutputSchema,\n handler: (params) => {\n return vendorSync({ repos: params.repos, confirmedCommand: params.confirmedCommand })\n },\n})\n", "import { z } from 'zod'\n\nimport { logger } from 'src/lib/logger'\nimport { defineMcpTool, textContent } from 'src/types'\n\nimport packageJson from '../../../package.json' with { type: 'json' }\n\n/**\n * Print the infra-kit CLI version\n */\nexport const version = async () => {\n const cliVersion = packageJson.version\n\n logger.info(cliVersion)\n\n const structuredContent = { version: cliVersion }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\n// MCP Tool Registration\nexport const versionMcpTool = defineMcpTool({\n name: 'version',\n description: 'Print the installed infra-kit CLI version',\n inputSchema: {},\n outputSchema: {\n version: z.string().describe('Installed infra-kit CLI version (from package.json)'),\n },\n handler: version,\n})\n", "/* eslint-disable sonarjs/cognitive-complexity */\nimport confirm from '@inquirer/confirm'\nimport { z } from 'zod'\nimport { $ } from 'zx'\n\nimport {\n buildCmuxWorkspaceTitle,\n canonicalizeCmuxTitle,\n listCmuxWorkspaceTitles,\n openCmuxWorkspaceWithLayout,\n} from 'src/integrations/cmux'\nimport { getReleasePRsWithInfo } from 'src/integrations/gh'\nimport { IDE_MODES, addIdeWorktreeFolders } from 'src/integrations/ide'\nimport type { IdeMode } from 'src/integrations/ide'\nimport { commandEcho, confirmOrExit } from 'src/lib/command-echo'\nimport { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'\nimport { isPromptCancellation } from 'src/lib/errors/is-prompt-cancellation'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { assertManagementContext } from 'src/lib/git-guard'\nimport { getCurrentWorktrees, getProjectRoot, getRepoName } from 'src/lib/git-utils'\nimport { getInfraKitConfig, resolveConfiguredIdes } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { pickReleaseBranches } from 'src/lib/prompts/release-picker'\nimport { formatBranchName, isReleaseBranch, parseReleaseRef } from 'src/lib/release-id'\nimport {\n detectReleaseType,\n formatBranchPickerItems,\n getJiraDescriptions,\n releaseBranchLabels,\n} from 'src/lib/release-utils'\nimport type { ReleaseType } from 'src/lib/release-utils'\nimport { defineMcpTool, textContent } from 'src/types'\nimport type { RequiredConfirmedOptionArg } from 'src/types'\n\n// Constants\nconst FEATURE_DIR = 'feature'\nconst RELEASE_DIR = 'release'\n\ninterface WorktreeManagementArgs extends RequiredConfirmedOptionArg {\n all?: boolean\n versions?: string\n ide?: IdeMode\n /** @deprecated Alias for `ide`, kept for back-compat. Ignored when `ide` is set. */\n cursor?: IdeMode\n githubDesktop?: boolean\n cmux?: boolean\n}\n\n/**\n * Manage git worktrees for release branches\n * Creates worktrees for active release branches and removes unused ones\n */\nexport const worktreesAdd = async (options: WorktreeManagementArgs) => {\n const { confirmedCommand, all, versions, githubDesktop, cmux } = options\n // `cursor` is the deprecated alias for `ide`; `ide` wins when both are present.\n const ide = options.ide ?? options.cursor\n\n await assertManagementContext({ operation: 'create worktrees', requiredBranch: 'dev' })\n\n try {\n const currentWorktrees = await getCurrentWorktrees('release')\n const projectRoot = await getProjectRoot()\n\n const worktreeDir = `${projectRoot}${WORKTREES_DIR_SUFFIX}`\n\n await ensureWorktreeDirectory(`${worktreeDir}/${RELEASE_DIR}`)\n await ensureWorktreeDirectory(`${worktreeDir}/${FEATURE_DIR}`)\n\n let selectedReleaseBranches: string[] = []\n\n if (versions) {\n selectedReleaseBranches = versions.split(',').map((v) => {\n return formatBranchName(parseReleaseRef(v.trim()))\n })\n } else {\n const releasePRsInfo = await getReleasePRsWithInfo()\n\n const releasePRsList = releasePRsInfo.map((pr) => {\n return pr.branch\n })\n\n if (releasePRsList.length === 0) {\n logger.info('\u2139\uFE0F No open release branches found')\n\n commandEcho.print()\n\n return {\n content: textContent(JSON.stringify({ createdWorktrees: [], count: 0 }, null, 2)),\n structuredContent: { createdWorktrees: [], count: 0 },\n }\n }\n\n if (all) {\n selectedReleaseBranches = releasePRsList\n } else {\n commandEcho.setInteractive()\n\n const releaseTypes = new Map<string, ReleaseType>(\n releasePRsInfo.map((pr) => {\n return [pr.branch, detectReleaseType(pr.title)]\n }),\n )\n\n const descriptions = await getJiraDescriptions()\n\n selectedReleaseBranches = await pickReleaseBranches(\n formatBranchPickerItems({ branches: releasePRsList, descriptions, types: releaseTypes }),\n { required: true },\n )\n }\n }\n\n // Track --all flag if all branches were selected (either via flag or interactively)\n if (all) {\n commandEcho.addOption('--all', true)\n } else {\n commandEcho.addOption('--versions', releaseBranchLabels(selectedReleaseBranches))\n }\n\n // Ask for confirmation\n await confirmOrExit(confirmedCommand, 'Are you sure you want to proceed with these worktree changes?')\n\n // Track --yes flag if confirmation was interactive (user confirmed)\n if (!confirmedCommand) {\n commandEcho.addOption('--yes', true)\n }\n\n const config = await getInfraKitConfig()\n\n // One attach style: every configured editor gets the worktrees added to its\n // workspace. Per-run skip is `--ide none` / `--no-ide`; no editor configured\n // means there's nothing to open.\n const ideMode: IdeMode = ide ?? (resolveConfiguredIdes(config).length > 0 ? 'workspace' : 'none')\n\n commandEcho.addOption('--ide', ideMode)\n\n const openInGithubDesktop =\n githubDesktop ??\n config.worktrees?.openInGithubDesktop ??\n (await confirm({ message: 'Open created worktrees in GitHub Desktop?' }))\n\n if (typeof githubDesktop === 'undefined' && config.worktrees?.openInGithubDesktop === undefined) {\n commandEcho.setInteractive()\n }\n\n if (openInGithubDesktop) {\n commandEcho.addOption('--github-desktop', true)\n } else {\n commandEcho.addOption('--no-github-desktop', true)\n }\n\n const openInCmux =\n cmux ?? config.worktrees?.openInCmux ?? (await confirm({ message: 'Open created worktrees in cmux?' }))\n\n if (typeof cmux === 'undefined' && config.worktrees?.openInCmux === undefined) {\n commandEcho.setInteractive()\n }\n\n if (openInCmux) {\n commandEcho.addOption('--cmux', true)\n } else {\n commandEcho.addOption('--no-cmux', true)\n }\n\n const { branchesToCreate } = categorizeWorktrees({\n selectedReleaseBranches,\n currentWorktrees,\n })\n\n const createdWorktrees = await createWorktrees(branchesToCreate, worktreeDir)\n\n logResults(createdWorktrees)\n\n if (ideMode === 'workspace') {\n await addIdeWorktreeFolders({ projectRoot, worktreeDir, branches: createdWorktrees })\n }\n\n if (openInGithubDesktop) {\n for (const branch of createdWorktrees) {\n await $`github ${worktreeDir}/${branch}`\n await $`sleep 5`\n }\n }\n\n if (openInCmux) {\n const repoName = await getRepoName()\n const existingTitles = await listCmuxWorkspaceTitles()\n\n for (const branch of createdWorktrees) {\n const title = buildCmuxWorkspaceTitle({ repoName, branch })\n\n // Skip branches whose cmux workspace is already open (canonical match),\n // so re-running worktrees-add never duplicates an existing workspace.\n if (existingTitles.has(canonicalizeCmuxTitle(title))) {\n continue\n }\n\n await openCmuxWorkspaceWithLayout({\n cwd: `${worktreeDir}/${branch}`,\n title,\n })\n }\n }\n\n commandEcho.print()\n\n const structuredContent = {\n createdWorktrees,\n count: createdWorktrees.length,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n } catch (error) {\n // A cancelled prompt (Ctrl-C / Esc) is a user back-out, not a failure: let it\n // reach the top-level boundary untouched so it exits cleanly, instead of being\n // logged as an error with a misleading \"branches already exist\" remediation.\n if (isPromptCancellation(error)) throw error\n\n logger.error({ error }, '\u274C Error managing worktrees')\n throw new OperationError(error, {\n operation: 'create worktrees',\n remediation: \"verify branches don't already exist as worktrees: 'git worktree list'\",\n })\n }\n}\n\n/**\n * Ensure the worktree directory exists\n */\nconst ensureWorktreeDirectory = async (worktreeDir: string): Promise<void> => {\n await $`mkdir -p ${worktreeDir}`\n}\n\ninterface CategorizeWorktreesArgs {\n selectedReleaseBranches: string[]\n currentWorktrees: string[]\n}\n\n/**\n * Categorize release worktrees into those that need to be created or removed\n */\nconst categorizeWorktrees = (args: CategorizeWorktreesArgs): { branchesToCreate: string[] } => {\n const { selectedReleaseBranches, currentWorktrees } = args\n\n const currentBranchNames = currentWorktrees.filter((branch) => {\n return isReleaseBranch(branch)\n })\n\n const branchesToCreate = selectedReleaseBranches.filter((branch) => {\n return !currentBranchNames.includes(branch)\n })\n\n return { branchesToCreate }\n}\n\n/**\n * Create worktrees for the specified branches\n */\nconst createWorktrees = async (branches: string[], worktreeDir: string): Promise<string[]> => {\n const results = await Promise.allSettled(\n branches.map(async (branch) => {\n const worktreePath = `${worktreeDir}/${branch}`\n\n await $`git worktree add ${worktreePath} ${branch}`\n await $({ cwd: worktreePath })`pnpm install`\n\n return branch\n }),\n )\n\n const created: string[] = []\n\n for (const [index, result] of results.entries()) {\n if (result.status === 'fulfilled') {\n created.push(result.value)\n } else {\n const branch = branches[index]\n const err = new OperationError(result.reason, {\n operation: `git worktree add for ${branch}`,\n remediation: 'check the branch name and that the parent dir is writable',\n })\n\n logger.error({ error: result.reason, msg: err.message })\n }\n }\n\n return created\n}\n\n/**\n * Log the results of worktree management\n */\nconst logResults = (created: string[]): void => {\n if (created.length > 0) {\n logger.info('\u2705 Created git worktrees:')\n for (const branch of created) {\n logger.info(branch)\n }\n logger.info('')\n } else {\n logger.info('\u2139\uFE0F No new git worktrees to create')\n }\n}\n\n// MCP Tool Registration\nexport const worktreesAddMcpTool = defineMcpTool({\n name: 'worktrees-add',\n description:\n 'Create local git worktrees for release branches under the worktrees directory and run \"pnpm install\" in each. Mutates the local filesystem. When invoked via MCP, pass either \"versions\" (comma-separated) or all=true \u2014 the branch picker and \"open in Cursor / GitHub Desktop / cmux\" follow-up prompts are unreachable without a TTY, and the CLI confirmation is auto-skipped for MCP calls.',\n inputSchema: {\n all: z\n .boolean()\n .optional()\n .describe(\n 'Add worktrees for every open release branch. Either \"all\" or \"versions\" must be provided for MCP calls (the interactive picker is unavailable without a TTY). Ignored if \"versions\" is provided.',\n ),\n versions: z\n .string()\n .optional()\n .describe(\n 'Comma-separated release versions or names to target (e.g. \"1.2.5, 1.2.6\" or \"checkout-redesign, 1.2.5\"). Either \"versions\" or all=true must be provided for MCP calls. Overrides \"all\" when set.',\n ),\n ide: z\n .enum(IDE_MODES)\n .optional()\n .describe(\n 'Editor open mode for created worktrees, applied to all configured editors (Cursor and/or Zed, per the \"ide\" config). \"workspace\" (the only attach style) adds each worktree to every configured editor workspace and opens it. \"none\" skips the editor. Resolution order: this flag \u2192 \"workspace\" when at least one \"ide\" is configured \u2192 \"none\" otherwise.',\n ),\n cursor: z\n .enum(IDE_MODES)\n .optional()\n .describe('Deprecated alias for \"ide\". Prefer \"ide\". Ignored when \"ide\" is provided.'),\n githubDesktop: z\n .boolean()\n .optional()\n .describe(\n 'Open each created worktree in GitHub Desktop. Resolution order: this flag \u2192 \"worktrees.openInGithubDesktop\" from infra-kit config \u2192 interactive prompt (CLI) / false (MCP, no TTY).',\n ),\n cmux: z\n .boolean()\n .optional()\n .describe(\n 'Open each created worktree in a new cmux workspace, all rooted at the worktree directory. Pane layout follows \"worktrees.cmux.layout\" (default \"two-columns\": left | right; or \"three-pane\": left split top/bottom + full-height right). Resolution order: this flag \u2192 \"worktrees.openInCmux\" from infra-kit config \u2192 interactive prompt (CLI) / false (MCP, no TTY).',\n ),\n },\n outputSchema: {\n createdWorktrees: z.array(z.string()).describe('List of created git worktree branches'),\n count: z.number().describe('Number of git worktrees created'),\n },\n handler: worktreesAdd,\n})\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\n\ninterface AddFoldersToCursorWorkspaceArgs {\n workspacePath: string\n folderPaths: string[]\n}\n\ninterface AddFoldersToCursorWorkspaceResult {\n added: string[]\n skipped: string[]\n}\n\ninterface WorkspaceFolderEntry {\n path: string\n name?: string\n}\n\ninterface WorkspaceFile {\n folders?: WorkspaceFolderEntry[]\n [key: string]: unknown\n}\n\n/**\n * Adds folders to a Cursor (`.code-workspace`) file's `folders` array, skipping\n * entries that already point to the same absolute path. Folder paths are written\n * as relative to the workspace file's directory to match Cursor's default style.\n */\nexport const addFoldersToCursorWorkspace = async (\n args: AddFoldersToCursorWorkspaceArgs,\n): Promise<AddFoldersToCursorWorkspaceResult> => {\n const { workspacePath, folderPaths } = args\n\n const workspaceDir = path.dirname(workspacePath)\n\n let raw: string\n\n try {\n raw = await fs.readFile(workspacePath, 'utf-8')\n } catch (error) {\n throw new Error(`Cursor workspace file not found at ${workspacePath}: ${(error as Error).message}`)\n }\n\n let parsed: WorkspaceFile\n\n try {\n parsed = JSON.parse(raw) as WorkspaceFile\n } catch (error) {\n throw new Error(\n `Failed to parse ${workspacePath} as JSON. Comments (JSONC) are not supported. ${(error as Error).message}`,\n )\n }\n\n const existingFolders = parsed.folders ?? []\n const existingAbsolutePaths = new Set(\n existingFolders.map((entry) => {\n return path.resolve(workspaceDir, entry.path)\n }),\n )\n\n const added: string[] = []\n const skipped: string[] = []\n\n for (const folderPath of folderPaths) {\n const absolutePath = path.resolve(folderPath)\n\n if (existingAbsolutePaths.has(absolutePath)) {\n skipped.push(folderPath)\n continue\n }\n\n const relativePath = path.relative(workspaceDir, absolutePath)\n\n existingFolders.push({ path: relativePath })\n existingAbsolutePaths.add(absolutePath)\n added.push(folderPath)\n }\n\n parsed.folders = existingFolders\n\n await fs.writeFile(workspacePath, `${JSON.stringify(parsed, null, 2)}\\n`, 'utf-8')\n\n return { added, skipped }\n}\n", "import { $ } from 'zx'\n\n/**\n * Spawns Cursor against a `.code-workspace` file (or any path). Centralised here\n * \u2014 rather than inlined at each call site \u2014 so the editor spawn lives inside the\n * cursor provider module and is covered by a single `vi.mock('src/integrations/cursor')`.\n * Keeping it out of the IDE-orchestration layer is what stops tests that mock the\n * provider (but not `zx`) from shelling out to a real editor.\n */\nexport const launchCursor = async (workspacePath: string): Promise<void> => {\n await $`cursor ${workspacePath}`\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport { addFoldersToCursorWorkspace } from './add-folders-to-workspace'\nimport { removeFoldersFromCursorWorkspace } from './remove-folders-from-workspace'\n\ninterface ReconcileCursorWorkspaceFoldersArgs {\n workspacePath: string\n worktreeDir: string\n currentBranches: string[]\n}\n\ninterface ReconcileCursorWorkspaceFoldersResult {\n added: string[]\n removed: string[]\n}\n\ninterface WorkspaceFolderEntry {\n path: string\n name?: string\n}\n\ninterface WorkspaceFile {\n folders?: WorkspaceFolderEntry[]\n [key: string]: unknown\n}\n\n/**\n * Reconciles the configured Cursor workspace's `folders` array against the\n * actual set of release worktrees on disk:\n * - Adds any worktree folders that aren't already listed.\n * - Removes entries whose absolute path lives under `${worktreeDir}/release/`\n * but no longer corresponds to a current branch (drift from manual\n * `git worktree remove`, deleted branches, etc.).\n *\n * Non-worktree folder entries are left untouched.\n */\nexport const reconcileCursorWorkspaceFolders = async (\n args: ReconcileCursorWorkspaceFoldersArgs,\n): Promise<ReconcileCursorWorkspaceFoldersResult> => {\n const { workspacePath, worktreeDir, currentBranches } = args\n\n const workspaceDir = path.dirname(workspacePath)\n const releaseRoot = path.resolve(`${worktreeDir}/release`)\n\n const raw = await fs.readFile(workspacePath, 'utf-8')\n const parsed = JSON.parse(raw) as WorkspaceFile\n\n const existingFolders = parsed.folders ?? []\n\n const desiredAbsolutePaths = new Set(\n currentBranches.map((branch) => {\n return path.resolve(`${worktreeDir}/${branch}`)\n }),\n )\n\n const danglingFolderPaths: string[] = []\n\n for (const entry of existingFolders) {\n const entryAbsolutePath = path.resolve(workspaceDir, entry.path)\n\n const isReleaseShaped = entryAbsolutePath === releaseRoot || entryAbsolutePath.startsWith(`${releaseRoot}/`)\n\n if (isReleaseShaped && !desiredAbsolutePaths.has(entryAbsolutePath)) {\n danglingFolderPaths.push(entryAbsolutePath)\n }\n }\n\n let removed: string[] = []\n\n if (danglingFolderPaths.length > 0) {\n const result = await removeFoldersFromCursorWorkspace({\n workspacePath,\n folderPaths: danglingFolderPaths,\n })\n\n removed = result.removed\n }\n\n const desiredFolderPaths = currentBranches.map((branch) => {\n return `${worktreeDir}/${branch}`\n })\n\n const { added } =\n desiredFolderPaths.length > 0\n ? await addFoldersToCursorWorkspace({ workspacePath, folderPaths: desiredFolderPaths })\n : { added: [] as string[] }\n\n return { added, removed }\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\n\ninterface RemoveFoldersFromCursorWorkspaceArgs {\n workspacePath: string\n folderPaths: string[]\n}\n\ninterface RemoveFoldersFromCursorWorkspaceResult {\n removed: string[]\n notFound: string[]\n}\n\ninterface WorkspaceFolderEntry {\n path: string\n name?: string\n}\n\ninterface WorkspaceFile {\n folders?: WorkspaceFolderEntry[]\n [key: string]: unknown\n}\n\n/**\n * Removes folders from a Cursor (`.code-workspace`) file's `folders` array. Entries\n * are matched by resolved absolute path, so relative and absolute entries pointing\n * at the same folder are both removed.\n */\nexport const removeFoldersFromCursorWorkspace = async (\n args: RemoveFoldersFromCursorWorkspaceArgs,\n): Promise<RemoveFoldersFromCursorWorkspaceResult> => {\n const { workspacePath, folderPaths } = args\n\n const workspaceDir = path.dirname(workspacePath)\n\n let raw: string\n\n try {\n raw = await fs.readFile(workspacePath, 'utf-8')\n } catch (error) {\n throw new Error(`Cursor workspace file not found at ${workspacePath}: ${(error as Error).message}`)\n }\n\n let parsed: WorkspaceFile\n\n try {\n parsed = JSON.parse(raw) as WorkspaceFile\n } catch (error) {\n throw new Error(\n `Failed to parse ${workspacePath} as JSON. Comments (JSONC) are not supported. ${(error as Error).message}`,\n )\n }\n\n const existingFolders = parsed.folders ?? []\n const targetAbsolutePaths = new Set(\n folderPaths.map((folderPath) => {\n return path.resolve(folderPath)\n }),\n )\n\n const removedAbsolutePaths = new Set<string>()\n\n const filteredFolders = existingFolders.filter((entry) => {\n const entryAbsolutePath = path.resolve(workspaceDir, entry.path)\n\n if (targetAbsolutePaths.has(entryAbsolutePath)) {\n removedAbsolutePaths.add(entryAbsolutePath)\n\n return false\n }\n\n return true\n })\n\n parsed.folders = filteredFolders\n\n await fs.writeFile(workspacePath, `${JSON.stringify(parsed, null, 2)}\\n`, 'utf-8')\n\n const removed: string[] = []\n const notFound: string[] = []\n\n for (const folderPath of folderPaths) {\n const absolutePath = path.resolve(folderPath)\n\n if (removedAbsolutePaths.has(absolutePath)) {\n removed.push(folderPath)\n } else {\n notFound.push(folderPath)\n }\n }\n\n return { removed, notFound }\n}\n", "import path from 'node:path'\n\n/**\n * Resolves the configured Cursor workspace path against the project root.\n * Absolute paths are returned unchanged.\n */\nexport const resolveCursorWorkspacePath = (configValue: string, projectRoot: string): string => {\n if (path.isAbsolute(configValue)) {\n return configValue\n }\n\n return path.resolve(projectRoot, configValue)\n}\n", "import { logger } from 'src/lib/logger'\n\nimport { launchCursor } from './launch-cursor'\nimport { reconcileCursorWorkspaceFolders } from './reconcile-workspace-folders'\nimport { resolveCursorWorkspacePath } from './resolve-workspace-path'\n\ninterface OpenCursorWorkspaceArgs {\n projectRoot: string\n worktreeDir: string\n currentBranches: string[]\n /**\n * The Cursor entry's config, selected by the facade from the (possibly\n * multi-IDE) infra-kit config. Passed in rather than re-read here so this\n * opener acts on the exact entry the caller chose \u2014 essential once `ide` can\n * be an array of providers.\n */\n cursorConfig: { workspaceConfigPath: string }\n}\n\ninterface OpenCursorWorkspaceOutcome {\n ran: boolean\n added: number\n removed: number\n}\n\n/**\n * Reconciles the configured Cursor `.code-workspace` `folders` array against the\n * set of release worktrees on disk, then launches Cursor against it (the folder\n * reconcile still runs when there are no worktrees, but the relaunch is skipped\n * so `worktrees-reload` never pops an empty Cursor window). Used by\n * `worktrees-reload`.\n *\n * No-ops (returns `ran: false`) when the Cursor entry has no `workspaceConfigPath`.\n * All failures are swallowed into a warning \u2014 opening Cursor is best-effort.\n */\nexport const openCursorWorkspace = async (args: OpenCursorWorkspaceArgs): Promise<OpenCursorWorkspaceOutcome> => {\n const { projectRoot, worktreeDir, currentBranches, cursorConfig } = args\n\n if (!cursorConfig.workspaceConfigPath) {\n logger.warn('\u26A0\uFE0F Skipping Cursor: workspaceConfigPath is not set.')\n\n return { ran: false, added: 0, removed: 0 }\n }\n\n const workspacePath = resolveCursorWorkspacePath(cursorConfig.workspaceConfigPath, projectRoot)\n\n try {\n const { added, removed } = await reconcileCursorWorkspaceFolders({\n workspacePath,\n worktreeDir,\n currentBranches,\n })\n\n if (currentBranches.length > 0) {\n await launchCursor(workspacePath)\n }\n\n return { ran: true, added: added.length, removed: removed.length }\n } catch (error) {\n logger.warn({ error }, `\u26A0\uFE0F Failed to reconcile/open Cursor workspace at ${workspacePath}`)\n\n return { ran: false, added: 0, removed: 0 }\n }\n}\n", "import { $ } from 'zx'\n\ninterface AddFoldersToZedWorkspaceArgs {\n folderPaths: string[]\n}\n\ninterface AddFoldersToZedWorkspaceResult {\n added: string[]\n}\n\n/**\n * Adds folders to Zed's current workspace via `zed --add <path>` per folder.\n * This is the only legitimate use of `--add` (it targets the most-recent\n * workspace, so it is reserved for the incremental add-only path right after\n * `worktrees-add` creates new worktrees \u2014 never for the full cold-start open).\n *\n * Zed has no folder-remove CLI and no skip-if-present concept, so every folder\n * is reported as added (no `skipped` set, unlike the Cursor `.code-workspace`).\n */\nexport const addFoldersToZedWorkspace = async (\n args: AddFoldersToZedWorkspaceArgs,\n): Promise<AddFoldersToZedWorkspaceResult> => {\n const { folderPaths } = args\n\n for (const folderPath of folderPaths) {\n await $`zed --add ${folderPath}`\n }\n\n return { added: folderPaths }\n}\n", "import { $ } from 'zx'\n\nimport { logger } from 'src/lib/logger'\n\ninterface OpenZedWorkspaceArgs {\n projectRoot: string\n worktreeDir: string\n currentBranches: string[]\n}\n\ninterface OpenZedWorkspaceOutcome {\n ran: boolean\n added: number\n removed: number\n}\n\n/**\n * Opens a single Zed workspace containing the project root plus every release\n * worktree, via one `zed <root> <wt...>` invocation (Zed realizes a multi-folder\n * workspace from multiple path arguments). Used by `worktrees-reload`; skips the\n * launch when there are no worktrees so it never pops a bare Zed window.\n *\n * Zed has no portable workspace file and no folder-remove CLI, so there is\n * nothing to reconcile: `added` is the number of worktrees opened and `removed`\n * is always 0. All failures are swallowed into a warning \u2014 opening Zed is\n * best-effort, mirroring the Cursor integration.\n */\nexport const openZedWorkspace = async (args: OpenZedWorkspaceArgs): Promise<OpenZedWorkspaceOutcome> => {\n const { projectRoot, worktreeDir, currentBranches } = args\n\n if (currentBranches.length === 0) {\n return { ran: false, added: 0, removed: 0 }\n }\n\n const paths = [\n projectRoot,\n ...currentBranches.map((branch) => {\n return `${worktreeDir}/${branch}`\n }),\n ]\n\n try {\n await $`zed ${paths}`\n\n return { ran: true, added: currentBranches.length, removed: 0 }\n } catch (error) {\n logger.warn({ error }, '\u26A0\uFE0F Failed to open Zed workspace')\n\n return { ran: false, added: 0, removed: 0 }\n }\n}\n", "import { $ } from 'zx'\n\nimport { logger } from 'src/lib/logger'\n\ninterface ReuseZedWorkspaceArgs {\n projectRoot: string\n worktreeDir: string\n remainingBranches: string[]\n}\n\ninterface ReuseZedWorkspaceOutcome {\n ran: boolean\n}\n\n/**\n * Reflects a worktree removal in the open Zed window by running\n * `zed --reuse <root> <remaining release worktrees...>`.\n *\n * IMPORTANT \u2014 this is destructive: `--reuse` REPLACES the focused window's\n * ENTIRE folder set with exactly these paths. The remaining set is built from\n * release worktrees only (`getCurrentWorktrees('release')`), so any other folder\n * open in that window \u2014 feature worktrees, scratch dirs, user-added dirs \u2014 is\n * DROPPED and is NOT recoverable by infra-kit (Zed offers no undo and we keep no\n * record of the prior set). The caller MUST therefore only invoke this on an\n * interactive path where a human is present and can re-add folders via\n * `zed --add`. We emit a disclosure log line saying as much.\n *\n * Distinct from `openZedWorkspace`, which runs `zed <paths>` WITHOUT `--reuse`\n * to realize a multi-folder workspace; that does not replace a focused window.\n *\n * Best-effort: any launch failure is swallowed into a warning, mirroring\n * `openZedWorkspace`. Root is always included so the path list is never empty.\n */\nexport const reuseZedWorkspace = async (args: ReuseZedWorkspaceArgs): Promise<ReuseZedWorkspaceOutcome> => {\n const { projectRoot, worktreeDir, remainingBranches } = args\n\n const paths = [\n projectRoot,\n ...remainingBranches.map((branch) => {\n return `${worktreeDir}/${branch}`\n }),\n ]\n\n try {\n await $`zed --reuse ${paths}`\n\n logger.info(\n `\u21BB Refreshed Zed workspace to root + ${remainingBranches.length} release worktree(s) (other folders in the focused window are not preserved)`,\n )\n\n return { ran: true }\n } catch (error) {\n logger.warn({ error }, '\u26A0\uFE0F Failed to refresh Zed workspace')\n\n return { ran: false }\n }\n}\n", "/**\n * Exhaustiveness guard for discriminated unions. Place in the `default` branch of\n * a `switch` over a union: once every case is handled the argument narrows to\n * `never`, so adding a new variant later turns this into a compile error.\n *\n * @example\n * switch (provider) {\n * case 'cursor': return openCursor()\n * case 'zed': return openZed()\n * default: return assertNever(provider) // compile error if a provider is unhandled\n * }\n */\nexport const assertNever = (value: never): never => {\n throw new Error(`Unexpected value: ${JSON.stringify(value)}`)\n}\n", "import { addFoldersToCursorWorkspace, launchCursor, resolveCursorWorkspacePath } from 'src/integrations/cursor'\nimport { addFoldersToZedWorkspace } from 'src/integrations/zed'\nimport { assertNever } from 'src/lib/assert-never'\nimport { getInfraKitConfig, resolveConfiguredIdes } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\n\nimport type { AddIdeWorktreeFoldersOutcome } from './types'\n\ninterface AddIdeWorktreeFoldersArgs {\n projectRoot: string\n worktreeDir: string\n branches: string[]\n}\n\n/**\n * Workspace-mode add (used by `worktrees-add`): append the given worktree folders\n * to every configured editor's workspace and open it. Returns one outcome per\n * configured provider (empty array when no IDE is configured), iterating\n * sequentially.\n *\n * Cursor appends to the `.code-workspace` `folders` array (reporting `skipped`\n * for already-present entries) then runs `cursor <workspace>`. Zed has no\n * workspace file, so it `zed --add`s each folder (`skipped` is always 0).\n */\nexport const addIdeWorktreeFolders = async (\n args: AddIdeWorktreeFoldersArgs,\n): Promise<AddIdeWorktreeFoldersOutcome[]> => {\n const { projectRoot, worktreeDir, branches } = args\n\n const config = await getInfraKitConfig()\n const ides = resolveConfiguredIdes(config)\n\n const folderPaths = branches.map((branch) => {\n return `${worktreeDir}/${branch}`\n })\n\n const outcomes: AddIdeWorktreeFoldersOutcome[] = []\n\n for (const ide of ides) {\n switch (ide.provider) {\n case 'cursor': {\n if (!ide.config.workspaceConfigPath) {\n logger.warn('\u26A0\uFE0F Skipping Cursor: ide.config.workspaceConfigPath is not set in infra-kit config')\n\n outcomes.push({ ran: false, provider: 'cursor', added: 0, skipped: 0 })\n break\n }\n\n const workspacePath = resolveCursorWorkspacePath(ide.config.workspaceConfigPath, projectRoot)\n\n const { added, skipped } = await addFoldersToCursorWorkspace({ workspacePath, folderPaths })\n\n const skippedSuffix = skipped.length > 0 ? ` (${skipped.length} already present)` : ''\n\n logger.info(`\u2705 Added ${added.length} folder(s) to ${workspacePath}${skippedSuffix}`)\n\n await launchCursor(workspacePath)\n\n outcomes.push({ ran: true, provider: 'cursor', added: added.length, skipped: skipped.length })\n break\n }\n case 'zed': {\n const { added } = await addFoldersToZedWorkspace({ folderPaths })\n\n logger.info(`\u2705 Added ${added.length} folder(s) to Zed workspace`)\n\n outcomes.push({ ran: true, provider: 'zed', added: added.length, skipped: 0 })\n break\n }\n default: {\n assertNever(ide)\n }\n }\n }\n\n return outcomes\n}\n", "import { openCursorWorkspace } from 'src/integrations/cursor'\nimport { openZedWorkspace } from 'src/integrations/zed'\nimport { assertNever } from 'src/lib/assert-never'\nimport { getInfraKitConfig, resolveConfiguredIdes } from 'src/lib/infra-kit-config'\n\nimport type { OpenIdeWorkspaceOutcome } from './types'\n\ninterface OpenIdeWorkspaceArgs {\n projectRoot: string\n worktreeDir: string\n currentBranches: string[]\n}\n\n/**\n * Provider-agnostic entry point for the reload open: for every configured\n * editor, reconciles (or, for Zed, simply assembles) the workspace against the\n * release worktrees on disk and launches it \u2014 skipping the launch when there are\n * no worktrees, so `worktrees-reload` never pops a bare editor window. Returns\n * one outcome per configured provider (empty array when no IDE is configured).\n * Iterates sequentially \u2014 `worktrees-reload` already wraps this call in an outer\n * `Promise.all` with cmux, so a `Promise.all` here would compound editor-spawn\n * concurrency. Best-effort \u2014 every provider swallows failures into a warning.\n */\nexport const openIdeWorkspace = async (args: OpenIdeWorkspaceArgs): Promise<OpenIdeWorkspaceOutcome[]> => {\n const config = await getInfraKitConfig()\n const ides = resolveConfiguredIdes(config)\n\n const outcomes: OpenIdeWorkspaceOutcome[] = []\n\n for (const ide of ides) {\n switch (ide.provider) {\n case 'cursor': {\n const outcome = await openCursorWorkspace({ ...args, cursorConfig: ide.config })\n\n outcomes.push({ ...outcome, provider: 'cursor' })\n break\n }\n case 'zed': {\n const outcome = await openZedWorkspace(args)\n\n outcomes.push({ ...outcome, provider: 'zed' })\n break\n }\n default: {\n assertNever(ide)\n }\n }\n }\n\n return outcomes\n}\n", "import { assertNever } from 'src/lib/assert-never'\n\nimport type { IdeProvider } from './types'\n\n/**\n * Human-facing display name for an IDE provider, used in log lines. Falls back to\n * a generic \"IDE\" when no provider is configured.\n */\nexport const ideProviderLabel = (provider: IdeProvider | undefined): string => {\n switch (provider) {\n case 'cursor': {\n return 'Cursor'\n }\n case 'zed': {\n return 'Zed'\n }\n case undefined: {\n return 'IDE'\n }\n default: {\n return assertNever(provider)\n }\n }\n}\n", "import { removeFoldersFromCursorWorkspace, resolveCursorWorkspacePath } from 'src/integrations/cursor'\nimport { reuseZedWorkspace } from 'src/integrations/zed'\nimport { assertNever } from 'src/lib/assert-never'\nimport { getInfraKitConfig, resolveConfiguredIdes } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\n\nimport type { RemoveIdeWorktreeFoldersOutcome } from './types'\n\ninterface RemoveIdeWorktreeFoldersArgs {\n projectRoot: string\n worktreeDir: string\n /** All release worktrees BEFORE removal (release-only \u2014 see Zed note below). */\n currentWorktrees: string[]\n /** The subset of `currentWorktrees` that was just removed. */\n removedWorktrees: string[]\n /**\n * Whether the caller is on an interactive path where firing Zed's destructive\n * `zed --reuse` is acceptable (a human is present and confirming). Interactive\n * `worktrees-remove` passes `!confirmedCommand`; `worktrees-sync` and every\n * non-interactive/MCP run pass `false`.\n */\n allowEditorRelaunch: boolean\n}\n\n/**\n * Strip removed worktrees from every configured editor's workspace (used by\n * `worktrees-remove` and `worktrees-sync`). Returns one outcome per configured\n * provider (empty array when no IDE is configured or no worktrees were removed),\n * iterating sequentially.\n *\n * Cursor surgically edits the `.code-workspace` `folders` array \u2014 it removes\n * only the named paths and leaves everything else intact.\n *\n * Zed has no surgical remove. The only mutation mechanism, `zed --reuse`,\n * REPLACES the focused window's entire folder set, so it can only re-state the\n * release worktrees we know about and silently drops any other open folder\n * (`remaining` is built from release worktrees only). Because that is\n * destructive and unrecoverable, it fires ONLY when `allowEditorRelaunch` is\n * true (interactive `worktrees-remove`). On the non-interactive/MCP path it is a\n * deliberate no-op (`supported: true`, `removed: []`) with an info message \u2014 the\n * capability exists; skipping is a policy choice, not a missing capability.\n *\n * Zed's `removed` is ALWAYS `[]`: `--reuse` performs no diff, so it confirms no\n * specific removal and we never report intended-but-unverified paths.\n */\nexport const removeIdeWorktreeFolders = async (\n args: RemoveIdeWorktreeFoldersArgs,\n): Promise<RemoveIdeWorktreeFoldersOutcome[]> => {\n const { projectRoot, worktreeDir, currentWorktrees, removedWorktrees, allowEditorRelaunch } = args\n\n if (removedWorktrees.length === 0) {\n return []\n }\n\n const config = await getInfraKitConfig()\n const ides = resolveConfiguredIdes(config)\n\n const folderPaths = removedWorktrees.map((branch) => {\n return `${worktreeDir}/${branch}`\n })\n\n const remainingBranches = currentWorktrees.filter((branch) => {\n return !removedWorktrees.includes(branch)\n })\n\n const outcomes: RemoveIdeWorktreeFoldersOutcome[] = []\n\n for (const ide of ides) {\n switch (ide.provider) {\n case 'cursor': {\n outcomes.push(await removeFromCursor({ ide, projectRoot, folderPaths }))\n break\n }\n case 'zed': {\n outcomes.push(await removeFromZed({ projectRoot, worktreeDir, remainingBranches, allowEditorRelaunch }))\n break\n }\n default: {\n assertNever(ide)\n }\n }\n }\n\n return outcomes\n}\n\ninterface RemoveFromCursorArgs {\n ide: Extract<ReturnType<typeof resolveConfiguredIdes>[number], { provider: 'cursor' }>\n projectRoot: string\n folderPaths: string[]\n}\n\n/** Surgically strip the removed folder paths from the Cursor `.code-workspace`. */\nconst removeFromCursor = async (args: RemoveFromCursorArgs): Promise<RemoveIdeWorktreeFoldersOutcome> => {\n const { ide, projectRoot, folderPaths } = args\n\n if (!ide.config.workspaceConfigPath) {\n return { provider: 'cursor', supported: true, removed: [] }\n }\n\n const workspacePath = resolveCursorWorkspacePath(ide.config.workspaceConfigPath, projectRoot)\n\n try {\n const { removed } = await removeFoldersFromCursorWorkspace({ workspacePath, folderPaths })\n\n if (removed.length > 0) {\n logger.info(`\u2705 Removed ${removed.length} folder(s) from ${workspacePath}`)\n }\n\n return { provider: 'cursor', supported: true, removed }\n } catch (error) {\n logger.warn({ error }, `\u26A0\uFE0F Failed to update Cursor workspace at ${workspacePath}`)\n\n return { provider: 'cursor', supported: true, removed: [] }\n }\n}\n\ninterface RemoveFromZedArgs {\n projectRoot: string\n worktreeDir: string\n remainingBranches: string[]\n allowEditorRelaunch: boolean\n}\n\n/**\n * Reflect the removal in Zed by relaunching the focused window onto the\n * remaining set \u2014 but ONLY on an interactive path (see the module doc for why\n * `zed --reuse` is destructive). Otherwise a deliberate no-op. `removed` is\n * always `[]` because `--reuse` performs no diff.\n */\nconst removeFromZed = async (args: RemoveFromZedArgs): Promise<RemoveIdeWorktreeFoldersOutcome> => {\n const { projectRoot, worktreeDir, remainingBranches, allowEditorRelaunch } = args\n\n if (!allowEditorRelaunch) {\n logger.info(\n '\u2139\uFE0F Zed folder removal skipped (no interactive session); close removed worktree folders in Zed manually if needed.',\n )\n\n return { provider: 'zed', supported: true, removed: [] }\n }\n\n await reuseZedWorkspace({ projectRoot, worktreeDir, remainingBranches })\n\n return { provider: 'zed', supported: true, removed: [] }\n}\n", "export type IdeProvider = 'cursor' | 'zed'\n\n/**\n * CLI-level open modes for `worktrees-add` (`--ide`). `workspace` adds each\n * created worktree to the configured editor's workspace and opens it (the only\n * attach style); `none` means \"don't open any editor\".\n */\nexport const IDE_MODES = ['workspace', 'none'] as const\nexport type IdeMode = (typeof IDE_MODES)[number]\n\n// Each facade returns one outcome PER configured provider (an array), so every\n// outcome is tagged with its `provider`.\nexport interface OpenIdeWorkspaceOutcome {\n ran: boolean\n provider: IdeProvider\n added: number\n removed: number\n}\n\nexport interface AddIdeWorktreeFoldersOutcome {\n ran: boolean\n provider: IdeProvider\n added: number\n skipped: number\n}\n\nexport interface RemoveIdeWorktreeFoldersOutcome {\n provider: IdeProvider\n /**\n * True when the provider can reflect a removal. Both providers now report\n * `true`; `false` is reserved for a future provider with no remove capability.\n */\n supported: boolean\n /**\n * Folders confirmed removed. For Cursor this is the real diff of the\n * `.code-workspace` `folders` array. For Zed it is ALWAYS `[]`: the only\n * mechanism (`zed --reuse`) replaces the whole window without producing a\n * diff, so it confirms no specific removal even when it refreshed the window.\n */\n removed: string[]\n}\n", "import { z } from 'zod'\n\nimport { getReleasePRsWithInfo } from 'src/integrations/gh'\nimport { getCurrentWorktrees } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { displayLabel, formatJiraName, parseBranchName } from 'src/lib/release-id'\nimport { detectReleaseType, formatVersionLabel, getJiraDescriptions } from 'src/lib/release-utils'\nimport type { ReleaseType } from 'src/lib/release-utils'\nimport { defineMcpTool, textContent } from 'src/types'\n\ninterface WorktreeInfo {\n version: string\n type: ReleaseType\n description: string | null\n}\n\n/**\n * List all release git worktrees with version, type, and Jira description\n */\nexport const worktreesList = async () => {\n const currentWorktrees = await getCurrentWorktrees('release')\n\n if (currentWorktrees.length === 0) {\n logger.info('\u2139\uFE0F No active worktrees found')\n\n return {\n content: textContent(JSON.stringify({ worktrees: [], count: 0 }, null, 2)),\n structuredContent: { worktrees: [], count: 0 },\n }\n }\n\n const [releasePRsInfo, jiraDescriptions] = await Promise.all([getReleasePRsWithInfo(), getJiraDescriptions()])\n\n const releaseTypes = new Map<string, ReleaseType>(\n releasePRsInfo.map((pr) => {\n return [pr.branch, detectReleaseType(pr.title)]\n }),\n )\n\n // Skip worktrees whose branch does not parse as a release id (lenient source).\n const worktrees: WorktreeInfo[] = currentWorktrees.flatMap((branch) => {\n const id = parseBranchName(branch)\n\n if (!id) return []\n\n // Human label `1.2.3` | `<name>`; Jira-descriptions map is keyed by the\n // Jira version NAME (`v1.2.3` | `<name>`) \u2014 same split as formatBranchChoices.\n const version = displayLabel(id)\n const type = releaseTypes.get(branch) || 'regular'\n const description = jiraDescriptions.get(formatJiraName(id)) || null\n\n return [{ version, type, description }]\n })\n\n // Log formatted output\n const maxVersionLength = Math.max(\n ...worktrees.map((w) => {\n return w.version.length\n }),\n )\n\n const formattedLines = worktrees.map((worktree) => {\n const label = formatVersionLabel(worktree.version, worktree.type, maxVersionLength)\n\n if (worktree.description) {\n return `${label} ${worktree.description}`\n }\n\n return label\n })\n\n logger.info('\uD83C\uDF3F Active worktrees:')\n logger.info(`\\n${formattedLines.join('\\n')}\\n`)\n\n const structuredContent = {\n worktrees,\n count: worktrees.length,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n}\n\n// MCP Tool Registration\nexport const worktreesListMcpTool = defineMcpTool({\n name: 'worktrees-list',\n description:\n 'List existing release-branch worktrees with version, release type (regular / hotfix), and Jira fix-version description. Read-only.',\n inputSchema: {},\n outputSchema: {\n worktrees: z\n .array(\n z.object({\n version: z.string().describe('Release version'),\n type: z.enum(['regular', 'hotfix']).describe('Release type'),\n description: z.string().nullable().describe('Jira version description'),\n }),\n )\n .describe('List of all worktrees with details'),\n count: z.number().describe('Number of worktrees'),\n },\n handler: worktreesList,\n})\n", "import { z } from 'zod'\n\nimport {\n buildCmuxWorkspaceTitle,\n canonicalizeCmuxTitle,\n closeCmuxWorkspaceByTitle,\n listCmuxWorkspaceTitles,\n openCmuxWorkspaceWithLayout,\n} from 'src/integrations/cmux'\nimport { ideProviderLabel, openIdeWorkspace } from 'src/integrations/ide'\nimport type { IdeProvider } from 'src/integrations/ide'\nimport { commandEcho } from 'src/lib/command-echo'\nimport { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { getCurrentWorktrees, getProjectRoot, getRepoName } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { defineMcpTool, textContent } from 'src/types'\n\ninterface WorktreesReloadResult {\n closedCmux: string[]\n openedCmux: string[]\n ideProviders: IdeProvider[]\n ideFoldersAdded: number\n ideFoldersRemoved: number\n}\n\n/**\n * Reload command: closes every open cmux workspace for the release worktrees on\n * disk, then reopens the full set \u2014 cmux workspaces are recreated fresh and the\n * configured editor workspace is reconciled and relaunched. The single\n * window-restore command: also serves the cold-start case (editor + cmux closed),\n * at the cost of tearing down any live cmux windows first.\n *\n * The editor has no per-window close primitive, so its \"reload\" is a reconcile +\n * relaunch of the configured workspace. Safe to run from any branch\n * (non-destructive to git \u2014 only cmux/editor view state is touched).\n */\nexport const worktreesReload = async () => {\n try {\n const projectRoot = await getProjectRoot()\n const worktreeDir = `${projectRoot}${WORKTREES_DIR_SUFFIX}`\n const currentBranches = await getCurrentWorktrees('release')\n const repoName = await getRepoName()\n\n // Phase 1 \u2014 close (cmux only; Cursor has no per-window close).\n const closedCmux = await closeCmux({ currentBranches, repoName })\n\n // Phase 2 \u2014 reopen. cmux and Cursor are independent surfaces, so reopen them\n // concurrently to shrink the window where the just-closed cmux workspaces are gone.\n const [ideOutcomes, { opened: openedCmux }] = await Promise.all([\n openIdeWorkspace({ projectRoot, worktreeDir, currentBranches }),\n reopenCmux({ worktreeDir, currentBranches, repoName }),\n ])\n\n const result: WorktreesReloadResult = {\n closedCmux,\n openedCmux,\n ideProviders: ideOutcomes\n .filter((outcome) => {\n return outcome.ran\n })\n .map((outcome) => {\n return outcome.provider\n }),\n ideFoldersAdded: ideOutcomes.reduce((sum, outcome) => {\n return sum + outcome.added\n }, 0),\n ideFoldersRemoved: ideOutcomes.reduce((sum, outcome) => {\n return sum + outcome.removed\n }, 0),\n }\n\n logResults(result, {\n ideRan: ideOutcomes.some((outcome) => {\n return outcome.ran\n }),\n })\n\n commandEcho.print()\n\n return {\n content: textContent(JSON.stringify(result, null, 2)),\n structuredContent: { ...result },\n }\n } catch (error) {\n logger.error({ error }, '\u274C Error reloading worktrees')\n throw new OperationError(error, {\n operation: 'reload worktrees',\n remediation: 'run `infra-kit worktrees list` to confirm the branches exist',\n })\n }\n}\n\ninterface CloseCmuxArgs {\n currentBranches: string[]\n repoName: string\n}\n\n/**\n * Close the cmux workspace for each release worktree that is currently open.\n * Snapshots `listCmuxWorkspaceTitles()` up front purely so the returned list\n * reflects which workspaces were actually open (the close itself is best-effort\n * and per-title). Returns the titles a close was attempted for.\n */\nexport const closeCmux = async (args: CloseCmuxArgs): Promise<string[]> => {\n const { currentBranches, repoName } = args\n\n if (currentBranches.length === 0) {\n return []\n }\n\n const openBefore = await listCmuxWorkspaceTitles()\n\n const closed: string[] = []\n\n for (const branch of currentBranches) {\n const title = buildCmuxWorkspaceTitle({ repoName, branch })\n\n // openBefore holds canonical keys; match the built title the same way so a\n // workspace stored under a drifted title (whitespace / old v-prefix) resolves.\n if (!openBefore.has(canonicalizeCmuxTitle(title))) {\n continue\n }\n\n await closeCmuxWorkspaceByTitle(title)\n closed.push(title)\n }\n\n return closed\n}\n\ninterface ReopenCmuxArgs {\n worktreeDir: string\n currentBranches: string[]\n repoName: string\n}\n\ninterface ReopenCmuxOutcome {\n opened: string[]\n}\n\n/**\n * Recreate a cmux workspace for every release worktree on disk. Force-opens each\n * branch (no dedup against the live workspace list): reload has just awaited the\n * close of these workspaces, so it favours liveness \u2014 guaranteeing every branch\n * ends up open.\n */\nexport const reopenCmux = async (args: ReopenCmuxArgs): Promise<ReopenCmuxOutcome> => {\n const { worktreeDir, currentBranches, repoName } = args\n\n const opened: string[] = []\n\n for (const branch of currentBranches) {\n const title = buildCmuxWorkspaceTitle({ repoName, branch })\n\n try {\n await openCmuxWorkspaceWithLayout({ cwd: `${worktreeDir}/${branch}`, title })\n opened.push(title)\n } catch (error) {\n logger.warn({ error, title }, `\u26A0\uFE0F Failed to reopen cmux workspace for ${branch}`)\n }\n }\n\n return { opened }\n}\n\ninterface LogResultsContext {\n ideRan: boolean\n}\n\nconst logResults = (result: WorktreesReloadResult, context: LogResultsContext): void => {\n if (result.closedCmux.length > 0) {\n logger.info(`\uD83E\uDDF9 Closed ${result.closedCmux.length} cmux workspace(s)`)\n }\n\n if (context.ideRan) {\n const ideLabels = result.ideProviders\n .map((provider) => {\n return ideProviderLabel(provider)\n })\n .join(', ')\n\n if (result.ideFoldersAdded > 0) {\n logger.info(`\u2705 Added ${result.ideFoldersAdded} folder(s) to ${ideLabels} workspace(s)`)\n }\n\n if (result.ideFoldersRemoved > 0) {\n logger.info(`\uD83E\uDDF9 Removed ${result.ideFoldersRemoved} dangling folder(s) from ${ideLabels} workspace(s)`)\n }\n }\n\n if (result.openedCmux.length > 0) {\n logger.info('\u2705 Reopened cmux workspaces:')\n for (const title of result.openedCmux) {\n logger.info(title)\n }\n }\n\n if (\n !context.ideRan &&\n result.closedCmux.length === 0 &&\n result.openedCmux.length === 0 &&\n result.ideFoldersAdded === 0 &&\n result.ideFoldersRemoved === 0\n ) {\n logger.info('\u2139\uFE0F Nothing to reload')\n }\n}\n\n// MCP Tool Registration\nexport const worktreesReloadMcpTool = defineMcpTool({\n name: 'worktrees-reload',\n description:\n 'Close all open cmux workspaces for the current release worktrees, then reopen the full set \u2014 the single window-restore command. Every configured editor (Cursor and/or Zed) is reconciled + relaunched (no per-window close). Disruptive: closes live cmux windows first. Safe to run from any branch. Use to refresh stale windows or to restore everything after a cold start (editor + cmux closed).',\n inputSchema: {},\n outputSchema: {\n closedCmux: z.array(z.string()).describe('Titles of cmux workspaces a close was attempted for (best-effort)'),\n openedCmux: z.array(z.string()).describe('Titles of cmux workspaces reopened during this run'),\n ideProviders: z\n .array(z.string())\n .describe('Configured IDE providers that were reloaded (cursor | zed); empty if none configured'),\n ideFoldersAdded: z.number().describe('Total worktree folders added across all configured editor workspaces'),\n ideFoldersRemoved: z\n .number()\n .describe('Total dangling worktree folders removed across all configured editor workspaces'),\n },\n handler: worktreesReload,\n})\n", "import { z } from 'zod'\n\nimport { getReleasePRsWithInfo } from 'src/integrations/gh'\nimport { removeIdeWorktreeFolders } from 'src/integrations/ide'\nimport { commandEcho, confirmOrExit } from 'src/lib/command-echo'\nimport { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'\nimport { isPromptCancellation } from 'src/lib/errors/is-prompt-cancellation'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { assertManagementContext } from 'src/lib/git-guard'\nimport { getCurrentWorktrees, getProjectRoot, getRepoName } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { pickReleaseBranches } from 'src/lib/prompts/release-picker'\nimport { formatBranchName, parseReleaseRef } from 'src/lib/release-id'\nimport {\n detectReleaseType,\n formatBranchPickerItems,\n getJiraDescriptions,\n releaseBranchLabels,\n} from 'src/lib/release-utils'\nimport type { ReleaseType } from 'src/lib/release-utils'\nimport { removeWorktrees } from 'src/lib/worktrees'\nimport { defineMcpTool, textContent } from 'src/types'\nimport type { RequiredConfirmedOptionArg } from 'src/types'\n\n// Constants\ninterface WorktreeManagementArgs extends RequiredConfirmedOptionArg {\n all?: boolean\n versions?: string\n}\n\n/**\n * Manage git worktrees for release branches\n * Creates worktrees for active release branches and removes unused ones\n */\nexport const worktreesRemove = async (options: WorktreeManagementArgs) => {\n const { confirmedCommand, all, versions } = options\n\n await assertManagementContext({ operation: 'remove worktrees', requiredBranch: 'dev' })\n\n try {\n const currentWorktrees = await getCurrentWorktrees('release')\n\n if (currentWorktrees.length === 0) {\n logger.info('\u2139\uFE0F No active worktrees to remove')\n\n commandEcho.print()\n\n return {\n content: textContent(JSON.stringify({ removedWorktrees: [], count: 0 }, null, 2)),\n structuredContent: { removedWorktrees: [], count: 0 },\n }\n }\n\n const projectRoot = await getProjectRoot()\n\n const worktreeDir = `${projectRoot}${WORKTREES_DIR_SUFFIX}`\n\n let selectedReleaseBranches: string[] = []\n\n if (all) {\n selectedReleaseBranches = currentWorktrees\n } else if (versions) {\n selectedReleaseBranches = versions.split(',').map((v) => {\n return formatBranchName(parseReleaseRef(v.trim()))\n })\n } else {\n commandEcho.setInteractive()\n\n const [descriptions, prInfo] = await Promise.all([getJiraDescriptions(), getReleasePRsWithInfo()])\n\n const releaseTypes = new Map<string, ReleaseType>(\n prInfo.map((pr) => {\n return [pr.branch, detectReleaseType(pr.title)]\n }),\n )\n\n selectedReleaseBranches = await pickReleaseBranches(\n formatBranchPickerItems({ branches: currentWorktrees, descriptions, types: releaseTypes }),\n { required: true },\n )\n }\n\n // Track --all flag if all branches were selected (either via flag or interactively)\n const allSelected = selectedReleaseBranches.length === currentWorktrees.length\n\n if (allSelected) {\n commandEcho.addOption('--all', true)\n } else {\n commandEcho.addOption('--versions', releaseBranchLabels(selectedReleaseBranches))\n }\n\n // Ask for confirmation\n await confirmOrExit(confirmedCommand, 'Are you sure you want to proceed with these worktree changes?')\n\n // Track --yes flag if confirmation was interactive (user confirmed)\n if (!confirmedCommand) {\n commandEcho.addOption('--yes', true)\n }\n\n const repoName = await getRepoName()\n\n const removedWorktrees = await removeWorktrees({\n branches: selectedReleaseBranches,\n worktreeDir,\n repoName,\n pruneFolder: allSelected,\n })\n\n // `!confirmedCommand` is the interactive path (a human used the picker/confirm). Only there\n // do we let Zed's destructive `--reuse` relaunch fire; MCP/--yes runs (confirmedCommand=true)\n // and worktrees-sync never relaunch an editor window.\n await removeIdeWorktreeFolders({\n projectRoot,\n worktreeDir,\n currentWorktrees,\n removedWorktrees,\n allowEditorRelaunch: !confirmedCommand,\n })\n\n logResults(removedWorktrees)\n\n commandEcho.print()\n\n const structuredContent = {\n removedWorktrees,\n count: removedWorktrees.length,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n } catch (error) {\n // A cancelled prompt (Ctrl-C / Esc) is a user back-out, not a failure: let it\n // reach the top-level boundary untouched so it exits cleanly, instead of being\n // logged as an error with a misleading remediation.\n if (isPromptCancellation(error)) throw error\n\n logger.error({ error }, '\u274C Error managing worktrees')\n throw new OperationError(error, {\n operation: 'remove worktrees',\n remediation: \"check 'git worktree list' for the path; uncommitted changes block removal\",\n })\n }\n}\n\n/**\n * Log the results of worktree management\n */\nconst logResults = (removed: string[]): void => {\n if (removed.length > 0) {\n logger.info('\u274C Removed worktrees:')\n for (const branch of removed) {\n logger.info(branch)\n }\n logger.info('')\n } else {\n logger.info('\u2139\uFE0F No unused worktrees to remove')\n }\n}\n\n// MCP Tool Registration\nexport const worktreesRemoveMcpTool = defineMcpTool({\n name: 'worktrees-remove',\n description:\n 'Remove local git worktrees for release branches. When everything is removed, also runs \"git worktree prune\" to clear stale metadata; the worktrees directory and its release/feature subfolders are left in place. When invoked via MCP, pass either \"versions\" (comma-separated) or all=true \u2014 the branch picker is unreachable without a TTY, and the CLI confirmation is auto-skipped for MCP calls, so the caller is responsible for gating.',\n inputSchema: {\n all: z\n .boolean()\n .optional()\n .describe(\n 'Remove every existing worktree. Either \"all\" or \"versions\" must be provided for MCP calls (the interactive picker is unavailable without a TTY). Ignored if \"versions\" is provided.',\n ),\n versions: z\n .string()\n .optional()\n .describe(\n 'Comma-separated release versions or names to target (e.g. \"1.2.5, 1.2.6\" or \"checkout-redesign, 1.2.5\"). Either \"versions\" or all=true must be provided for MCP calls. Overrides \"all\" when set.',\n ),\n },\n outputSchema: {\n removedWorktrees: z.array(z.string()).describe('List of removed git worktree branches'),\n count: z.number().describe('Number of git worktrees removed'),\n },\n handler: worktreesRemove,\n})\n", "import { z } from 'zod'\nimport { $ } from 'zx'\n\nimport { buildCmuxWorkspaceTitle, closeCmuxWorkspaceByTitle } from 'src/integrations/cmux'\nimport { getReleasePRs } from 'src/integrations/gh'\nimport { removeIdeWorktreeFolders } from 'src/integrations/ide'\nimport { commandEcho, confirmOrExit } from 'src/lib/command-echo'\nimport { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'\nimport { isPromptCancellation } from 'src/lib/errors/is-prompt-cancellation'\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { assertManagementContext } from 'src/lib/git-guard'\nimport { getCurrentWorktrees, getProjectRoot, getRepoName } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { isReleaseBranch } from 'src/lib/release-id'\nimport { defineMcpTool, textContent } from 'src/types'\nimport type { RequiredConfirmedOptionArg } from 'src/types'\n\ninterface WorktreeSyncArgs extends RequiredConfirmedOptionArg {}\n\n/**\n * Manage git worktrees for release branches.\n *\n * Creates worktrees for active release branches and removes unused ones\n */\nexport const worktreesSync = async (options: WorktreeSyncArgs) => {\n const { confirmedCommand } = options\n\n await assertManagementContext({ operation: 'sync worktrees', requiredBranch: 'dev' })\n\n try {\n const currentWorktrees = await getCurrentWorktrees('release')\n const projectRoot = await getProjectRoot()\n\n const worktreeDir = `${projectRoot}${WORKTREES_DIR_SUFFIX}`\n\n const releasePRsList = await getReleasePRs()\n\n // Ask for confirmation\n await confirmOrExit(confirmedCommand, 'Are you sure you want to proceed with these worktree changes?')\n\n // Track --yes flag if confirmation was interactive (user confirmed)\n if (!confirmedCommand) {\n commandEcho.addOption('--yes', true)\n }\n\n const { branchesToRemove } = categorizeWorktrees({\n releasePRsList,\n currentWorktrees,\n })\n\n const repoName = await getRepoName()\n\n const removedWorktrees = await removeWorktrees({\n branches: branchesToRemove,\n worktreeDir,\n repoName,\n })\n\n // Hard `false`: sync is background/stale-cleanup (predominantly the MCP path) \u2014 it must never\n // relaunch and overwrite a focused Zed window. Zed removal stays a no-op here.\n await removeIdeWorktreeFolders({\n projectRoot,\n worktreeDir,\n currentWorktrees,\n removedWorktrees,\n allowEditorRelaunch: false,\n })\n\n logResults(removedWorktrees)\n\n commandEcho.print()\n\n const structuredContent = {\n removedWorktrees,\n count: removedWorktrees.length,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n }\n } catch (error) {\n // A cancelled prompt (Ctrl-C / Esc) is a user back-out, not a failure: let it\n // reach the top-level boundary untouched so it exits cleanly, instead of being\n // logged as an error with a misleading remediation.\n if (isPromptCancellation(error)) throw error\n\n logger.error({ error }, '\u274C Error managing worktrees')\n throw new OperationError(error, {\n operation: 'sync worktrees with remote',\n remediation: \"ensure 'gh auth status' is ok and you can reach origin\",\n })\n }\n}\n\ninterface CategorizeWorktreesArgs {\n releasePRsList: string[]\n currentWorktrees: string[]\n}\n\n/**\n * Categorize worktrees into those that need to be created or removed\n */\nconst categorizeWorktrees = (args: CategorizeWorktreesArgs): { branchesToRemove: string[] } => {\n const { releasePRsList, currentWorktrees } = args\n\n const currentBranchNames = currentWorktrees.filter((branch) => {\n return isReleaseBranch(branch)\n })\n\n const branchesToRemove = currentBranchNames.filter((branch) => {\n return !releasePRsList.includes(branch)\n })\n\n return { branchesToRemove }\n}\n\ninterface RemoveWorktreesArgs {\n branches: string[]\n worktreeDir: string\n repoName: string\n}\n\n/**\n * Remove worktrees for the specified branches and close their cmux workspaces\n */\nconst removeWorktrees = async (args: RemoveWorktreesArgs): Promise<string[]> => {\n const { branches, worktreeDir, repoName } = args\n\n const removed: string[] = []\n\n for (const branch of branches) {\n try {\n const worktreePath = `${worktreeDir}/${branch}`\n\n const title = buildCmuxWorkspaceTitle({ repoName, branch })\n\n await closeCmuxWorkspaceByTitle(title)\n\n await $`git worktree remove ${worktreePath}`\n removed.push(branch)\n } catch (error) {\n const err = new OperationError(error, {\n operation: `remove stale worktree for ${branch}`,\n remediation: 'inspect the worktree dir manually; rerun with the branch checked out elsewhere',\n })\n\n logger.error({ error, branch, msg: err.message })\n }\n }\n\n return removed\n}\n\n/**\n * Log the results of worktree management\n */\nconst logResults = (removed: string[]): void => {\n if (removed.length > 0) {\n logger.info('\u274C Removed worktrees:')\n for (const branch of removed) {\n logger.info(branch)\n }\n logger.info('')\n } else {\n logger.info('\u2139\uFE0F No unused worktrees to remove')\n }\n}\n\n// MCP Tool Registration\nexport const worktreesSyncMcpTool = defineMcpTool({\n name: 'worktrees-sync',\n description:\n 'Remove worktrees whose release PR is no longer open (stale cleanup). Only removes \u2014 never creates; use worktrees-add to create worktrees for new releases. The CLI confirmation is auto-skipped for MCP calls, so the caller is responsible for gating.',\n inputSchema: {},\n outputSchema: {\n removedWorktrees: z.array(z.string()).describe('List of removed worktree branches'),\n count: z.number().describe('Number of worktrees removed during sync'),\n },\n handler: worktreesSync,\n})\n", "import type { z } from 'zod'\n\nimport { auditMcpTool } from 'src/commands/audit'\nimport { doctorMcpTool } from 'src/commands/doctor'\nimport { envClearMcpTool } from 'src/commands/env-clear'\nimport { envListMcpTool } from 'src/commands/env-list'\nimport { envLoadMcpTool } from 'src/commands/env-load'\nimport { envStatusMcpTool } from 'src/commands/env-status'\nimport { ghMergeDevMcpTool } from 'src/commands/gh-merge-dev'\nimport { ghReleaseDeliverMcpTool } from 'src/commands/gh-release-deliver'\nimport { ghReleaseDeployAllMcpTool } from 'src/commands/gh-release-deploy-all'\nimport { ghReleaseDeploySelectedMcpTool } from 'src/commands/gh-release-deploy-selected'\nimport { ghReleaseListMcpTool } from 'src/commands/gh-release-list'\nimport { releaseCreateMcpTool } from 'src/commands/release-create'\nimport { releaseDescEditMcpTool } from 'src/commands/release-desc-edit'\nimport { vendorCheckMcpTool } from 'src/commands/vendor-check'\nimport { vendorDiffMcpTool } from 'src/commands/vendor-diff'\nimport { vendorManifestMcpTool } from 'src/commands/vendor-manifest'\nimport { vendorSyncMcpTool } from 'src/commands/vendor-sync'\nimport { versionMcpTool } from 'src/commands/version'\nimport { worktreesAddMcpTool } from 'src/commands/worktrees-add'\nimport { worktreesListMcpTool } from 'src/commands/worktrees-list'\nimport { worktreesReloadMcpTool } from 'src/commands/worktrees-reload'\nimport { worktreesRemoveMcpTool } from 'src/commands/worktrees-remove'\nimport { worktreesSyncMcpTool } from 'src/commands/worktrees-sync'\nimport type { ToolsExecutionResult } from 'src/types'\n\n/**\n * Registration-facing shape of an MCP tool. The concrete `*McpTool` definitions\n * are generic over their Zod input/output shapes (and invariant), so they cannot\n * share one precise `McpTool<...>` element type in an array. This widened, non-\n * generic view exposes exactly what registration needs and every concrete tool\n * assigns to it. Matches the loose handler typing already used in tool-handler.\n */\nexport interface CatalogMcpTool {\n name: string\n description: string\n inputSchema: z.ZodRawShape\n outputSchema: z.ZodRawShape\n // Heterogeneous tool params; loose `any` mirrors the existing tool-handler typing.\n handler: (params: any) => Promise<ToolsExecutionResult>\n}\n\n/**\n * Single source of truth for the CLI command surface. It consolidates what used\n * to live in three hand-maintained places (the MCP `tools[]` array and the\n * three no-arg-menu name arrays) into one list, so they can no longer drift.\n *\n * It does NOT replace Commander's `.command().option()` wiring in entry/cli.ts \u2014\n * that stays the source of truth for argument parsing. This catalog only carries\n * cross-surface metadata: the canonical names, which menu group a command shows\n * in, and whether the command is exposed as an MCP tool.\n */\n\n/**\n * The menu groups, in display order \u2014 the SINGLE source of both membership and presentation. `cli.ts`\n * renders the palette by mapping over this list, so adding, renaming, or reordering a group is a\n * one-line edit here and nowhere else. (The group list used to be restated three times in `cli.ts`, and\n * the labels a fourth time in the Inquirer separators.)\n *\n * One group per CLI noun, ordered by how often it is reached for. `Develop` leads because `dev` is the\n * daily driver. `Environment` means the Doppler env commands and nothing else \u2014 it used to be a\n * 13-entry drawer holding config, vendor, and setup commands too, which made its name a lie.\n */\nexport const MENU_GROUPS = [\n { key: 'develop', label: 'Develop' },\n { key: 'release', label: 'Release Management' },\n { key: 'worktrees', label: 'Worktrees' },\n { key: 'environment', label: 'Environment' },\n { key: 'configuration', label: 'Configuration' },\n { key: 'vendor', label: 'Vendor' },\n { key: 'setup', label: 'Setup & Diagnostics' },\n] as const\n\n/** Top-level menu group for the no-arg interactive picker (null = not shown). */\nexport type MenuGroup = (typeof MENU_GROUPS)[number]['key']\n\nexport interface CommandCatalogEntry {\n /**\n * Stable flat id for the command (`release-create`), and the name its MCP tool registers under \u2014 MCP\n * tool names cannot contain a space, so the grouped path cannot serve as one.\n *\n * NOT a CLI surface: the CLI registers only the grouped form ({@link groupPath}). Flat Commander\n * commands were removed once every menu surface learned to address a command by its group path.\n */\n cliName: string\n /** Menu group, or null for subcommands not shown at the top level. */\n menuGroup: MenuGroup | null\n /** The co-located MCP tool, or null for CLI-only commands (init/config/vendor group). */\n mcpTool: CatalogMcpTool | null\n /**\n * Whether the command is registered as an MCP tool. Explicit allowlist:\n * `doctor`, `vendor-sync`, and `vendor-manifest` are deliberately UNEXPOSED\n * (vendor-sync/manifest mutate consumer repos; doctor is host-inspecting), so\n * they must never become agent-callable by accident.\n */\n mcpExposed: boolean\n /**\n * Canonical Commander argv for this command (grouped form, e.g. `['vendor','check']`) \u2014 the ONLY form\n * the CLI registers. Every menu surface renders `groupPath.join(' ')` and spawns/parses\n * `infra-kit <...groupPath>`, so what the palette shows is exactly what it runs and what the user can\n * retype.\n */\n groupPath: string[]\n /**\n * The command hands the terminal to a full-screen child (an `$EDITOR`) that enters the alternate\n * screen and restores it itself. The session never enters it for them \u2014 but a child killed before it\n * restores the primary buffer would leave every later frame drawn into an abandoned buffer, so the\n * session's post-child hygiene reset leaves the alternate screen defensively. Only `config-edit`.\n */\n entersAltScreen?: boolean\n /**\n * Running this from inside the session cannot mutate the parent shell's env (a child can't). The\n * transcript entry appends a notice that the change applies only after the session exits. `env-load`\n * / `env-clear`.\n */\n sessionEnvNotice?: boolean\n /**\n * A foreground process that runs until the user stops it, rather than doing a unit of work and\n * exiting. Only `dev`. It breaks BOTH of the session shell's assumptions about a child, so the two\n * consequences are deliberately carried by ONE flag \u2014 they must never be set apart:\n *\n * 1. **It produces no verdict.** Its Commander action RESOLVES seconds after boot (the process stays\n * alive on fastify/chokidar handles, not on a pending promise), so `postAction` would write a\n * session report while the server is merely *starting*. Report-presence means \"the command produced\n * a verdict\" (see `session/report.ts`), so the report is suppressed for it entirely.\n * 2. **It exits `128 + signo`, not 0.** It installs its own SIGINT handler and exits 130 on a Ctrl-C\n * \u2014 deliberately, so a supervisor sees an honest signal death. Without this flag the session would\n * read that as a failure and stamp every normal dev exit `\u2717 failed`.\n */\n longRunning?: boolean\n}\n\n/**\n * Authored in no-arg-menu display order so the interactive picker derives\n * directly from this list (see entry/cli.ts). MCP registration filters this\n * list by `mcpExposed`; MCP tool order is not contractual (clients address\n * tools by name), so the registration order need not match the array order.\n */\nexport const commandCatalog: CommandCatalogEntry[] = [\n // --- Develop (menu group) ---\n // The local dev server, and the reason most people open this menu at all \u2014 so it leads.\n //\n // It is a long-running foreground child, which the one-shot Inquirer menu could not host: that menu\n // ran the pick IN-PROCESS, so a dev server would have blocked the menu process forever. The session\n // shell spawns every pick as a separate child on the primary screen and swallows SIGINT while one\n // runs, so Ctrl-C stops only the dev server and the palette comes back. See `longRunning` for the two\n // session assumptions it still breaks (no verdict; exits 128+signo).\n //\n // Spawning the BARE `['dev']` path is deliberate: `shouldRunWizard` fires on exactly bare + TTY +\n // non-json, so a menu pick lands in the interactive wizard \u2014 the wizard IS the flag picker.\n //\n // Not an MCP tool: it never returns a result, so it cannot fit the request/response tool contract.\n {\n cliName: 'dev',\n menuGroup: 'develop',\n mcpTool: null,\n mcpExposed: false,\n groupPath: ['dev'],\n longRunning: true,\n },\n\n // --- Release Management (menu group) ---\n {\n cliName: 'merge-dev',\n menuGroup: 'release',\n mcpTool: ghMergeDevMcpTool,\n mcpExposed: true,\n groupPath: ['release', 'merge-dev'],\n },\n {\n cliName: 'release-list',\n menuGroup: 'release',\n mcpTool: ghReleaseListMcpTool,\n mcpExposed: true,\n groupPath: ['release', 'list'],\n },\n {\n cliName: 'release-create',\n menuGroup: 'release',\n mcpTool: releaseCreateMcpTool,\n mcpExposed: true,\n groupPath: ['release', 'create'],\n },\n {\n cliName: 'release-desc-edit',\n menuGroup: 'release',\n mcpTool: releaseDescEditMcpTool,\n mcpExposed: true,\n groupPath: ['release', 'desc-edit'],\n },\n {\n cliName: 'release-deploy-all',\n menuGroup: 'release',\n mcpTool: ghReleaseDeployAllMcpTool,\n mcpExposed: true,\n groupPath: ['release', 'deploy-all'],\n },\n {\n cliName: 'release-deploy-selected',\n menuGroup: 'release',\n mcpTool: ghReleaseDeploySelectedMcpTool,\n mcpExposed: true,\n groupPath: ['release', 'deploy-selected'],\n },\n // release-deliver does prod delivery + admin-merge \u2014 genuinely irreversible,\n // so it is CLI-only by design (mirrors the vendor-sync/manifest rationale).\n {\n cliName: 'release-deliver',\n menuGroup: 'release',\n mcpTool: ghReleaseDeliverMcpTool,\n mcpExposed: false,\n groupPath: ['release', 'deliver'],\n },\n\n // --- Worktrees (menu group) ---\n {\n cliName: 'worktrees-add',\n menuGroup: 'worktrees',\n mcpTool: worktreesAddMcpTool,\n mcpExposed: true,\n groupPath: ['worktrees', 'add'],\n },\n {\n cliName: 'worktrees-list',\n menuGroup: 'worktrees',\n mcpTool: worktreesListMcpTool,\n mcpExposed: true,\n groupPath: ['worktrees', 'list'],\n },\n {\n cliName: 'worktrees-reload',\n menuGroup: 'worktrees',\n mcpTool: worktreesReloadMcpTool,\n mcpExposed: true,\n groupPath: ['worktrees', 'reload'],\n },\n // worktrees-remove runs `git worktree remove` on each leaf worktree \u2014\n // genuinely irreversible (uncommitted work is lost), so it is CLI-only by\n // design (mirrors the vendor-sync/manifest rationale).\n {\n cliName: 'worktrees-remove',\n menuGroup: 'worktrees',\n mcpTool: worktreesRemoveMcpTool,\n mcpExposed: false,\n groupPath: ['worktrees', 'remove'],\n },\n {\n cliName: 'worktrees-sync',\n menuGroup: 'worktrees',\n mcpTool: worktreesSyncMcpTool,\n mcpExposed: true,\n groupPath: ['worktrees', 'sync'],\n },\n\n // --- Environment (menu group): the Doppler env commands, and ONLY those ---\n // Every menu-eligible entry must be a Commander LEAF (an action, no subcommands) so the session\n // shell's report discriminator holds. The bare `vendor`/`config` GROUPS are menuGroup:null \u2014 a bare\n // group prints help and exits non-zero, which the shell would misreport. Their useful leaves\n // (vendor-check/diff/config, config-path/edit) are surfaced in the Vendor/Configuration groups below,\n // each with a hidden flat Commander alias (see src/lib/program/program.ts) so the palette can\n // introspect + single-token dispatch.\n {\n cliName: 'env-status',\n menuGroup: 'environment',\n mcpTool: envStatusMcpTool,\n mcpExposed: true,\n groupPath: ['env-status'],\n },\n { cliName: 'env-list', menuGroup: 'environment', mcpTool: envListMcpTool, mcpExposed: true, groupPath: ['env-list'] },\n {\n cliName: 'env-load',\n menuGroup: 'environment',\n mcpTool: envLoadMcpTool,\n mcpExposed: true,\n groupPath: ['env-load'],\n sessionEnvNotice: true,\n },\n {\n cliName: 'env-clear',\n menuGroup: 'environment',\n mcpTool: envClearMcpTool,\n mcpExposed: true,\n groupPath: ['env-clear'],\n sessionEnvNotice: true,\n },\n\n // --- Configuration (menu group) ---\n {\n cliName: 'config-path',\n menuGroup: 'configuration',\n mcpTool: null,\n mcpExposed: false,\n groupPath: ['config', 'path'],\n },\n {\n cliName: 'config-edit',\n menuGroup: 'configuration',\n mcpTool: null,\n mcpExposed: false,\n groupPath: ['config', 'edit'],\n entersAltScreen: true,\n },\n\n // --- Vendor (menu group) ---\n {\n cliName: 'vendor-check',\n menuGroup: 'vendor',\n mcpTool: vendorCheckMcpTool,\n mcpExposed: true,\n groupPath: ['vendor', 'check'],\n },\n {\n cliName: 'vendor-diff',\n menuGroup: 'vendor',\n mcpTool: vendorDiffMcpTool,\n mcpExposed: true,\n groupPath: ['vendor', 'diff'],\n },\n {\n cliName: 'vendor-config',\n menuGroup: 'vendor',\n mcpTool: null,\n mcpExposed: false,\n groupPath: ['vendor', 'config'],\n },\n\n // --- Setup & Diagnostics (menu group) ---\n // `init`/`doctor` set the machine up and check it; `audit` checks the REPO against its config rules.\n // Both answer \"is this in a good state?\", which is why they sit together.\n { cliName: 'init', menuGroup: 'setup', mcpTool: null, mcpExposed: false, groupPath: ['init'] },\n { cliName: 'doctor', menuGroup: 'setup', mcpTool: doctorMcpTool, mcpExposed: false, groupPath: ['doctor'] },\n { cliName: 'audit', menuGroup: 'setup', mcpTool: auditMcpTool, mcpExposed: true, groupPath: ['audit'] },\n { cliName: 'version', menuGroup: 'setup', mcpTool: versionMcpTool, mcpExposed: true, groupPath: ['version'] },\n\n // --- Not menu items (groups, long-running, or internal) ---\n // Bare groups: help + non-zero exit, so they never belong in the leaf-only menu.\n { cliName: 'vendor', menuGroup: null, mcpTool: null, mcpExposed: false, groupPath: ['vendor'] },\n { cliName: 'config', menuGroup: null, mcpTool: null, mcpExposed: false, groupPath: ['config'] },\n // Internal shell-startup trigger; hidden from the menu and never an MCP tool\n // (it can't apply env to a shell \u2014 only the zsh integration sources the file).\n { cliName: 'env-autoload', menuGroup: null, mcpTool: null, mcpExposed: false, groupPath: ['env-autoload'] },\n // The MCP boundary auto-confirms every tool, so an agent-triggered unattended global package install\n // must never be reachable there. menuGroup null keeps it off the no-arg picker too \u2014 updating the CLI\n // is a deliberate act, not something to land on by arrowing through a menu.\n { cliName: 'self-update', menuGroup: null, mcpTool: null, mcpExposed: false, groupPath: ['self-update'] },\n // Launcher for the MCP server itself; it blocks on a stdio transport, so it is neither a one-shot menu\n // command nor expressible as a request/response tool.\n { cliName: 'mcp', menuGroup: null, mcpTool: null, mcpExposed: false, groupPath: ['mcp'] },\n\n // --- vendor subcommands (not top-level menu items; MCP tools where applicable) ---\n {\n cliName: 'vendor-manifest',\n menuGroup: null,\n mcpTool: vendorManifestMcpTool,\n mcpExposed: false,\n groupPath: ['vendor', 'manifest'],\n },\n {\n cliName: 'vendor-sync',\n menuGroup: null,\n mcpTool: vendorSyncMcpTool,\n mcpExposed: false,\n groupPath: ['vendor', 'sync'],\n },\n]\n\n/** The MCP tools to register: catalog entries that are exposed and carry a tool. */\nexport const getExposedMcpTools = (): CatalogMcpTool[] => {\n return commandCatalog.flatMap((entry) => {\n return entry.mcpExposed && entry.mcpTool ? [entry.mcpTool] : []\n })\n}\n\n/**\n * Catalog entries for a menu group, in catalog (display) order.\n *\n * Yields ENTRIES, not names: every menu surface addresses a command by its {@link\n * CommandCatalogEntry.groupPath} (`['release','create']`), which is both what it renders and what it\n * spawns. `cliName` is an id \u2014 the MCP tool name, which cannot hold a space \u2014 and is not a CLI surface.\n *\n * @example\n * getMenuGroupEntries('vendor').map((entry) => entry.groupPath.join(' ')) // => ['vendor check', \u2026]\n */\nexport const getMenuGroupEntries = (group: MenuGroup): CommandCatalogEntry[] => {\n return commandCatalog.filter((entry) => {\n return entry.menuGroup === group\n })\n}\n\n/**\n * True when the invoked command runs until the user stops it (see {@link CommandCatalogEntry.longRunning}).\n * Keyed by the SPACE-JOINED Commander path (`'dev'`, `'vendor check'`) so it matches what `commandPath()`\n * hands the `postAction` hook, not the flat alias.\n *\n * @example\n * isLongRunningCommand('dev') // => true (no verdict to report; exits 128+signo)\n * isLongRunningCommand('vendor check') // => false\n */\nexport const isLongRunningCommand = (commandPath: string): boolean => {\n return commandCatalog.some((entry) => {\n return entry.longRunning === true && entry.groupPath.join(' ') === commandPath\n })\n}\n"],
|
|
5
|
-
"mappings": "izBAAA,OAAOA,OAAU,YACjB,OAAOC,OAAa,eACpB,OAAS,KAAAC,MAAS,MCFlB,OAAOC,OAAQ,mBASR,IAAMC,GAAa,MAAOC,GAAqC,CACpE,GAAI,CACF,aAAMF,GAAG,OAAOE,CAAM,EAEf,EACT,MAAQ,CACN,MAAO,EACT,CACF,ECjBA,OAAOC,OAAU,YCAjB,OAAOC,OAAQ,mBACf,OAAOC,OAAU,YACjB,OAAS,iBAAAC,OAAqB,WAC9B,OAAS,KAAAC,OAAS,MAQX,IAAMC,GAAsB,sBAWtBC,GAAkB,MAAOC,GAAkD,CACtF,GAAI,CACF,IAAMC,EAAM,MAAMC,GAAG,SAASC,GAAK,KAAKH,EAAY,cAAc,EAAG,OAAO,EAE5E,OAAO,KAAK,MAAMC,CAAG,CACvB,MAAQ,CACN,MAAO,CAAC,CACV,CACF,EAcaG,GAAoB,MAC/BJ,EACAK,EAA2CC,KACT,CAClC,IAAMC,EAAaJ,GAAK,KAAKH,EAAYF,EAAmB,EAE5D,GAAI,CAAE,MAAMU,GAAWD,CAAU,EAC/B,MAAM,IAAI,MAAM,GAAGT,EAAmB,iBAAiBS,CAAU,EAAE,EAMrE,IAAME,EAAO,MAAMP,GAAG,KAAKK,CAAU,EAI/BG,GADY,MAAM,OAFN,GAAGC,GAAcJ,CAAU,EAAE,IAAI,UAAU,OAAOE,EAAK,OAAO,CAAC,KAGtD,QAE3B,GAAIC,IAAc,OAChB,MAAM,IAAI,MAAM,GAAGZ,EAAmB,OAAOS,CAAU,wBAAwB,EAGjF,IAAMK,EAAiB,OAAOF,GAAc,WAAa,MAAOA,EAA4B,EAAIA,EAE1FG,EAASC,GAAoB,UAAUF,CAAc,EAE3D,GAAI,CAACC,EAAO,QACV,MAAM,IAAI,MAAM,WAAWf,EAAmB,OAAOS,CAAU,KAAKQ,GAAE,cAAcF,EAAO,KAAK,CAAC,EAAE,EAGrG,OAAOG,GAAqBH,EAAO,KAAMR,CAAQ,CACnD,EC5EA,OAAOY,OAAQ,mBACf,OAAOC,OAAU,YACjB,OAAOC,OAAU,OAIjB,IAAMC,GAAiB,sBAMjBC,GAAgB,MAAOC,IACX,MAAMC,GAAG,QAAQD,EAAK,CAAE,cAAe,EAAK,CAAC,EAAE,MAAM,IAC5D,CAAC,CACT,GAGE,OAAQE,GACAA,EAAM,YAAY,CAC1B,EACA,IAAKA,GACGC,GAAK,KAAKH,EAAKE,EAAM,IAAI,CACjC,EAOCE,GAAgB,MAAOC,EAAgBC,IAAuC,CAClF,IAAMC,EAAiB,CAAC,EAExB,QAAWP,KAAOK,EAAM,CACtB,GAAIC,IAAY,IAAK,CACnBC,EAAK,KAAK,GAAI,MAAMR,GAAcC,CAAG,CAAE,EAEvC,QACF,CAEA,IAAMQ,EAAYL,GAAK,KAAKH,EAAKM,CAAO,EAEpC,MAAMG,GAAWD,CAAS,GAC5BD,EAAK,KAAKC,CAAS,CAEvB,CAEA,OAAOD,CACT,EAMMG,GAAa,MAAOC,EAAqBC,IAAuC,CACpF,IAAIP,EAAO,CAACM,CAAW,EAEvB,QAAWL,KAAWM,EAAQ,MAAM,GAAG,EACrCP,EAAO,MAAMD,GAAcC,EAAMC,CAAO,EAG1C,OAAOD,CACT,EAcaQ,GAAmB,MAAOF,GAA2C,CAChF,IAAMG,EAAM,MAAMb,GAAG,SAASE,GAAK,KAAKQ,EAAab,EAAc,EAAG,OAAO,EAGvEiB,IAFUC,GAAK,MAAMF,CAAG,GAAK,CAAC,GAEX,UAAY,CAAC,GAAG,OAAQF,GACxC,CAACA,EAAQ,WAAW,GAAG,GAAK,CAACA,EAAQ,WAAW,QAAQ,CAChE,EAEKK,EAAQ,IAAI,IAElB,QAAWL,KAAWG,EAAU,CAC9B,IAAMV,EAAO,MAAMK,GAAWC,EAAaC,CAAO,EAElD,QAAWZ,KAAOK,EACZ,MAAMI,GAAWN,GAAK,KAAKH,EAAK,cAAc,CAAC,GACjDiB,EAAM,IAAIjB,CAAG,CAGnB,CAEA,MAAO,CAAC,GAAGiB,CAAK,EAAE,KAAK,CACzB,ECrFO,IAAMC,GAAc,MACzBC,EACAC,EAA2CC,KAC8B,CACzE,GAAI,CACF,IAAMC,EAAQ,MAAMC,GAAkBJ,EAAYC,CAAQ,EAE1D,MAAO,CACL,MAAO,CAAE,KAAMI,GAAqB,OAAQ,OAAQ,QAAS,mBAAoB,EACjF,MAAAF,CACF,CACF,OAASG,EAAK,CACZ,MAAO,CACL,MAAO,CAAE,KAAMD,GAAqB,OAAQ,OAAQ,QAAUC,EAAc,OAAQ,EACpF,MAAO,IACT,CACF,CACF,EC7BA,OAAOC,OAAQ,mBACf,OAAOC,OAAU,YASV,IAAMC,GAAa,MAAOC,EAAoBC,IAC5C,QAAQ,IACbA,EAAc,IAAI,MAAOC,GAAS,CAChC,IAAMC,EAAO,MAAMN,GAAG,KAAKC,GAAK,KAAKE,EAAYE,CAAI,CAAC,EAAE,MAAM,IACrD,IACR,EAED,OAAKC,EAIAA,EAAK,OAAO,EAIV,CAAE,KAAM,QAAQD,CAAI,GAAI,OAAQ,OAAiB,QAAS,QAAS,EAHjE,CAAE,KAAM,QAAQA,CAAI,GAAI,OAAQ,OAAiB,QAAS,eAAeA,CAAI,sBAAuB,EAJpG,CAAE,KAAM,QAAQA,CAAI,GAAI,OAAQ,OAAiB,QAAS,iBAAiBA,CAAI,EAAG,CAQ7F,CAAC,CACH,ECnBK,IAAME,GAAe,CAACC,EAAiCC,IACrDA,EAAgB,IAAKC,GAAW,CACrC,IAAMC,EAAQH,EAAQE,CAAM,EAE5B,OAAI,OAAOC,GAAU,SACZ,CAAE,KAAM,UAAUD,CAAM,GAAI,OAAQ,OAAQ,QAAS,YAAYA,CAAM,2BAA4B,EAGxGC,EAAM,KAAK,EAAE,SAAW,EACnB,CAAE,KAAM,UAAUD,CAAM,GAAI,OAAQ,OAAQ,QAAS,IAAIA,CAAM,oCAAqC,EAGtG,CAAE,KAAM,UAAUA,CAAM,GAAI,OAAQ,OAAQ,QAAS,SAAU,CACxE,CAAC,ECrBH,OAAOE,OAAQ,mBACf,OAAOC,OAAU,YAIjB,IAAMC,GAAa,aAWNC,GAAa,MAAOC,EAAoBC,IAAqD,CACxG,GAAIA,EAAc,SAAW,EAC3B,MAAO,CAAC,EAGV,IAAIC,EAEJ,GAAI,CACF,IAAMC,EAAM,MAAMP,GAAG,SAASC,GAAK,KAAKG,EAAYF,EAAU,EAAG,OAAO,EAExEI,EAAS,KAAK,MAAMC,CAAG,CACzB,OAASC,EAAK,CACZ,MAAO,CAAC,CAAE,KAAMN,GAAY,OAAQ,OAAQ,QAAS,qBAAqBA,EAAU,KAAMM,EAAc,OAAO,EAAG,CAAC,CACrH,CAEA,IAAMC,EAAQH,EAAO,MAErB,OAAIG,IAAU,MAAQ,OAAOA,GAAU,SAC9B,CAAC,CAAE,KAAMP,GAAY,OAAQ,OAAQ,QAAS,gCAAgCA,EAAU,EAAG,CAAC,EAG9FG,EAAc,IAAKK,GAAS,CACjC,IAAMC,EAAUD,KAAQD,GAAS,MAAMC,CAAI,KAAMD,EAEjD,MAAO,CACL,KAAM,SAASC,CAAI,GACnB,OAAQC,EAAU,OAAS,OAC3B,QAASA,EAAU,UAAY,uBAAuBD,CAAI,QAAQR,EAAU,EAC9E,CACF,CAAC,CACH,ENpBO,IAAMU,GAAkB,MAC7BC,EACAC,EAA2CC,KACN,CACrC,IAAMC,EAAU,MAAMC,GAAgBJ,CAAU,EAC1CK,EAAcF,EAAQ,MAAQG,GAAK,SAASN,CAAU,EAEtD,CAAE,MAAOO,EAAa,MAAAC,CAAM,EAAI,MAAMC,GAAYT,EAAYC,CAAQ,EACtES,EAAyB,CAACH,CAAW,EAEvCC,IACFE,EAAO,KAAK,GAAGC,GAAaR,EAAQ,SAAW,CAAC,EAAGK,EAAM,eAAe,CAAC,EACzEE,EAAO,KAAK,GAAI,MAAME,GAAWZ,EAAYQ,EAAM,aAAa,CAAE,EAClEE,EAAO,KAAK,GAAI,MAAMG,GAAWb,EAAYQ,EAAM,UAAU,CAAE,GAGjE,IAAMM,EAASJ,EAAO,MAAOK,GACpBA,EAAM,SAAW,MACzB,EAED,MAAO,CAAE,WAAAf,EAAY,YAAAK,EAAa,OAAAK,EAAQ,OAAAI,CAAO,CACnD,EOVO,IAAME,EAAeC,GACnB,CAAC,CAAE,KAAM,OAAQ,KAAAA,CAAK,CAAC,EAuBnBC,EACXC,GAEOA,ECvDT,OAAOC,OAAU,YA0BjB,IAAMC,GAAwB,CAACC,EAAqBC,IAA0C,CAC5F,IAAMC,EAAO,IAAI,IAEjB,QAAWC,KAAU,OAAO,OAAOH,CAAO,EACxC,QAAWI,KAAO,OAAO,KAAKC,GAAcF,EAAQF,CAAU,EAAE,KAAK,EACnEC,EAAK,IAAIE,CAAG,EAIhB,MAAO,CAAC,GAAGF,CAAI,CACjB,EAQMI,GAAsB,MAAOC,EAAcL,IAA4C,CAC3F,IAAMM,EAAsD,CAAC,EACvDC,EAA0B,CAAC,EAEjC,aAAM,QAAQ,IACZP,EAAK,IAAI,MAAOE,GAAQ,CACtB,GAAI,CAEF,IAAMM,GADM,MAAMC,GAAQC,GAAK,KAAKL,EAAM,OAAQH,EAAK,IAAI,CAAC,IACxC,OAAO,OAE3B,GAAI,CAACM,EAAQ,OAEbF,EAAYJ,CAAG,EAAI,OAAO,YACxB,OAAO,QAAQM,CAAM,EAAE,IAAI,CAAC,CAACG,EAAOC,CAAM,IACjC,CAACD,EAAOC,EAAO,WAAW,CAClC,CACH,CACF,OAASC,EAAO,CACdN,EAAW,KAAK,CAAE,IAAAL,EAAK,QAASW,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,CAAE,CAAC,CAC1F,CACF,CAAC,CACH,EAEO,CACL,SAAU,CAACX,EAAKS,IACPL,EAAYJ,CAAG,IAAIS,CAAK,EAEjC,WAAAJ,CACF,CACF,EAGMO,GAAe,CAACC,EAAgDR,IAC7D,CACL,GAAGA,EAAW,IAAKM,IACV,CACL,KAAM,4BAA4BA,EAAM,GAAG,GAC3C,OAAQ,OACR,QAAS,uBAAuBA,EAAM,GAAG,oEAAoEA,EAAM,OAAO,EAC5H,EACD,EACD,GAAGE,EAAO,IAAKC,IACN,CAAE,KAAM,qBAAqBA,EAAM,MAAM,GAAI,OAAQ,OAAQ,QAASA,EAAM,OAAQ,EAC5F,CACH,EAcWC,GAAkB,MAAOZ,GAA0D,CAC9F,IAAMP,GAAW,MAAMoB,EAAkB,GAAG,kBAE5C,GAAI,CAACpB,GAAW,OAAO,KAAKA,CAAO,EAAE,SAAW,EAC9C,OAAO,KAKT,IAAMqB,EAAYC,GAAmBtB,CAAO,EAE5C,GAAIqB,EAAU,OAAS,EAAG,CACxB,IAAME,EAASF,EAAU,IAAKH,IACrB,CAAE,KAAM,qBAAqBA,EAAM,MAAM,GAAI,OAAQ,OAAQ,QAASA,EAAM,OAAQ,EAC5F,EAED,MAAO,CAAE,WAAYX,EAAM,YAAa,oBAAqB,OAAAgB,EAAQ,OAAQ,EAAM,CACrF,CAEA,IAAMC,EAAUC,GAAgBlB,CAAI,EAC9BmB,EAASC,GAAepB,CAAI,EAC5BN,EAA8B,CAClC,IAAKuB,EAAQ,IAAKI,GACTA,EAAE,IACV,EACD,GAAIF,EAAO,IAAKE,GACPA,EAAE,IACV,CACH,EAEM,CAAE,SAAAC,EAAU,WAAApB,CAAW,EAAI,MAAMH,GAAoBC,EAAMR,GAAsBC,EAASC,CAAU,CAAC,EAErG6B,EAA0B,CAC9B,WAAA7B,EACA,YAAa,OAAO,YAClBuB,EAAQ,IAAKI,GACJ,CAACA,EAAE,KAAMA,EAAE,WAAW,CAC9B,CACH,EACA,SAAAC,CACF,EAEME,EAAaf,GAAagB,GAAoBhC,EAAS8B,CAAG,EAAGrB,CAAU,EAEvEc,EACJQ,EAAW,SAAW,EAClB,CACE,CACE,KAAM,mCACN,OAAQ,OACR,QAAS,sEACX,CACF,EACAA,EAEN,MAAO,CAAE,WAAYxB,EAAM,YAAa,oBAAqB,OAAAgB,EAAQ,OAAQQ,EAAW,SAAW,CAAE,CACvG,EV/HA,IAAME,GAAkB,MAAOC,GAAmC,CAChE,IAAIC,EAAUC,GAAK,QAAQF,CAAK,EAEhC,KAAOC,IAAYC,GAAK,QAAQD,CAAO,GAAG,CACxC,GAAI,MAAME,GAAWD,GAAK,KAAKD,EAAS,cAAc,CAAC,EACrD,OAAOA,EAGTA,EAAUC,GAAK,QAAQD,CAAO,CAChC,CAEA,GAAI,MAAME,GAAWD,GAAK,KAAKD,EAAS,cAAc,CAAC,EACrD,OAAOA,EAGT,MAAM,IAAI,MAAM,qCAAqCD,CAAK,EAAE,CAC9D,EAOMI,GAAiB,MAAOC,GACxBA,EAAQ,KACH,CAAC,CAAE,IAAK,MAAMC,EAAe,EAAG,SAAUC,EAAmB,CAAC,EAGnEF,EAAQ,KACG,MAAMG,GAAiB,MAAMF,EAAe,CAAC,GAE9C,IAAKG,IACR,CAAE,IAAAA,CAAI,EACd,EAGI,CAAC,CAAE,IAAK,MAAMV,GAAgBM,EAAQ,KAAOK,GAAQ,IAAI,CAAC,CAAE,CAAC,EAMhEC,GAAaC,GAA0C,CAC3D,IAAMC,EAASD,EAAO,OAAS,OAAS,OAExCE,EAAO,KAAK;AAAA,EAAKF,EAAO,WAAW,WAAMC,CAAM,EAAE,EAEjD,QAAWE,KAASH,EAAO,OAAQ,CACjC,IAAMI,EAAOD,EAAM,SAAW,OAAS,SAAW,SAElDD,EAAO,KAAK,KAAKE,CAAI,IAAID,EAAM,IAAI,KAAKA,EAAM,OAAO,EAAE,CACzD,CACF,EAgBaE,GAAQ,MAAOZ,EAAwB,CAAC,IAAM,CACzD,IAAMa,EAAU,MAAMd,GAAeC,CAAO,EAEtCc,EAAqC,CAAC,EAE5C,QAAWC,KAAUF,EACnBC,EAAQ,KAAK,MAAME,GAAgBD,EAAO,IAAKA,EAAO,QAAQ,CAAC,EAOjE,GAAIf,EAAQ,KAAM,CAChB,IAAMiB,EAAe,MAAMC,GAAgBL,EAAQ,CAAC,EAAG,GAAG,EAEtDI,GACFH,EAAQ,KAAKG,CAAY,CAE7B,CAEA,QAAWV,KAAUO,EACnBR,GAAUC,CAAM,EAGlB,IAAMY,EAAYL,EAAQ,MAAOP,GACxBA,EAAO,MACf,EAEDE,EAAO,KAAK;AAAA,EAAKU,EAAY,mBAAgB,qBAAgB,KAAKL,EAAQ,MAAM,WAAW,EAE3F,IAAMM,EAAoB,CACxB,UAAAD,EACA,SAAUL,EAAQ,IAAKP,IACd,CACL,KAAMA,EAAO,YACb,OAAQA,EAAO,OACf,OAAQA,EAAO,MACjB,EACD,CACH,EAEA,MAAO,CACL,QAASc,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAEME,GAAmB,CACvB,IAAKC,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,0CAA0C,EAC/E,KAAMA,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,0DAA0D,CAClG,EAEMC,GAAoB,CACxB,UAAWD,EAAE,QAAQ,EAAE,SAAS,iDAAiD,EACjF,SAAUA,EACP,MACCA,EAAE,OAAO,CACP,KAAMA,EAAE,OAAO,EACf,OAAQA,EAAE,QAAQ,EAClB,OAAQA,EAAE,MACRA,EAAE,OAAO,CACP,KAAMA,EAAE,OAAO,EACf,OAAQA,EAAE,KAAK,CAAC,OAAQ,MAAM,CAAC,EAC/B,QAASA,EAAE,OAAO,CACpB,CAAC,CACH,CACF,CAAC,CACH,EACC,SAAS,2BAA2B,CACzC,EAGaE,GAAeC,EAAc,CACxC,KAAM,QACN,YACE,wQACF,YAAaJ,GACb,aAAcE,GACd,QAAUG,GACDf,GAAM,CAAE,IAAKe,EAAO,IAAK,KAAMA,EAAO,IAAK,CAAC,CAEvD,CAAC,EW9LD,OAAOC,MAAQ,UACf,OAAOC,OAAQ,UACf,OAAOC,OAAU,YACjB,OAAS,KAAAC,OAAS,MAClB,OAAS,KAAAC,OAAS,KCJlB,OAAOC,MAAQ,UACf,OAAOC,OAAU,YCyCV,IAAMC,GAAkB,CAACC,EAAiBC,EAAqBC,IAA+B,CACnG,IAAMC,EAAWH,EAAQ,QAAQC,CAAW,EACtCG,EAASJ,EAAQ,QAAQE,CAAS,EAExC,OAAOC,IAAa,IAAMC,IAAW,IAAMA,GAAUD,CACvD,EAaaE,GAAqB,CAACL,EAAiBC,EAAqBC,IAAqC,CAC5G,IAAMC,EAAWH,EAAQ,QAAQC,CAAW,EACtCG,EAASJ,EAAQ,QAAQE,CAAS,EAExC,GAAIC,IAAa,IAAMC,IAAW,IAAMA,EAASD,EAAU,OAAO,KAGlE,IAAMG,EAASN,EAAQ,MAAM,EAAGG,CAAQ,EAAE,QAAQ,OAAQ,EAAE,EACtDI,EAAQP,EAAQ,MAAMI,EAASF,EAAU,MAAM,EAAE,QAAQ,OAAQ,EAAE,EAEzE,OAAOI,GAAUC,EAAQ;AAAA,EAAKA,CAAK,GAAK,GAC1C,EA2BO,IAAMC,GAAoB,CAACC,EAAqBC,EAAcC,IAC5D,GAAGF,CAAW;AAAA,EAAKC,CAAI;AAAA,EAAKC,CAAS,GAqBjCC,GAAqB,CAAC,CACjC,QAAAC,EACA,KAAAH,EACA,YAAAD,EACA,UAAAE,EACA,UAAAG,EAAY,kBACd,IAAsC,CACpC,IAAMC,EAAQP,GAAkBC,EAAaC,EAAMC,CAAS,EACtDK,EAAUC,GAAgBJ,EAASJ,EAAaE,CAAS,EAE/D,GAAIG,IAAc,oBAAsBE,EAAS,CAC/C,IAAME,EAAWL,EAAQ,QAAQJ,CAAW,EACtCU,EAASN,EAAQ,QAAQF,CAAS,EAAIA,EAAU,OAEtD,OAAOE,EAAQ,MAAM,EAAGK,CAAQ,EAAIH,EAAQF,EAAQ,MAAMM,CAAM,CAClE,CAMA,IAAMC,GAFWJ,EAAWK,GAAmBR,EAASJ,EAAaE,CAAS,GAAKE,EAAWA,GAExE,QAAQ,OAAQ,EAAE,EAExC,OAAOO,EAAK,OAAS,EAAI,GAAGA,CAAI;AAAA,EAAKL,CAAK;AAAA,EAAO,GAAGA,CAAK;AAAA,CAC3D,EDtIO,IAAMO,GAAsB,2BACtBC,GAAoB,yBAE3BC,GAAwB,0BAOjBC,GAAsB,kCACtBC,GAAoB,gCAE3BC,GAAc,YACdC,GAAc,YAkBdC,GAAoBC,GACjB,GAAGN,EAAqB,GAAGM,CAAO,OAQrCC,GAAmBD,GAChB,CACLD,GAAiBC,CAAO,EACxB,GACA,cACA,GACA,+FACA,oGACA,GACA,6CACA,GACA,+KACA,wJACA,6KACA,kFACA,0EACA,qFACA,iGACA,GACA,iBACA,GACA,sLACA,oGACF,EAAE,KAAK;AAAA,CAAI,EAGPE,GAAoBC,GAA2B,CACnD,GAAIC,EAAG,WAAWD,CAAQ,GAAKC,EAAG,UAAUD,CAAQ,EAAE,eAAe,EACnE,MAAM,IAAI,MAAM,qBAAqBA,CAAQ,uCAAuC,CAExF,EAGME,GAAcF,GAA2B,CAC7C,IAAMG,EAAQ,IAAI,KAAK,EAAE,YAAY,EAAE,QAAQ,QAAS,GAAG,EAE3DF,EAAG,aAAaD,EAAU,GAAGA,CAAQ,WAAWG,CAAK,EAAE,CACzD,EAOMC,GAAe,CAACJ,EAAkBK,IAAqC,CAC3EN,GAAiBC,CAAQ,EAEzB,IAAMM,EAAUL,EAAG,WAAWD,CAAQ,EAGtC,OAFiBM,EAAUL,EAAG,aAAaD,EAAU,OAAO,EAAI,QAE/CK,EAAoB,aAEjCC,GAASJ,GAAWF,CAAQ,EAEhCC,EAAG,UAAUM,GAAK,QAAQP,CAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,EACxDC,EAAG,cAAcD,EAAUK,EAAa,OAAO,EAExCC,EAAU,UAAY,UAC/B,EAEME,GAAS,CAACR,EAAkBS,IACzBR,EAAG,WAAWD,CAAQ,EAAIC,EAAG,aAAaD,EAAU,OAAO,EAAIS,EAGlEC,GAAqB,CAACV,EAAkBW,EAAeC,IAAsB,CACjF,IAAMC,EAAUZ,EAAG,aAAaD,EAAU,OAAO,EAEjD,GAAI,CAACc,GAAgBD,EAASF,EAAOC,CAAG,EACtC,MAAM,IAAI,MAAM,4DAA4DZ,CAAQ,EAAE,CAE1F,EAUMe,GAA2BC,GAAoC,CACnE,GAAI,CAACf,EAAG,WAAWe,CAAU,EAAG,MAAO,YAEvCjB,GAAiBiB,CAAU,EAE3B,IAAMH,EAAUZ,EAAG,aAAae,EAAY,OAAO,EAC7CC,EAAWC,GAAmBL,EAASxB,GAAqBC,EAAiB,EAEnF,OAAI2B,IAAa,KAAa,YAE1BA,EAAS,KAAK,IAAM,IACtBf,GAAWc,CAAU,EACrBf,EAAG,OAAOe,CAAU,EAEb,WAGFZ,GAAaY,EAAYC,CAAQ,CAC1C,EAaaE,GAAkB,SAA4C,CACzE,IAAIC,EAEJ,GAAI,CACFA,GAAkB,MAAMC,EAAuB,GAAG,IACpD,MAAQ,CACN,OAAAC,EAAO,KAAK,qEAAgE,EAErE,CAAE,QAAS,GAAM,KAAM,KAAM,QAAS,CAAC,CAAE,CAClD,CAEA,GAAI,CAACrB,EAAG,WAAWmB,CAAc,EAC/B,OAAAE,EAAO,KAAK,2EAAsE,EAE3E,CAAE,QAAS,GAAM,KAAM,KAAM,QAAS,CAAC,CAAE,EAGlD,IAAMC,EAAOhB,GAAK,QAAQa,CAAc,EAClCvB,EAAU2B,GAAY,QACtBC,EAAO3B,GAAgBD,CAAO,EAE9BmB,EAAaT,GAAK,KAAKgB,EAAM7B,EAAW,EACxCgC,EAAanB,GAAK,KAAKgB,EAAM5B,EAAW,EAK1CgC,EAAgBnB,GAAOkB,EAAY,EAAE,EAEzCC,EAAgBT,GAAmBS,EAAenC,GAAqBC,EAAiB,GAAKkC,EAC7FA,EAAgBC,GAAmB,CACjC,QAASD,EACT,KAAAF,EACA,YAAapC,GACb,UAAWC,GACX,UAAW,kBACb,CAAC,EAGD,IAAMuC,EAA4B,CAChC,CAAE,KAAMH,EAAY,OAAQtB,GAAasB,EAAYC,CAAa,CAAE,EACpE,CAAE,KAAMX,EAAY,OAAQD,GAAwBC,CAAU,CAAE,CAClE,EAEAN,GAAmBgB,EAAYrC,GAAqBC,EAAiB,EAErE,QAAWwC,KAAQD,EAEbC,EAAK,SAAW,aAAeA,EAAK,OAASJ,GAEjDJ,EAAO,KAAK,KAAKQ,EAAK,OAAO,OAAO,CAAC,CAAC,IAAIvB,GAAK,SAASgB,EAAMO,EAAK,IAAI,CAAC,EAAE,EAG5E,OAAAR,EAAO,KAAK,6CAA6CzB,CAAO,GAAG,EAE5D,CAAE,QAAS,GAAO,KAAA0B,EAAM,QAAAM,CAAQ,CACzC,EE1NA,OAAOE,OAAQ,UACf,OAAOC,OAAQ,UACf,OAAOC,OAAU,YACjB,OAAOC,OAAa,eCGpB,OAAOC,OAAQ,mBACf,OAAOC,OAAU,YACjB,OAAOC,OAAa,eCkBpB,IAAMC,GAAkB;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kCAuFXC,GAAyB,IAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBPD,EAAe;AAAA;AAAA,EAeJE,GAA2BC,GAC/B,kCAAkCA,CAAW,iCAA4BA,CAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkB3FH,EAAe;AAAA;AAAA,EAcJI,GAAqB,IACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ECzLT,OAAOC,OAAQ,mBACf,OAAOC,OAAQ,UAiBR,IAAMC,EAAa,MAAOC,GAAuC,CACtE,GAAI,CACF,aAAMH,GAAG,OAAOG,CAAQ,EAEjB,EACT,MAAQ,CACN,MAAO,EACT,CACF,EAWaC,EAAWD,GAA6B,CACnD,IAAME,EAAOJ,GAAG,QAAQ,EAExB,OAAOE,EAAS,WAAWE,CAAI,EAAI,IAAIF,EAAS,MAAME,EAAK,MAAM,CAAC,GAAKF,CACzE,EFdA,IAAIG,GAAS,GAUPC,GAAsBC,GACnBA,EAAS,QAAQ,UAAW,gBAAgB,EAU/CC,GAAiB,MAAOC,GAA6C,CACzE,GAAI,CACF,OAAO,MAAMC,GAAG,SAASD,EAAU,OAAO,CAC5C,MAAQ,CACN,OAAO,IACT,CACF,EAuBaE,GAAwB,MAAOC,GAA+D,CACzG,IAAMC,EAAaD,EAAM,YACnBE,EAAcR,GAAmBO,CAAU,EAEjD,MAAMH,GAAG,MAAMK,GAAK,QAAQF,CAAU,EAAG,CAAE,UAAW,EAAK,CAAC,EAE5D,IAAMG,EAAgB,CAAE,MAAMC,EAAWJ,CAAU,EAE/CG,GAGF,MAAMN,GAAG,UAAUG,EAAY;AAAA,EAAa,OAAO,EAGrD,IAAMK,EAAUC,GAAwBP,EAAM,WAAW,EACnDQ,EAAoB,MAAMZ,GAAeM,CAAW,IAAOI,EAEjE,OAAIE,GACF,MAAMV,GAAG,UAAUI,EAAaI,EAAS,OAAO,EAG9CF,GAKFK,GAAuB,EAGlB,CAAE,WAAAR,EAAY,YAAAC,EAAa,cAAAE,EAAe,iBAAAI,CAAiB,CACpE,EAWaE,GAAsBC,GAC1B,WAAWC,EAAQD,EAAO,UAAU,CAAC,eAAUC,EAAQD,EAAO,WAAW,CAAC,gCAuBtEE,GAA0B,SAA2B,CAKhE,GAJIC,GAAQ,IAAI,mBAIZrB,GACF,OAGFA,GAAS,GAET,IAAIO,EAEJ,GAAI,CACFA,EAAQ,MAAMe,EAAuB,CACvC,MAAQ,CAEN,MACF,CAEA,GAAM,MAAMV,EAAWL,EAAM,IAAI,EAIjC,GAAI,CACF,IAAMW,EAAS,MAAMZ,GAAsBC,CAAK,EAE5CW,EAAO,cACTK,EAAO,KAAKN,GAAmBC,CAAM,CAAC,EAC7BA,EAAO,kBAChBK,EAAO,MAAM,aAAaJ,EAAQD,EAAO,WAAW,CAAC,+BAA+B,CAExF,OAASM,EAAK,CACZD,EAAO,MAAM,CAAE,IAAAC,EAAK,IAAK,mBAAmBL,EAAQZ,EAAM,WAAW,CAAC,6BAA8B,CAAC,CACvG,CACF,EGjLA,OAAOkB,MAAQ,mBACf,OAAOC,OAAU,YACjB,OAAS,iBAAAC,OAAqB,WAC9B,OAAOC,OAAU,OACjB,OAAS,KAAAC,OAAS,MCJlB,OAAOC,OAAQ,mBACf,OAAOC,OAAQ,UACf,OAAOC,OAAU,YACjB,OAAS,KAAAC,OAAS,MCHlB,OAAS,KAAAC,OAAS,MAgBX,IAAMC,GAAsB,cAStBC,GAAsBF,GAChC,OAAO,CAEN,aAAcA,GAAE,OAAO,EAAE,IAAI,CAAC,EAE9B,QAASA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CACpC,CAAC,EACA,OAAO,EDdH,IAAMG,GAAuB,IAC3BC,GAAK,KAAKC,GAAG,QAAQ,EAAGC,GAAsBC,EAAmB,EAc7DC,GAAeC,GAAsB,CAChD,GAAIA,IAAM,IAAK,OAAOJ,GAAG,QAAQ,EACjC,GAAII,EAAE,WAAW,IAAI,EAAG,OAAOL,GAAK,KAAKC,GAAG,QAAQ,EAAGI,EAAE,MAAM,CAAC,CAAC,EACjE,GAAIL,GAAK,WAAWK,CAAC,EAAG,OAAOA,EAE/B,MAAM,IAAI,MAAM,qDAAqDA,CAAC,EAAE,CAC1E,EAiBaC,GAAoB,SAAoC,CACnE,IAAMC,EAAaR,GAAqB,EAEpCS,EAEJ,GAAI,CACFA,EAAM,MAAMC,GAAG,SAASF,EAAY,OAAO,CAC7C,MAAQ,CACN,MAAM,IAAI,MACR,+BAA+BA,CAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAO3C,CACF,CAEA,IAAIG,EAEJ,GAAI,CACFA,EAAOF,EAAI,KAAK,IAAM,GAAK,CAAC,EAAI,KAAK,MAAMA,CAAG,CAChD,OAASG,EAAK,CACZ,MAAM,IAAI,MAAM,kCAAkCJ,CAAU,KAAMI,EAAc,OAAO,EAAE,CAC3F,CAEA,IAAMC,EAASC,GAAoB,UAAUH,CAAI,EAEjD,GAAI,CAACE,EAAO,QACV,MAAM,IAAI,MAAM,6BAA6BL,CAAU,KAAKO,GAAE,cAAcF,EAAO,KAAK,CAAC,EAAE,EAG7F,OAAOA,EAAO,IAChB,EDzDA,IAAMG,GAAiBC,GACdA,EAAS,QAAQ,UAAW,MAAM,EAgB9BC,GAAsB,SAA2B,CAC5D,IAAIC,EAEJ,GAAI,CACFA,EAAQ,MAAMC,EAAuB,CACvC,MAAQ,CAEN,MACF,CAEA,IAAMC,EAAyB,CAC7B,CAAE,MAAO,iBAAkB,IAAKL,GAAcG,EAAM,IAAI,EAAG,KAAMA,EAAM,KAAM,OAAQG,EAAqB,EAC1G,CACE,MAAO,8BAMP,IAAKC,GAAK,KAAKA,GAAK,QAAQJ,EAAM,UAAU,EAAG,YAAY,EAC3D,KAAMA,EAAM,WACZ,OAAQK,EACV,EACA,CACE,MAAO,yBAAyBL,EAAM,WAAW,kBACjD,IAAKH,GAAcG,EAAM,WAAW,EACpC,KAAMA,EAAM,YACZ,OAAQK,EACV,CACF,EAEIC,EAAW,EAEf,QAAWC,KAASL,EAAQ,CAC1B,GAAM,CAACM,EAAWC,CAAU,EAAI,MAAM,QAAQ,IAAI,CAACC,EAAWH,EAAM,GAAG,EAAGG,EAAWH,EAAM,IAAI,CAAC,CAAC,EAEjG,GAAKC,EAEL,IAAIC,EAAY,CACdE,EAAO,KACL,kBAAaC,EAAQL,EAAM,GAAG,CAAC,WAAMK,EAAQL,EAAM,IAAI,CAAC,kDAC1D,EAEA,QACF,CAKA,GAAI,CACF,IAAMM,EAAM,MAAMC,EAAG,SAASP,EAAM,IAAK,OAAO,EAC1CQ,EAAUC,GAAK,MAAMH,CAAG,GAAK,CAAC,EAC9BI,EAASV,EAAM,OAAO,UAAUQ,CAAM,EAE5C,GAAI,CAACE,EAAO,QAAS,CACnBN,EAAO,KAAK,kBAAaC,EAAQL,EAAM,GAAG,CAAC,2BAAsBW,GAAE,cAAcD,EAAO,KAAK,CAAC,EAAE,EAEhG,QACF,CAEA,MAAMH,EAAG,MAAMV,GAAK,QAAQG,EAAM,IAAI,EAAG,CAAE,UAAW,EAAK,CAAC,EAC5D,MAAMO,EAAG,UAAUP,EAAM,KAAM,GAAG,KAAK,UAAUU,EAAO,KAAM,KAAM,CAAC,CAAC;AAAA,EAAM,OAAO,EACnF,MAAMH,EAAG,GAAGP,EAAM,IAAK,CAAE,MAAO,EAAK,CAAC,EAEtCI,EAAO,KAAK,mBAAcC,EAAQL,EAAM,GAAG,CAAC,WAAMK,EAAQL,EAAM,IAAI,CAAC,EAAE,EACvED,GACF,OAASa,EAAK,CACZR,EAAO,KAAK,kBAAaC,EAAQL,EAAM,GAAG,CAAC,WAAOY,EAAc,OAAO,EAAE,CAC3E,EACF,CAEIb,EAAW,GACbc,GAAyB,CAE7B,EAqBaC,GAAkC,SAA2B,CACxE,IAAIrB,EAEJ,GAAI,CACFA,EAAQ,MAAMC,EAAuB,CACvC,MAAQ,CAEN,MACF,CAEA,IAAMqB,EAAMlB,GAAK,QAAQJ,EAAM,UAAU,EAMnCuB,EAAwC,CAC5C,CAAE,KAAMnB,GAAK,KAAKkB,EAAK,aAAa,EAAG,GAAItB,EAAM,UAAW,EAC5D,CAAE,KAAMI,GAAK,KAAKkB,EAAK,sBAAsB,EAAG,GAAIlB,GAAK,KAAKkB,EAAK,yBAAyB,CAAE,CAChG,EAEIE,EAAU,EAEd,OAAW,CAAE,KAAAC,EAAM,GAAAC,CAAG,IAAKH,EAAO,CAChC,GAAM,CAACI,EAAYC,CAAQ,EAAI,MAAM,QAAQ,IAAI,CAAClB,EAAWe,CAAI,EAAGf,EAAWgB,CAAE,CAAC,CAAC,EAEnF,GAAKC,EAEL,IAAIC,EAAU,CACZjB,EAAO,KAAK,kBAAaC,EAAQa,CAAI,CAAC,WAAMb,EAAQc,CAAE,CAAC,kDAAkD,EAEzG,QACF,CAIA,GAAI,CACF,MAAMZ,EAAG,MAAMV,GAAK,QAAQsB,CAAE,EAAG,CAAE,UAAW,EAAK,CAAC,EACpD,MAAMZ,EAAG,OAAOW,EAAMC,CAAE,EAExBf,EAAO,KAAK,kBAAaC,EAAQa,CAAI,CAAC,WAAMb,EAAQc,CAAE,CAAC,EAAE,EACzDF,GACF,OAASL,EAAK,CACZR,EAAO,KAAK,kBAAaC,EAAQa,CAAI,CAAC,WAAON,EAAc,OAAO,EAAE,CACtE,EACF,CAEIK,EAAU,GACZJ,GAAyB,CAE7B,EAuBaS,GAA6B,SAA2B,CACnE,IAAMC,EAAUC,GAAqB,EAC/BT,EAAMlB,GAAK,QAAQ0B,CAAO,EAC1BE,EAAQ5B,GAAK,KAAKkB,EAAK,kBAAkB,EAEzC,CAACW,EAAWC,CAAS,EAAI,MAAM,QAAQ,IAAI,CAACxB,EAAWsB,CAAK,EAAGtB,EAAWoB,CAAO,CAAC,CAAC,EAEzF,GAAKG,EAEL,IAAIC,EAAW,CACbvB,EAAO,KACL,kBAAaC,EAAQoB,CAAK,CAAC,WAAMpB,EAAQkB,CAAO,CAAC,8DACnD,EAEA,MACF,CAMA,GAAI,CACF,IAAMK,EAAO,MAAMrB,EAAG,KAAKkB,CAAK,EAG1BnB,GADY,MAAM,OADN,GAAGuB,GAAcJ,CAAK,EAAE,IAAI,UAAU,OAAOG,EAAK,OAAO,CAAC,KAEvD,QACfE,EAAc,OAAOxB,GAAQ,WAC7ByB,EAAWD,EAAc,MAAOxB,EAAsB,EAAIA,EAE1DI,EAASsB,GAAoB,UAAUD,CAAQ,EAErD,GAAI,CAACrB,EAAO,QAAS,CACnBN,EAAO,KAAK,kBAAaC,EAAQoB,CAAK,CAAC,mCAA8Bd,GAAE,cAAcD,EAAO,KAAK,CAAC,EAAE,EAEpG,MACF,CAEA,MAAMH,EAAG,MAAMQ,EAAK,CAAE,UAAW,EAAK,CAAC,EACvC,MAAMR,EAAG,UAAUgB,EAAS,GAAG,KAAK,UAAUb,EAAO,KAAM,KAAM,CAAC,CAAC;AAAA,EAAM,OAAO,EAChF,MAAMH,EAAG,GAAGkB,EAAO,CAAE,MAAO,EAAK,CAAC,EAE9BK,EAGF1B,EAAO,KACL,mBAAcC,EAAQoB,CAAK,CAAC,WAAMpB,EAAQkB,CAAO,CAAC,+MAEkBlB,EAAQkB,CAAO,CAAC,2BACtF,EAEAnB,EAAO,KAAK,mBAAcC,EAAQoB,CAAK,CAAC,WAAMpB,EAAQkB,CAAO,CAAC,EAAE,CAEpE,OAASX,EAAK,CACZR,EAAO,KAAK,kBAAaC,EAAQoB,CAAK,CAAC,WAAOb,EAAc,OAAO,EAAE,CACvE,EACF,EAaMqB,GAAsBzB,GAA2F,CACrH,IAAM0B,EAAM1B,EAAO,IAEnB,GAAI0B,IAAQ,MAAQ,OAAOA,GAAQ,SACjC,MAAO,CAAE,QAAS,GAAO,OAAQ1B,CAAO,EAG1C,IAAI2B,EAAU,GAERC,EAAcC,GAA4B,CAC9C,GAAIA,IAAU,MAAQ,OAAOA,GAAU,UAAY,EAAE,WAAYA,GAC/D,OAAOA,EAGT,IAAMC,EAAUD,EAA8B,OAE9C,GAAIC,IAAW,MAAQ,OAAOA,GAAW,UAAY,EAAE,SAAUA,GAC/D,OAAOD,EAGTF,EAAU,GAEV,IAAMI,EAAa,OAAO,YACxB,OAAO,QAAQD,CAAiC,EAAE,OAAO,CAAC,CAACE,CAAG,IACrDA,IAAQ,MAChB,CACH,EAEA,MAAO,CAAE,GAAIH,EAAmC,OAAQE,CAAW,CACrE,EAEME,EAAU,MAAM,QAAQP,CAAG,EAAIA,EAAI,IAAIE,CAAU,EAAIA,EAAWF,CAAG,EAEzE,OAAKC,EAIE,CAAE,QAAS,GAAM,OAAQ,CAAE,GAAG3B,EAAQ,IAAKiC,CAAQ,CAAE,EAHnD,CAAE,QAAS,GAAO,OAAQjC,CAAO,CAI5C,EAeakC,GAA+B,SAA2B,CACrE,IAAIjD,EAEJ,GAAI,CACFA,EAAQ,MAAMC,EAAuB,CACvC,MAAQ,CACN,MACF,CAEA,IAAMiD,EAAY,CAAClD,EAAM,KAAMA,EAAM,WAAYA,EAAM,WAAW,EAE9DmD,EAAa,EAEjB,QAAWrD,KAAYoD,EACrB,GAAM,MAAMxC,EAAWZ,CAAQ,EAE/B,GAAI,CACF,IAAMe,EAAM,MAAMC,EAAG,SAAShB,EAAU,OAAO,EAE/C,GAAIe,EAAI,KAAK,IAAM,GAAI,SAEvB,IAAME,EAAS,KAAK,MAAMF,CAAG,EACvB,CAAE,QAAA6B,EAAS,OAAAzB,CAAO,EAAIuB,GAAmBzB,CAAM,EAErD,GAAI,CAAC2B,EAAS,SAEd,MAAM5B,EAAG,UAAUhB,EAAU,GAAG,KAAK,UAAUmB,EAAQ,KAAM,CAAC,CAAC;AAAA,EAAM,OAAO,EAE5EN,EAAO,KAAK,mCAA8BC,EAAQd,CAAQ,CAAC,0BAA0B,EACrFqD,GACF,OAAShC,EAAK,CACZR,EAAO,KAAK,8BAAyBC,EAAQd,CAAQ,CAAC,WAAOqB,EAAc,OAAO,EAAE,CACtF,CAGEgC,EAAa,GACf/B,GAAyB,CAE7B,EJtWO,IAAMgC,GAAe,0BACfC,GAAa,wBAEpBC,GAAgD,CAAC,CAAC,qBAAsB,uBAAuB,CAAC,EAChGC,GAAgB,8BAkBTC,GAAO,SAA2B,CAC7C,IAAMC,EAAYC,GAAK,KAAKC,GAAG,QAAQ,EAAG,QAAQ,EAK5CC,EAAWC,GAAG,WAAWJ,CAAS,EAAIK,GAAoBD,GAAG,aAAaJ,EAAW,OAAO,CAAC,EAAI,GAEjGM,EAAUC,GAAmB,CACjC,QAASJ,EACT,KAAMK,GAAe,EACrB,YAAab,GACb,UAAWC,GACX,UAAW,YACb,CAAC,EAEDQ,GAAG,cAAcJ,EAAWM,CAAO,EAEnCG,EAAO,KAAK,sCAAsCT,CAAS,EAAE,EAI7D,MAAMU,GAAoB,EAI1B,MAAMC,GAA6B,EAKnC,MAAMC,GAAgC,EAKtC,MAAMC,GAA2B,EAEjCC,GAAqB,EAIrB,MAAMC,GAAgB,EAGtB,MAAMC,GAAwB,EAK9B,IAAMC,EAAQC,GAAQ,IAAI,OAAS,GAE9BD,EAAM,SAAS,KAAK,GACvBR,EAAO,KACL,4BAA4BQ,GAAS,OAAO,2HAC9C,EAGFR,EAAO,KAAK,2DAA2D,CACzE,EAkBaK,GAAuB,IAAY,CAC9C,IAAMK,EAAgBlB,GAAK,KAAKC,GAAG,QAAQ,EAAG,YAAY,EACpDkB,EAAiBnB,GAAK,KAAKkB,EAAe,gBAAgB,EAShE,GAPAf,GAAG,UAAUe,EAAe,CAAE,UAAW,EAAK,CAAC,EAI/Cf,GAAG,cAAcH,GAAK,KAAKkB,EAAe,yBAAyB,EAAGE,GAAuB,EAAG,OAAO,EACvGjB,GAAG,cAAcH,GAAK,KAAKkB,EAAe,sBAAsB,EAAGG,GAAmB,EAAG,OAAO,EAE5FlB,GAAG,WAAWgB,CAAc,EAAG,CACjCX,EAAO,KAAK,yCAAyCW,CAAc,iCAAiC,EAEpG,MACF,CAEAhB,GAAG,cAAcgB,EAAgB;AAAA,EAAa,OAAO,EAErDX,EAAO,KAAK,+BAA+BW,CAAc,uDAAuD,CAClH,EA0BMJ,GAA0B,SAA2B,CACzD,GAAI,CAAAE,GAAQ,IAAI,kBAEhB,GAAI,CACF,IAAMK,EAAQ,MAAMC,EAAuB,EAG3C,GAAI,CAACpB,GAAG,WAAWmB,EAAM,IAAI,EAAG,OAEhC,IAAME,EAAS,MAAMC,GAAsBH,CAAK,EAE5CE,EAAO,eACThB,EAAO,KAAKkB,GAAmBF,CAAM,CAAC,CAE1C,OAASG,EAAK,CACZnB,EAAO,MAAM,CAAE,IAAAmB,EAAK,IAAK,iDAAkD,CAAC,CAC9E,CACF,EAEMC,GAAeC,GAEjBA,EAAK,WAAW,GAAG,GACnBA,EAAK,WAAW,QAAQ,GACxBA,EAAK,WAAW,UAAU,GAC1BA,EAAK,WAAW,WAAW,GAC3BA,EAAK,WAAW,YAAY,GAC5BA,EAAK,WAAW,KAAK,GACrBA,EAAK,WAAW,4BAA4B,GAC5CA,EAAK,WAAW,oBAAoB,GACpCA,EAAK,WAAW,iBAAiB,GACjCA,EAAK,WAAW,IAAI,GACpBA,EAAK,WAAW,WAAW,GAC3BA,EAAK,WAAW,WAAW,GAC3BA,EAAK,WAAW,eAAe,GAC/BA,EAAK,WAAW,qBAAqB,EAInCzB,GAAuB0B,GAA4B,CAEvD,IAAMN,EAASO,GAAmBD,EAASpC,GAAcC,EAAU,EAEnE,GAAI6B,IAAW,KAAM,OAAOA,EAG5B,OAAW,CAACQ,EAAOC,CAAG,IAAKrC,GAAe,CACxC,IAAMsC,EAAeH,GAAmBD,EAASE,EAAOC,CAAG,EAE3D,GAAIC,IAAiB,KAAM,OAAOA,CACpC,CAGA,IAAMC,EAAYL,EAAQ,QAAQjC,EAAa,EAE/C,GAAIsC,IAAc,GAAI,OAAOL,EAG7B,IAAMM,EAASN,EAAQ,MAAM,EAAGK,CAAS,EAAE,QAAQ,OAAQ,EAAE,EACvDE,EAAaP,EAAQ,MAAMK,CAAS,EAAE,MAAM;AAAA,CAAI,EAElDG,EAAI,EAER,KAAOA,EAAID,EAAW,QAAUT,GAAYS,EAAWC,CAAC,CAAE,GACxDA,IAGF,IAAMC,EAAYF,EAAW,MAAMC,CAAC,EAAE,KAAK;AAAA,CAAI,EAE/C,OAAOF,GAAUG,EAAY;AAAA,EAAKA,CAAS,GAAK,GAClD,EAMahC,GAAiB,IAAc,CAC1C,IAAMiC,EAAS,sBAEf,MAAO,CACL,gCACA,oCACA,iCAEA,2CACA,gEACA,KAEA,qCAEA,sCAEA,oDAIA,iCACA,6GACA,+BAA+BA,CAAM,2HAA2HA,CAAM,iBACtK,gCAAgCA,CAAM,6HAA6HA,CAAM,iBACzK,kBAAkBA,CAAM,iBAYxB,wBAGA,oCACA,sBACA,4BACA,IACA,0BACA,4CAEA,iEACA,+CACA,uCACA,yCACA,qCACA,8FACA,iGACA,kIACA,0BACA,6CAEA,qFACA,OACA,oIACA,2BACA,+CACA,sDACA,OACA,IACA,4BACA,8CACA,4CACA,KAcA,+BACA,oCACA,+BACA,wBAEA,6JACA,mEACA,OACA,IAUA,6BACA,yBACA,sCACA,6DACA,2CACA,yDACA,SACA,aACA,OAEA,iEACA,uDACA,0DACA,+BACA,oBACA,wDACA,gFACA,6BAEA,mEACA,mBAEA,wGACA,IAaA,kCACA,4CACA,6BACA,mDACA,8CACA,8FAIA,+BACA,wCACA,WAAWA,CAAM,2DACjB,qEACA,eACA,SACA,kBAEA,qBACA,SACA,IACA,8CACA,gCACA,IACF,EAAE,KAAK;AAAA,CAAI,CACb,EAMaC,GAAkB,IACtB,GAAG/C,EAAY;AAAA,EAAKa,GAAe,CAAC;AAAA,EAAKZ,EAAU,GOtZ5D,OAAS,KAAA+C,OAAS,KAOlB,IAAMC,GAA0B,IASnBC,GAA4B,SAA2B,CAClE,GAAI,CACF,MAAMF,sBAAqB,QAAQC,EAAuB,CAC5D,OAASE,EAAgB,CACvB,MAAM,IAAI,MAAM,2FAA4F,CAC1G,MAAOA,CACT,CAAC,CACH,CAEA,GAAI,CACF,MAAMH,eAAc,QAAQC,EAAuB,CACrD,OAASE,EAAgB,CACvB,MAAM,IAAI,MAAM,sEAAuE,CAAE,MAAOA,CAAM,CAAC,CACzG,CACF,ECVA,IAAMC,GAA2B,mCAC3BC,GAA0B,kCAYnBC,GAAgCC,GACvCA,EAAO,SAASH,EAAwB,EAAU,UAElDG,EAAO,SAASF,EAAuB,EAAU,SAE9C,UAyBIG,GAA8B,CAAC,CAC1C,KAAAC,EACA,QAAAC,EACA,OAAAC,EACA,UAAAC,CACF,IAA0C,CACxC,IAAMC,EACJJ,IAAS,UACL,CAAC,oBAAoBC,CAAO,uEAAkE,EAC9F,CAAC,mBAAmBC,CAAM,2BAA2BD,CAAO,gDAA2C,EAE7G,GAAIE,GAAaA,EAAU,OAAS,EAAG,CACrC,IAAME,EAAQL,IAAS,UAAY,qBAAuB,yBAAyBC,CAAO,IAE1FG,EAAM,KAAK,GAAGC,CAAK,KAAKF,EAAU,KAAK,IAAI,CAAC,GAAG,CACjD,CAEA,OAAAC,EAAM,KACJJ,IAAS,UACL,yFACA,0EACN,EAEOI,EAAM,KAAK;AAAA,CAAI,CACxB,ECvFA,OAAS,KAAAE,OAAS,KAUlB,IAAMC,GAA0B,IAKnBC,GAAoB,SAA6B,CAC5D,GAAM,CAAE,cAAAC,CAAc,EAAI,MAAMC,EAAkB,EAElD,OAAOD,EAAc,OAAO,IAC9B,EAYaE,GAAsB,SAC1BC,GAAmB,IACjBC,2BACR,EAUUC,GAAqB,MAAOC,GAChCH,GAAmB,IACjBC,+BAA8BE,CAAO,SAC7C,EAQGH,GAAqB,MAAOI,GAAwD,CACxF,IAAMC,EAAYJ,GAAE,MAEpBA,GAAE,MAAQ,GACV,GAAI,CACF,IAAMK,EAAS,MAAMF,EAAI,EAAE,QAAQT,EAAuB,EAE1D,OAAOY,GAAkBD,EAAO,MAAM,CACxC,MAAQ,CACN,OAAO,IACT,QAAE,CACAL,GAAE,MAAQI,CACZ,CACF,EAGME,GAAqBC,GAAoC,CAC7D,IAAIC,EAEJ,GAAI,CACFA,EAAS,KAAK,MAAMD,CAAM,CAC5B,MAAQ,CACN,OAAO,IACT,CAEA,OAAK,MAAM,QAAQC,CAAM,EAElBA,EACJ,IAAKC,GACGA,IAAU,MAAQ,OAAOA,GAAU,SAAYA,EAA6B,KAAO,MAC3F,EACA,OAAQC,GACA,OAAOA,GAAS,QACxB,EARgC,IASrC,ECzFA,OAAOC,OAAQ,mBA+BR,IAAMC,GAAsB,MAAOC,EAAkBC,IAA8C,CACxG,GAAI,CAACA,EACH,MAAO,CAAE,aAAc,GAAO,aAAc,CAAC,EAAG,WAAY,EAAM,EAGpE,GAAI,CACF,IAAMC,EAAM,MAAMJ,GAAG,SAASE,EAAU,OAAO,EACzCG,EAAkBD,EAAI,KAAK,IAAM,GAAK,CAAC,EAAI,KAAK,MAAMA,CAAG,EAE/D,GAAI,OAAOC,GAAW,UAAYA,IAAW,MAAQ,MAAM,QAAQA,CAAM,EACvE,MAAO,CAAE,aAAc,GAAO,aAAc,CAAC,EAAG,WAAY,EAAK,EAGnE,IAAMC,EAAO,OAAO,KAAKD,CAAM,EAE/B,MAAO,CAAE,aAAcC,EAAK,OAAS,EAAG,aAAcA,EAAM,WAAY,EAAM,CAChF,MAAQ,CACN,MAAO,CAAE,aAAc,GAAO,aAAc,CAAC,EAAG,WAAY,EAAK,CACnE,CACF,EAWaC,GAAqBC,GAC5BA,EAAQ,WACH,mCAGLA,EAAQ,aAAa,SAAW,EAC3B,8BAGF,IAAIA,EAAQ,aAAa,MAAM,iBAAiBA,EAAQ,aAAa,KAAK,IAAI,CAAC,ICtExF,OAAOC,OAAQ,UACf,OAAOC,OAAU,YAmCV,IAAMC,GAAkB,CAAC,CAAE,WAAAC,EAAY,WAAAC,EAAY,qBAAAC,CAAqB,IACzE,CAACF,GACD,CAACC,GACD,CAACC,EAA6B,GAE3BA,IAAyBD,EAIrBE,GAA2BC,GAAgC,CACtE,GAAI,CAACA,EAAa,MAAO,GAEzB,GAAI,CACF,OAAOC,GAAG,aAAaD,CAAW,CACpC,MAAQ,CACN,MAAO,EACT,CACF,EAQaE,GAAiB,CAACC,EAA6BC,IAA2B,CACrF,GAAI,CACF,IAAMC,EAAMC,GAAuBH,CAAmB,EAEtDF,GAAG,UAAUI,EAAK,CAAE,UAAW,GAAM,KAAM,GAAM,CAAC,EAClDE,GAAoBC,GAAK,KAAKH,EAAKI,CAAa,EAAGL,EAAU,GAAK,CACpE,OAASM,EAAO,CACdC,EAAO,MAAM,6BAA8BD,EAAgB,OAAO,EAAE,CACtE,CACF,EASaE,GAAuB,CAClCC,EAAgB,KAAK,IAAI,EACzBC,EAAqBC,KACZ,CACT,IAAMC,EAAOC,GAAiB,EAE1BC,EAEJ,GAAI,CACFA,EAAUjB,GAAG,YAAYe,EAAM,CAAE,cAAe,EAAK,CAAC,CACxD,MAAQ,CACN,MACF,CAEA,QAAWG,KAASD,EAAS,CAC3B,GAAI,CAACC,EAAM,YAAY,EAAG,SAE1B,IAAMtB,EAAaW,GAAK,KAAKQ,EAAMG,EAAM,IAAI,EAE7C,GAAI,CACF,GAAM,CAAE,QAAAC,CAAQ,EAAInB,GAAG,SAASO,GAAK,KAAKX,EAAYY,CAAa,CAAC,EAEhEI,EAAQO,GAAWN,EAAa,KAClCb,GAAG,OAAOJ,EAAY,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CAE1D,MAAQ,CAER,CACF,CACF,EAUawB,GAA6B,CAAClB,EAA6BmB,IAAyB,CAC/F,GAAI,CACF,IAAMjB,EAAMC,GAAuBH,CAAmB,EAEtD,GAAImB,EAAO,CACTrB,GAAG,OAAOI,EAAK,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EAE/C,MACF,CAGA,GAAI,CAACJ,GAAG,WAAWO,GAAK,KAAKH,EAAKI,CAAa,CAAC,EAAG,OAEnDF,GAAoBC,GAAK,KAAKH,EAAKkB,EAAc,EAAG;AAAA,EAAoC,GAAK,CAC/F,OAASb,EAAO,CACdC,EAAO,MAAM,kCAAmCD,EAAgB,OAAO,EAAE,CAC3E,CACF,Ed7FA,IAAMc,GAAe,MACnBC,EACAC,EACAC,EACAC,IACyB,CACzB,GAAI,CACF,aAAMC,KAAIH,CAAO,GAEV,CAAE,KAAAD,EAAM,OAAQ,OAAQ,QAASE,CAAW,CACrD,MAAQ,CACN,MAAO,CAAE,KAAAF,EAAM,OAAQ,OAAQ,QAASG,CAAQ,CAClD,CACF,EAEME,GAAwB,IAAmB,CAC/C,IAAML,EAAO,mBACPM,EAAYC,GAAK,KAAKC,GAAG,QAAQ,EAAG,QAAQ,EAElD,GAAI,CAACC,EAAG,WAAWH,CAAS,EAC1B,MAAO,CAAE,KAAAN,EAAM,OAAQ,OAAQ,QAAS,yCAA0C,EAGpF,IAAMU,EAAUD,EAAG,aAAaH,EAAW,OAAO,EAC5CK,EAAWD,EAAQ,QAAQE,EAAY,EACvCC,EAASH,EAAQ,QAAQI,EAAU,EAEzC,GAAIH,IAAa,IAAME,IAAW,IAAMA,EAASF,EAC/C,MAAO,CACL,KAAAX,EACA,OAAQ,OACR,QAAS,kEACX,EAGF,IAAMe,EAAiBL,EAAQ,MAAMC,EAAUE,EAASC,GAAW,MAAM,EAAE,KAAK,EAC1EE,EAAgBC,GAAgB,EAAE,KAAK,EAE7C,OAAIF,IAAmBC,EACd,CACL,KAAAhB,EACA,OAAQ,OACR,QAAS,uEACX,EAGK,CAAE,KAAAA,EAAM,OAAQ,OAAQ,QAAS,iDAAkD,CAC5F,EAQMkB,GAAiB,SAAkC,CACvD,IAAMlB,EAAO,aAETmB,EAEJ,GAAI,CACFA,EAAO,MAAMC,EAAe,CAC9B,MAAQ,CACN,MAAO,CAAE,KAAApB,EAAM,OAAQ,OAAQ,QAAS,+BAAgC,CAC1E,CAEA,IAAMqB,EAAQC,GAAwBH,CAAI,EAE1C,GAAI,CAACE,EACH,MAAO,CAAE,KAAArB,EAAM,OAAQ,OAAQ,QAAS,yBAA0B,EAGpE,IAAMuB,EAAWhB,GAAK,KAAKiB,GAAuBH,CAAK,EAAGI,CAAa,EAEvE,GAAI,CAAChB,EAAG,WAAWc,CAAQ,EACzB,MAAO,CAAE,KAAAvB,EAAM,OAAQ,OAAQ,QAAS,oDAAqD,EAG/F,IAAM0B,EAAa,KAAK,OAAO,KAAK,IAAI,EAAIjB,EAAG,SAASc,CAAQ,EAAE,SAAW,GAAI,EAC3EI,EAAa,KAAK,MAAMD,EAAa,EAAE,EACvCE,EAAa,KAAK,MAAMC,GAA2B,EAAE,EAE3D,OAAIH,GAAcG,GACT,CACL,KAAA7B,EACA,OAAQ,OACR,QAAS,kCAAkC2B,CAAU,cAAcC,CAAU,wBAC/E,EAGK,CAAE,KAAA5B,EAAM,OAAQ,OAAQ,QAAS,wBAAwB2B,CAAU,OAAQ,CACpF,EAEMG,GAAiC,SAAkC,CACvE,IAAM9B,EAAO,gCAEb,GAAI,CACF,IAAMmB,EAAO,MAAMC,EAAe,EAC5BW,EAAWxB,GAAK,KAAKY,EAAM,qBAAqB,EAEtD,GAAI,CAACV,EAAG,WAAWsB,CAAQ,EACzB,MAAO,CAAE,KAAA/B,EAAM,OAAQ,OAAQ,QAAS,oCAAoC+B,CAAQ,EAAG,EAGzF,IAAMrB,EAAUD,EAAG,aAAasB,EAAU,OAAO,EAIjD,MAFgB,+CAA+C,KAAKrB,CAAO,EAUpE,CAAE,KAAAV,EAAM,OAAQ,OAAQ,QAAS,uCAAwC,EAPvE,CACL,KAAAA,EACA,OAAQ,OACR,QAAS,kEACX,CAIJ,OAASgC,EAAK,CACZ,MAAO,CACL,KAAAhC,EACA,OAAQ,OACR,QAAS,uCAAwCgC,EAAc,OAAO,EACxE,CACF,CACF,EAEMC,GAA2B,SAAkC,CACjE,IAAMjC,EAAO,yBAEb,GAAI,CACF,OAAAkC,GAAyB,EACzB,MAAMC,EAAkB,EAEjB,CACL,KAAAnC,EACA,OAAQ,OACR,QAAS,6DACX,CACF,OAASgC,EAAK,CACZ,MAAO,CAAE,KAAAhC,EAAM,OAAQ,OAAQ,QAAUgC,EAAc,OAAQ,CACjE,CACF,EAUaI,GAA4B,SAAkC,CACzE,IAAMpC,EAAO,yBAETqC,EAEJ,GAAI,CACFA,EAAU,MAAMC,GAAkB,CACpC,MAAQ,CACN,MAAO,CAAE,KAAAtC,EAAM,OAAQ,OAAQ,QAAS,sEAAkE,CAC5G,CAEA,IAAMuC,EAAW,MAAMC,GAAoB,EAE3C,OAAID,IAAa,KACR,CAAE,KAAAvC,EAAM,OAAQ,OAAQ,QAAS,yEAAqE,EAG3GuC,EAAS,SAASF,CAAO,EACpB,CAAE,KAAArC,EAAM,OAAQ,OAAQ,QAAS,oBAAoBqC,CAAO,UAAW,EAGzE,CACL,KAAArC,EACA,OAAQ,OACR,QAAS,oBAAoBqC,CAAO,oFAClCE,EAAS,OAAS,EAAIA,EAAS,KAAK,IAAI,EAAI,MAC9C,EACF,CACF,EAyBaE,GAAwB,SAAkC,CACrE,IAAMzC,EAAO,qBAEb,GAAI,CACF,IAAM0C,EAAQ,MAAMC,EAAuB,EACrCC,EAASnC,EAAG,WAAWiC,EAAM,WAAW,EACxCG,EAAU,MAAMC,GAAoBJ,EAAM,YAAaE,CAAM,EAC7DG,EAASH,EAASI,GAAkBH,CAAO,EAAI,oCAErD,MAAO,CACL,KAAA7C,EACA,OAAQ,OACR,QAAS,GAAGiD,EAAQP,EAAM,WAAW,CAAC,IAAIK,CAAM,oBAAeL,EAAM,WAAW,EAClF,CACF,OAASV,EAAK,CACZ,MAAO,CAAE,KAAAhC,EAAM,OAAQ,OAAQ,QAAUgC,EAAc,OAAQ,CACjE,CACF,EAkBakB,GAA8B,SAAkC,CAC3E,IAAMlD,EAAO,4BAET0C,EAEJ,GAAI,CACFA,EAAQ,MAAMC,EAAuB,CACvC,MAAQ,CACN,MAAO,CAAE,KAAA3C,EAAM,OAAQ,OAAQ,QAAS,6DAAyD,CACnG,CAEA,IAAMmD,EAAa5C,GAAK,KAAKA,GAAK,QAAQmC,EAAM,UAAU,EAAG,aAAa,EAE1E,OAAKjC,EAAG,WAAW0C,CAAU,EAIxB1C,EAAG,WAAWiC,EAAM,UAAU,EAQ5B,CACL,KAAA1C,EACA,OAAQ,OACR,QAAS,qCAAqCiD,EAAQE,CAAU,CAAC,WAAMF,EAAQP,EAAM,UAAU,CAAC,kCAClG,EAXS,CACL,KAAA1C,EACA,OAAQ,OACR,QAAS,2CAA2CiD,EAAQE,CAAU,CAAC,sFACzE,EARO,CAAE,KAAAnD,EAAM,OAAQ,OAAQ,QAAS,mCAAoC,CAgBhF,EAQMoD,GAAkG,CACtG,OAAQ,CACN,QAAS,CAAC,SAAU,WAAW,EAC/B,MAAO,SACP,QAAS,4DACX,EACA,IAAK,CACH,QAAS,CAAC,MAAO,WAAW,EAC5B,MAAO,MACP,QAAS,sDACX,CACF,EAEMC,GAAW,MAAOC,GAAkD,CACxE,IAAMC,EAAOH,GAAeE,CAAQ,EAEpC,GAAI,CACF,aAAMlD,KAAImD,EAAK,OAAO,GAEf,CAAE,GAAI,GAAM,MAAOA,EAAK,MAAO,QAASA,EAAK,OAAQ,CAC9D,MAAQ,CACN,MAAO,CAAE,GAAI,GAAO,MAAOA,EAAK,MAAO,QAASA,EAAK,OAAQ,CAC/D,CACF,EAUaC,GAAoB,SAAkC,CACjE,IAAMxD,EAAO,gBAETyD,EAEJ,GAAI,CACFvB,GAAyB,EACzB,IAAMwB,EAAS,MAAMvB,EAAkB,EAEvCsB,EAAYE,EAAsBD,CAAM,EAAE,IAAKE,GACtCA,EAAI,QACZ,CACH,MAAQ,CACN,MAAO,CAAE,KAAA5D,EAAM,OAAQ,OAAQ,QAAS,sEAAkE,CAC5G,CAEA,GAAIyD,EAAU,SAAW,EACvB,MAAO,CAAE,KAAAzD,EAAM,OAAQ,OAAQ,QAAS,+BAAgC,EAG1E,IAAM6D,EAAS,MAAM,QAAQ,IAC3BJ,EAAU,IAAKH,GACND,GAASC,CAAQ,CACzB,CACH,EACMQ,EAAUD,EAAO,OAAQE,GACtB,CAACA,EAAM,EACf,EAED,OAAID,EAAQ,SAAW,EACd,CACL,KAAA9D,EACA,OAAQ,OACR,QAAS,cAAc6D,EACpB,IAAKE,GACGA,EAAM,KACd,EACA,KAAK,IAAI,CAAC,EACf,EAGK,CACL,KAAA/D,EACA,OAAQ,OACR,QAAS8D,EACN,IAAKC,GACGA,EAAM,OACd,EACA,KAAK,IAAI,CACd,CACF,EAEMC,GAAuB,CAAC,EAAG,EAAG,EAAG,EAAG,CAAC,EAErCC,GAAqB,SAAkC,CAC3D,IAAMjE,EAAO,iBAEb,GAAI,CAEF,IAAMkE,GADS,MAAM9D,qBACM,OACxB,MAAM;AAAA,CAAI,EACV,IAAK+D,GACGA,EAAE,KAAK,CACf,EACA,OAAQA,GACAA,EAAE,WAAW,MAAM,GAAKA,EAAE,WAAW,MAAM,CACnD,EAEGC,EAAmB,CAAC,EAE1B,QAAWC,KAAOL,GAAsB,CACtC,IAAMM,EAAOJ,EAAYG,EAAM,CAAC,GAE5B,CAACC,GAAQ,CAACA,EAAK,WAAW,MAAM,IAClCF,EAAO,KAAKC,CAAG,CAEnB,CAEA,OAAID,EAAO,OAAS,EACX,CACL,KAAApE,EACA,OAAQ,OACR,QAAS,+BAA+BoE,EAAO,KAAK,IAAI,CAAC,2CAC3D,EAGK,CACL,KAAApE,EACA,OAAQ,OACR,QAAS,mFACX,CACF,OAASgC,EAAK,CACZ,MAAO,CACL,KAAAhC,EACA,OAAQ,OACR,QAAS,oCAAqCgC,EAAc,OAAO,EACrE,CACF,CACF,EASauC,GAAkB,SAAoC,CACjE,IAAIC,EAEJ,GAAI,CACFA,GAAkB,MAAM7B,EAAuB,GAAG,IACpD,MAAQ,CACN,MAAO,CAAC,CACV,CAEA,GAAI,CAAClC,EAAG,WAAW+D,CAAc,EAAG,MAAO,CAAC,EAE5C,IAAMC,EAAalE,GAAK,KAAKA,GAAK,QAAQiE,CAAc,EAAG,WAAW,EAChE9D,EAAUD,EAAG,WAAWgE,CAAU,EAAIhE,EAAG,aAAagE,EAAY,OAAO,EAAI,GAC7EC,EAAUC,GAAgBjE,EAASkE,GAAqBC,EAAiB,EAE/E,MAAO,CACL,CACE,KAAM,kBACN,OAAQH,EAAU,OAAS,OAC3B,QAASA,EAAU,0BAA4B,6DACjD,CACF,CACF,EAUMI,GAAoBC,GACjBC,GAAsB,CAAC,UAAW,SAAS,EAAG,CAAE,KAAM,GAAM,IAAAD,CAAI,CAAC,EAIpEE,GAAYF,GACTC,GAAsB,CAAC,OAAO,EAAG,CAAE,IAAAD,CAAI,CAAC,EAI3CG,GAAoB,IAAI,IAAI,CAAC,4BAA6B,iCAAiC,CAAC,EAQ5FC,GAAiB,+BAcjBC,GAAuB,MAC3BC,EACAN,IACyB,CACzB,IAAM/E,EAAO,4BAA4BsF,EAAsB,GAG/D,OAFgB,MAAMD,EAAeC,GAAwB,EAAI,EAU1D,CAAE,KAAAtF,EAAM,OAAQ,OAAQ,QAAS,iCAAiCsF,EAAsB,EAAG,EAPzF,CACL,KAAAtF,EACA,OAAQ,OACR,QAAS,2CAA2CsF,EAAsB,qCAAqCR,GAAiBC,CAAG,CAAC,KACtI,CAIJ,EAUMQ,GAAuB,MAC3BC,EACAC,EACAC,EACAX,IACyB,CACzB,IAAM/E,EAAO,0BACP2F,EAAc,CAClB,YACA,GAAGF,EAAO,MAAM,EAAG,CAAC,EAAE,IAAKG,GAClBA,EAAM,IACd,CACH,EAMMxB,GALU,MAAM,QAAQ,IAC5BuB,EAAY,IAAI,MAAOE,IACd,CAAE,WAAAA,EAAY,OAAQ,MAAML,EAAUF,GAAwBO,CAAU,CAAE,EAClF,CACH,GACuB,OAAQC,GACtB,CAACA,EAAM,OAAO,EACtB,EAED,GAAI1B,EAAO,SAAW,EACpB,MAAO,CACL,KAAApE,EACA,OAAQ,OACR,QAAS,gCAAgCiD,EAAQyC,CAAM,CAAC,KAAKC,EAAY,KAAK,IAAI,CAAC,GACrF,EAGF,IAAMI,EAAQ3B,EAAO,IAAK0B,GACjBA,EAAM,OAAO,GAAK,GAAKA,EAAM,OAAO,IAC5C,EAED,OAAIC,EAAM,SAASZ,EAAc,EACxB,CACL,KAAAnF,EACA,OAAQ,OACR,QAAS,2CAA2CmF,EAAc,sBAAsBf,EACrF,IAAK0B,GACGA,EAAM,UACd,EACA,KAAK,IAAI,CAAC,0FACf,EAIAC,EAAM,KAAMC,GACHd,GAAkB,IAAIc,CAAI,CAClC,EAEM,CACL,KAAAhG,EACA,OAAQ,OACR,QAAS,8DAA8DiD,EAAQyC,CAAM,CAAC,yCAAoCT,GAASF,CAAG,CAAC,uBAAuBD,GAAiBC,CAAG,CAAC,KACrL,EAGK,CAAE,KAAA/E,EAAM,OAAQ,OAAQ,QAAS,yBAAyB+F,EAAM,KAAK,IAAI,CAAC,GAAI,CACvF,EASME,GAAyB,CAACC,EAAkBR,EAAgBX,IAA6B,CAC7F,IAAM/E,EAAO,sBAEb,OAAKkG,EAQE,CAAE,KAAAlG,EAAM,OAAQ,OAAQ,QAAS,GAAGiD,EAAQyC,CAAM,CAAC,8CAA+C,EAPhG,CACL,KAAA1F,EACA,OAAQ,OACR,QAAS,+DAA+DiF,GAASF,CAAG,CAAC,sBACvF,CAIJ,EAMMoB,GAA2B,MAC/BV,EACAW,EACArB,IACyB,CACzB,IAAM/E,EAAO,kBAEb,GAAIyF,EAAO,SAAW,EAAG,MAAO,CAAE,KAAAzF,EAAM,OAAQ,OAAQ,QAAS,+BAAgC,EAOjG,IAAMqG,GALS,MAAM,QAAQ,IAC3BZ,EAAO,IAAI,MAAOG,IACT,CAAE,MAAAA,EAAO,KAAM,MAAMQ,EAAYR,EAAM,IAAI,CAAE,EACrD,CACH,GAEG,OAAQE,GACA,CAACA,EAAM,IACf,EACA,IAAKA,GACGA,EAAM,MAAM,IACpB,EAEH,OAAIO,EAAM,SAAW,EACZ,CAAE,KAAArG,EAAM,OAAQ,OAAQ,QAAS,GAAGyF,EAAO,MAAM,8BAA+B,EAGlF,CACL,KAAAzF,EACA,OAAQ,OACR,QAAS,GAAGqG,EAAM,MAAM,6BAA6BA,EAAM,KAAK,IAAI,CAAC,mBAAmBrB,GAAsB,CAAC,QAAS,WAAY,QAAQ,EAAG,CAAE,IAAAD,CAAI,CAAC,CAAC,KACzJ,CACF,EAkBauB,GAAgB,MAAOC,EAA0B,CAAC,IAA8B,CAC3F,IAAMC,EAAaD,EAAK,YAAcE,GAChCpB,EAAiBkB,EAAK,gBAAkBG,GACxClB,EAAYe,EAAK,WAAaI,GAC9BC,EAAYL,EAAK,WAAaM,GAC9BC,EAAaP,EAAK,QAAUQ,GAC5BX,EAAcG,EAAK,aAAeS,GAClCtB,GAAUa,EAAK,QAAUU,IAAY,EAGrClC,EAAMyB,EAAW,EAEvB,GAAIzB,IAAQ,KACV,MAAO,CACL,CACE,KAAM,qBACN,OAAQ,OACR,QAAS,yEACX,CACF,EAGF,IAAMmC,EAAyB,CAC7B,KAAM,qBACN,OAAQ,OACR,QAAS,0CACX,EACMzB,EAASqB,EAAW,EACpBK,EAAU,MAAM/B,GAAqBC,EAAgBN,CAAG,EAGxDqC,EACJD,EAAQ,SAAW,OACf,CAAE,KAAM,0BAA2B,OAAQ,OAAQ,QAAS,4CAAwC,EACpG,MAAM5B,GAAqBC,EAAWC,EAAQC,EAAQX,CAAG,EAE/D,MAAO,CACLmC,EACAC,EACAC,EACAnB,GAAuBW,EAAU,EAAGlB,EAAQX,CAAG,EAC/C,MAAMoB,GAAyBV,EAAQW,EAAarB,CAAG,CACzD,CACF,EAKasC,GAAS,SAAY,CA6DhC,IAAMC,EAAwB,CAAC,GA5DG,MAAM,QAAQ,IAAI,CAClDvH,GACE,eACA,CAAC,KAAM,WAAW,EAClB,0BACA,oEACF,EACAA,GACE,mBACA,CAAC,KAAM,OAAQ,QAAQ,EACvB,8BACA,qDACF,EACAA,GACE,oBACA,CAAC,UAAW,WAAW,EACvB,2BACA,uFACF,EACAA,GACE,wBACA,CAAC,UAAW,IAAI,EAChB,+BACA,sDACF,EACAA,GACE,gBACA,CAAC,MAAO,WAAW,EACnB,uBACA,uHACF,EACAA,GACE,gBACA,CAAC,MAAO,WAAW,EACnB,mBACA,mEACF,EACAA,GACE,uCACA,CAAC,6BAA8B,WAAW,EAC1C,0CACA,gJACF,EACAA,GACE,iBACA,CAAC,OAAQ,WAAW,EACpB,oBACA,wDACF,EACAkE,GAAmB,EACnB,QAAQ,QAAQ5D,GAAsB,CAAC,EACvCa,GAAe,EACfY,GAA+B,EAC/BG,GAAyB,EACzBG,GAA0B,EAC1BK,GAAsB,EACtBS,GAA4B,EAC5BM,GAAkB,CACpB,CAAC,EAE6C,GAAI,MAAM8C,GAAc,EAAI,GAAI,MAAM/B,GAAgB,CAAE,EAEtGgD,EAAO,KAAK;AAAA,CAAyB,EAErC,QAAWC,KAASF,EAAQ,CAC1B,IAAMG,EAAOD,EAAM,SAAW,OAAS,SAAW,SAElDD,EAAO,KAAK,KAAKE,CAAI,IAAID,EAAM,IAAI,KAAKA,EAAM,OAAO,EAAE,CACzD,CAEA,IAAME,EAAoB,CACxB,OAAQJ,EAAO,IAAKK,IACX,CAAE,KAAMA,EAAE,KAAM,OAAQA,EAAE,OAAQ,QAASA,EAAE,OAAQ,EAC7D,EACD,UAAWL,EAAO,MAAOK,GAChBA,EAAE,SAAW,MACrB,CACH,EAEA,MAAO,CACL,QAASC,EAAY,KAAK,UAAUF,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAGaG,GAAgBC,EAAc,CACzC,KAAM,SACN,YAAa,iFACb,YAAa,CAAC,EACd,aAAc,CACZ,OAAQC,GACL,MACCA,GAAE,OAAO,CACP,KAAMA,GAAE,OAAO,EAAE,SAAS,mBAAmB,EAC7C,OAAQA,GAAE,KAAK,CAAC,OAAQ,MAAM,CAAC,EAAE,SAAS,cAAc,EACxD,QAASA,GAAE,OAAO,EAAE,SAAS,gCAAgC,CAC/D,CAAC,CACH,EACC,SAAS,2BAA2B,EACvC,UAAWA,GAAE,QAAQ,EAAE,SAAS,2BAA2B,CAC7D,EACA,QAASV,EACX,CAAC,Eep0BD,OAAOW,OAAQ,UACf,OAAOC,OAAU,YACjB,OAAOC,OAAa,eACpB,OAAS,KAAAC,OAAS,MAmClB,IAAMC,GAAwB,MAAOC,GAAkC,CACrE,IAAIC,EAAc,GAElB,GAAI,CACFA,EAAc,MAAMC,EAAe,CACrC,MAAQ,CACN,MACF,CAEA,IAAMC,EAAQC,GAAwBH,CAAW,EAE7CE,GAAOE,GAA2BF,EAAOH,CAAK,CACpD,EAQaM,GAAsBC,GAC1B,CACL,GAAGA,EAAS,IAAKC,GACR,SAASA,CAAC,EAClB,EACD,SAASC,EAAiB,GAC1B,SAASC,EAAwB,GACjC,SAASC,EAAyB,GAClC,SAASC,EAA8B,GACvC,SAASC,EAA2B,GACpC,SAASC,EAA4B,GACrC,UAAUC,EAAyB,MACrC,EAUWC,GAAW,MAAO,CAAE,MAAAhB,EAAQ,EAAM,EAAkB,CAAC,IAAM,CACtE,IAAMiB,EAAWC,GAAmB,EAC9BC,EAAcC,GAAK,KAAKH,EAAUI,CAAa,EAC/CC,EAAeC,GAAG,WAAWJ,CAAW,EAE9C,GAAI,CAACG,GAAgB,CAACtB,EACpB,MAAM,IAAI,MAAM,oDAAoD,EAItE,MAAMD,GAAsBC,CAAK,EAEjC,IAAMO,EAAWe,EAAeE,GAAyBL,CAAW,EAAI,CAAC,EAEnEM,EAAanB,GAAmBC,CAAQ,EAExCmB,EAAgBN,GAAK,QAAQH,EAAUU,EAAc,EAE3DJ,GAAG,UAAUN,EAAU,CAAE,UAAW,GAAM,KAAM,GAAM,CAAC,EAEvDW,GAAoBF,EAAe,GAAGD,EAAW,KAAK;AAAA,CAAI,CAAC;AAAA,EAAM,GAAK,EAGtEI,GAAQ,OAAO,MAAM,GAAGH,CAAa;AAAA,CAAI,EAIrCJ,GAAcC,GAAG,OAAOJ,EAAa,CAAE,MAAO,EAAK,CAAC,EAExD,IAAMW,EAAoB,CACxB,SAAUJ,EACV,cAAenB,EAAS,OACxB,gBAAiBkB,EACjB,OAAQzB,CACV,EAEA,MAAO,CACL,QAAS+B,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAGaE,GAAkBC,EAAc,CAC3C,KAAM,YACN,YACE,kiBACF,YAAa,CAAC,EACd,aAAc,CACZ,SAAUC,GAAE,OAAO,EAAE,SAAS,gDAAgD,EAC9E,cAAeA,GAAE,OAAO,EAAE,SAAS,6BAA6B,EAChE,gBAAiBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,4BAA4B,EAC1E,OAAQA,GAAE,QAAQ,EAAE,SAAS,oDAAoD,CACnF,EACA,QAAS,IACAlB,GAAS,CAEpB,CAAC,ECzID,OAAS,KAAAmB,OAAS,MAcX,IAAMC,GAAU,SAAY,CACjC,IAAMC,EAAU,MAAMC,GAAkB,EAClC,CAAE,aAAAC,CAAa,EAAI,MAAMC,EAAkB,EAEjDC,EAAO,KAAK,oBAAoBJ,CAAO;AAAA,CAAI,EAC3CI,EAAO,KAAK,oBAAoB,EAEhC,QAAWC,KAAOH,EAChBE,EAAO,KAAK,OAAOC,CAAG,EAAE,EAG1B,IAAMC,EAAoB,CACxB,QAAAN,EACA,QAASE,CACX,EAEA,MAAO,CACL,QAASK,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAGaE,GAAiBC,EAAc,CAC1C,KAAM,WACN,YACE,mPACF,YAAa,CAAC,EACd,aAAc,CACZ,QAASC,GAAE,OAAO,EAAE,SAAS,+BAA+B,EAC5D,QAASA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,+BAA+B,CACvE,EACA,QAASX,EACX,CAAC,EC/CD,OAAOY,OAAY,mBACnB,OAAS,UAAAC,OAAc,cACvB,OAAOC,OAAQ,UACf,OAAOC,OAAU,YACjB,OAAOC,OAAa,eACpB,OAAS,KAAAC,OAAS,MAClB,OAAS,KAAAC,OAAS,KCWX,IAAMC,GAAiBC,GAAuC,CACnE,GAAIA,IAAU,MAAQ,OAAOA,GAAU,SAAU,OACjD,IAAMC,EAAUD,EAA+B,OAE/C,OAAO,OAAOC,GAAW,UAAYA,EAAO,OAAS,EAAIA,EAAS,MACpE,EASMC,GAAe,CAACF,EAAgBG,IAAuC,CAC3E,IAAMF,EAASE,EAAI,eAAiBJ,GAAcC,CAAK,EACjDI,EAAQ,CAAC,aAAaD,EAAI,SAAS,EAAE,EAE3C,OAAIF,GAAQG,EAAM,KAAK,WAAWH,EAAO,MAAM,EAAG,GAAwB,EAAE,KAAK,CAAC,EAAE,EAChFE,EAAI,aAAaC,EAAM,KAAK,QAAQD,EAAI,WAAW,EAAE,EAElDC,EAAM,KAAK,UAAK,CACzB,EA8BaC,EAAN,cAA6B,KAAM,CAC/B,UACA,YAET,YAAYL,EAAgBG,EAA4B,CACtD,MAAMD,GAAaF,EAAOG,CAAG,EAAG,CAAE,MAAAH,CAAM,CAAC,EACzC,KAAK,KAAO,iBACZ,KAAK,UAAYG,EAAI,UACrB,KAAK,YAAcA,EAAI,WACzB,CACF,EDQA,IAAMG,GAA4BC,GAC5BA,EACK,CAAC,GAAGC,EAA4B,IAAIC,GAAiB,GAAG,CAAC,EAAE,EAK7D,CAAC,SAASD,EAA4B,GAAI,SAASE,EAAyB,EAAE,EAQ1EC,GAAwB,CAAC,CACpC,MAAAC,EACA,OAAAC,EACA,QAAAC,EACA,YAAAC,EACA,SAAAC,EACA,WAAAT,CACF,IACS,CACL,SACA,GAAGK,EAAM,IAAI,CAAC,CAACK,EAAKC,CAAK,IAChB,GAAGD,CAAG,IAAIR,GAAiBS,CAAK,CAAC,EACzC,EAGD,GAAGC,EAAiB,IAAIV,GAAiBI,CAAM,CAAC,GAChD,GAAGO,EAAwB,IAAIX,GAAiBI,CAAM,CAAC,GACvD,GAAGQ,EAAyB,IAAIZ,GAAiBK,CAAO,CAAC,GACzD,GAAGQ,EAA8B,IAAIb,GAAiBM,CAAW,CAAC,GAClE,GAAGQ,EAA2B,IAAId,GAAiBO,CAAQ,CAAC,GAC5D,GAAGV,GAAyBC,CAAU,EACtC,QACF,EASIiB,GAAyB,SAA6B,CAC1D,GAAI,CACF,OAAO,MAAMC,EAAe,CAC9B,MAAQ,CACN,MAAO,EACT,CACF,EAQaC,GAAmB,MAAO,CACrC,OAAAb,EACA,WAAAN,EAAa,GACb,YAAAoB,EACA,WAAAC,CACF,IAA+D,CAC7D,MAAMC,GAA0B,EAEhC,IAAMf,EAAU,MAAMgB,GAAkB,EAClCf,EAAc,MAAMS,GAAuB,EAE3CZ,EAAQ,MAAMmB,GAAuBjB,EAASD,CAAM,EAEpDG,EAAW,IAAI,KAAK,EAAE,YAAY,EAElCgB,EAAe,GADArB,GAAsB,CAAE,MAAAC,EAAO,OAAAC,EAAQ,QAAAC,EAAS,YAAAC,EAAa,SAAAC,EAAU,WAAAT,CAAW,CAAC,EACnE,KAAK;AAAA,CAAI,CAAC;AAAA,EAEzC0B,EAAWC,GAAmB,EAC9BC,EAAcC,GAAK,QAAQH,EAAUI,CAAa,EAIxD,GAAIV,GAAe,CAACA,EAAY,EAAG,OAAO,KAE1CW,GAAG,UAAUL,EAAU,CAAE,UAAW,GAAM,KAAM,GAAM,CAAC,EACvDM,GAAoBJ,EAAaH,EAAc,GAAK,EAOpD,IAAMQ,EAAuBC,GAAwB1B,CAAW,EAEhE,OAAI2B,GAAgB,CAAE,WAAAnC,EAAY,WAAAqB,EAAY,qBAAAY,CAAqB,CAAC,IAClEG,GAAef,EAAaI,CAAY,EACxCY,GAAqB,GAGhB,CACL,SAAUT,EACV,cAAevB,EAAM,OACrB,QAAAE,EACA,OAAAD,CACF,CACF,EAKagC,GAAU,MAAOC,GAAsB,CAClD,GAAM,CAAE,OAAAjC,CAAO,EAAIiC,EAEfC,EAAiB,GAErB,GAAIlC,EACFkC,EAAiBlC,MACZ,CAIL,MAAMgB,GAA0B,EAEhC,GAAM,CAAE,aAAAmB,CAAa,EAAI,MAAMC,EAAkB,EAEjDC,EAAY,eAAe,EAC3BH,EAAiB,MAAMI,GACrB,CACE,QAAS,4BACT,QAASH,EAAa,IAAKI,IAClB,CAAE,KAAMA,EAAK,MAAOA,CAAI,EAChC,CACH,EAGA,CAAE,OAAQC,GAAQ,MAAO,CAC3B,CACF,CAEAH,EAAY,UAAU,WAAYH,CAAc,EAGhD,IAAMO,EAAS,MAAM5B,GAAiB,CAAE,OAAQqB,EAAgB,WAAY,EAAM,CAAC,EAInF,GAAI,CAACO,EAAQ,MAAM,IAAI,MAAM,0CAA0C,EAGvED,GAAQ,OAAO,MAAM,GAAGC,EAAO,QAAQ;AAAA,CAAI,EAI3CJ,EAAY,MAAM,EAElB,IAAMK,EAAoB,CACxB,SAAUD,EAAO,SACjB,cAAeA,EAAO,cACtB,QAASA,EAAO,QAChB,OAAQA,EAAO,MACjB,EAEA,MAAO,CACL,QAASE,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAOaE,GAA2B,KAAO,KAOzCC,GAA8B,IAE9B3B,GAAyB,MAAOjB,EAAiBD,IAAqD,CAC1G,IAAM8C,EAAYC,GAAE,MAEpBA,GAAE,MAAQ,GACV,GAAI,CACF,IAAIN,EAEJ,GAAI,CACFA,EACE,MAAMM,gEAA+D9C,CAAO,aAAaD,CAAM,GAAG,QAChG6C,EACF,CACJ,OAASG,EAAgB,CACvB,MAAM,MAAMC,GAA8BD,EAAO/C,EAASD,CAAM,CAClE,CAEA,OAAAkD,GAAwBT,EAAO,MAAM,EAE9BU,GAAwBV,EAAO,MAAM,CAC9C,QAAE,CACAM,GAAE,MAAQD,CACZ,CACF,EASMG,GAAgC,MAAOD,EAAgB/C,EAAiBD,IAAmC,CAC/G,IAAMoD,EAASC,GAAcL,CAAK,IAAMA,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,GACvFM,EAAOC,GAA6BH,CAAM,EAEhD,GAAIE,IAAS,UACX,OAAON,aAAiB,MAAQA,EAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC,EAGjE,IAAMQ,EAAYF,IAAS,UAAY,MAAMG,GAAoB,EAAI,MAAMC,GAAmBzD,CAAO,EAErG,OAAO,IAAI,MAAM0D,GAA4B,CAAE,KAAAL,EAAM,QAAArD,EAAS,OAAAD,EAAQ,UAAAwD,CAAU,CAAC,CAAC,CACpF,EAEaN,GAA2BU,GAAyB,CAC/D,IAAMC,EAAQC,GAAO,WAAWF,EAAQ,OAAO,EAE/C,GAAIC,EAAQjB,GACV,MAAM,IAAI,MACR,+CAA+CiB,CAAK,YAAYjB,EAAwB,oCAC1F,CAEJ,EAEahD,GAAoBS,GAGxB,IAFSA,EAAM,WAAW,IAAK,OAAO,CAE3B,IAId0D,GAAuB,eAYhBZ,GAA2BS,GAA4C,CAClF,IAAII,EAEJ,GAAI,CACFA,EAAS,KAAK,MAAMJ,CAAM,CAC5B,MAAQ,CACN,MAAM,IAAI,MAAM,uDAAuD,KAAK,UAAUA,EAAO,MAAM,EAAG,EAAE,CAAC,CAAC,GAAG,CAC/G,CAEA,GAAII,IAAW,MAAQ,OAAOA,GAAW,UAAY,MAAM,QAAQA,CAAM,EACvE,MAAM,IAAI,MAAM,8FAA8F,EAGhH,IAAMjE,EAAiC,CAAC,EAExC,OAAW,CAACK,EAAKC,CAAK,IAAK,OAAO,QAAQ2D,CAAM,EAAG,CACjD,GAAI,CAACD,GAAqB,KAAK3D,CAAG,EAChC,MAAM,IAAI,MACR,+DAA+D,KAAK,UAAUA,EAAI,MAAM,EAAG,EAAE,CAAC,CAAC,GACjG,EAGF,GAAI,OAAOC,GAAU,SACnB,MAAM,IAAI,UAAU,4CAA4CD,CAAG,UAAU,OAAOC,CAAK,GAAG,EAG9FN,EAAM,KAAK,CAACK,EAAKC,CAAK,CAAC,CACzB,CAEA,GAAIN,EAAM,SAAW,EACnB,MAAM,IAAI,MAAM,4CAA4C,EAG9D,OAAOA,CACT,EAGakE,GAAiBC,EAAc,CAC1C,KAAM,WACN,YACE,4cACF,YAAa,CACX,OAAQC,GACL,OAAO,EACP,SAAS,qGAAqG,CACnH,EACA,aAAc,CACZ,SAAUA,GAAE,OAAO,EAAE,SAAS,0DAA0D,EACxF,cAAeA,GAAE,OAAO,EAAE,SAAS,4BAA4B,EAC/D,QAASA,GAAE,OAAO,EAAE,SAAS,sBAAsB,EACnD,OAAQA,GAAE,OAAO,EAAE,SAAS,qBAAqB,CACnD,EACA,QAASnC,EACX,CAAC,EE3YD,OAAOoC,OAAU,YACjB,OAAOC,OAAa,eACpB,OAAS,KAAAC,OAAS,MAuBX,IAAMC,GAAY,SAAY,CACnCC,EAAO,KAAK,6BAA6B,EAGzC,IAAMC,EAAWC,GAAmB,EAE9BC,EAAYC,GAAQ,IAAIC,EAAqB,EAC7CC,EAAcC,GAAK,KAAKN,EAAUO,CAAa,EAEjDC,EAAqB,EACrBC,EAAoB,EAElBC,EAAgBP,GAAQ,IAAIQ,EAAwB,GAAK,KACzDC,EAAiBT,GAAQ,IAAIU,EAAyB,GAAK,KAC3DC,EAAkBX,GAAQ,IAAIY,EAA2B,GAAK,KAC9DC,EAAab,GAAQ,IAAIc,EAA4B,IAAM,IAC3DC,EAAU,EAAQf,GAAQ,IAAIgB,EAAyB,EAE7D,GAAIT,EAAe,CACjB,IAAMU,EAAWC,GAAyBhB,CAAW,EAEjDe,EAAS,OAAS,IACpBX,EAAoBW,EAAS,OAC7BZ,EAAqBY,EAAS,OAAQE,GAC7BA,KAAKnB,GAAQ,GACrB,EAAE,QAGL,IAAMoB,EAAkBT,GAAiB,QAAQ,YAAa,EAAE,GAAK,KAC/DU,EAASR,EAAa,cAAgB,kBAM5C,GAJAjB,EAAO,KACL,KAAKW,CAAa,KAAKF,CAAkB,OAAOC,CAAiB,iBAAiBe,CAAM,cAAcZ,CAAc,eAAeW,CAAe,cAAcrB,CAAS;AAAA,CAC3K,EAEIO,EAAoB,GAAKD,EAAqBC,EAAmB,CACnE,IAAMgB,EAAUhB,EAAoBD,EAEpCT,EAAO,KACL,KAAK0B,CAAO,4HACd,CACF,CACF,KAAO,CACL,IAAMC,EAAcR,EAAU,gFAA6E,GAE3GnB,EAAO,KAAK,aAAaG,CAAS,kBAAkBwB,CAAW;AAAA,CAAI,CACrE,CAEA,IAAMC,EAAoB,CACxB,UAAAzB,EACA,mBAAAM,EACA,kBAAAC,EACA,cAAAC,EACA,eAAAE,EACA,gBAAAE,EACA,WAAAE,EACA,QAAAE,CACF,EAEA,MAAO,CACL,QAASU,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAGaE,GAAmBC,EAAc,CAC5C,KAAM,aACN,YACE,uWACF,YAAa,CAAC,EACd,aAAc,CACZ,UAAWC,GAAE,OAAO,EAAE,SAAS,6BAA6B,EAC5D,mBAAoBA,GAAE,OAAO,EAAE,SAAS,qDAAqD,EAC7F,kBAAmBA,GAAE,OAAO,EAAE,SAAS,kCAAkC,EACzE,cAAeA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8DAA8D,EAC5G,eAAgBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4CAA4C,EAC3F,gBAAiBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+CAA+C,EAC/F,WAAYA,GAAE,QAAQ,EAAE,SAAS,+EAA+E,EAChH,QAASA,GAAE,QAAQ,EAAE,SAAS,sEAAsE,CACtG,EACA,QAASjC,EACX,CAAC,EC3GD,OAAS,KAAAkC,OAAS,MAClB,OAAS,KAAAC,MAAS,KCDlB,OAAS,KAAAC,OAAS,KCAlB,OAAS,KAAAC,MAAS,KCAlB,OAAS,KAAAC,OAAS,KCAlB,OAAOC,OAAa,eAqBpB,IAAMC,GAAe,MAAOC,EAAoBC,IAAmC,CACjF,GAAID,EAAS,GACX,OAGF,IAAME,EAAY,MAAMF,EAAS,KAAK,EAEtC,MAAAG,EAAO,MACL,CACE,OAAQH,EAAS,OACjB,WAAYA,EAAS,WACrB,MAAOE,CACT,EACAD,CACF,EAEM,IAAI,MAAM,QAAQD,EAAS,MAAM,KAAKA,EAAS,UAAU,EAAE,CACnE,EAQaI,GAAoB,MAC/BC,EACAC,IACqC,CACrC,GAAI,CACF,GAAM,CAAE,QAAAC,EAAS,MAAAC,EAAO,MAAAC,EAAO,UAAAC,CAAU,EAAIJ,EAEvCK,EAAc,CAClB,KAAMN,EAAO,KACb,UAAWA,EAAO,WAAaK,EAC/B,YAAaL,EAAO,aAAe,GACnC,SAAUA,EAAO,UAAY,GAC7B,SAAUA,EAAO,UAAY,EAC/B,EAEMO,EAAM,GAAGL,CAAO,sBAGhBM,EAAc,KAAK,GAAGJ,CAAK,IAAID,CAAK,EAAE,EAEtCR,EAAW,MAAM,MAAMY,EAAK,CAChC,OAAQ,OACR,QAAS,CACP,OAAQ,mBACR,eAAgB,mBAChB,cAAe,SAASC,CAAW,EACrC,EACA,KAAM,KAAK,UAAUF,CAAW,CAClC,CAAC,EAED,aAAMZ,GAAaC,EAAU,+BAA+B,EAIrD,CACL,QAAS,GACT,QAJe,MAAMA,EAAS,KAAK,CAKrC,CACF,OAASc,EAAO,CACd,MAAAX,EAAO,MAAM,CAAE,MAAAW,CAAM,EAAG,6BAA6B,EAE/CA,CACR,CACF,EAOaC,GAAqB,MAAOT,GAA+C,CACtF,GAAI,CACF,GAAM,CAAE,QAAAC,EAAS,MAAAC,EAAO,MAAAC,EAAO,UAAAC,CAAU,EAAIJ,EAEvCM,EAAM,GAAGL,CAAO,uBAAuBG,CAAS,YAChDG,EAAc,KAAK,GAAGJ,CAAK,IAAID,CAAK,EAAE,EAEtCR,EAAW,MAAM,MAAMY,EAAK,CAChC,OAAQ,MACR,QAAS,CACP,OAAQ,mBACR,cAAe,SAASC,CAAW,EACrC,CACF,CAAC,EAED,aAAMd,GAAaC,EAAU,qCAAqC,EAEhD,MAAMA,EAAS,KAAK,CAGxC,OAASc,EAAO,CACd,MAAAX,EAAO,MAAM,CAAE,MAAAW,CAAM,EAAG,qCAAqC,EAEvDA,CACR,CACF,EAQaE,GAAoB,MAAOC,EAAqBX,IAAoD,CAC/G,GAAI,CAMF,OALiB,MAAMS,GAAmBT,CAAM,GACvB,KAAMY,GACtBA,EAAE,OAASD,CACnB,GAEiB,IACpB,OAASH,EAAO,CACd,MAAAX,EAAO,MAAM,CAAE,MAAAW,EAAO,YAAAG,CAAY,EAAG,oCAAoC,EAEnEH,CACR,CACF,EAQaK,GAAoB,MAC/Bd,EACAC,IACqC,CACrC,GAAI,CACF,GAAM,CAAE,QAAAC,EAAS,MAAAC,EAAO,MAAAC,CAAM,EAAIH,EAG5BK,EAAmC,CAAC,EAEtCN,EAAO,WAAa,SAAWM,EAAY,SAAWN,EAAO,UAC7DA,EAAO,WAAa,SAAWM,EAAY,SAAWN,EAAO,UAC7DA,EAAO,cAAgB,SAAWM,EAAY,YAAcN,EAAO,aACnEA,EAAO,cAAgB,SAAWM,EAAY,YAAcN,EAAO,aAEvE,IAAMO,EAAM,GAAGL,CAAO,uBAAuBF,EAAO,SAAS,GACvDQ,EAAc,KAAK,GAAGJ,CAAK,IAAID,CAAK,EAAE,EAEtCR,EAAW,MAAM,MAAMY,EAAK,CAChC,OAAQ,MACR,QAAS,CACP,OAAQ,mBACR,eAAgB,mBAChB,cAAe,SAASC,CAAW,EACrC,EACA,KAAM,KAAK,UAAUF,CAAW,CAClC,CAAC,EAED,aAAMZ,GAAaC,EAAU,+BAA+B,EAIrD,CACL,QAAS,GACT,QAJe,MAAMA,EAAS,KAAK,CAKrC,CACF,OAASc,EAAO,CACd,MAAAX,EAAO,MAAM,CAAE,MAAAW,CAAM,EAAG,6BAA6B,EAE/CA,CACR,CACF,EASaM,GAAqB,MAChCf,EACAC,IACsC,CACtC,GAAI,CACF,GAAM,CAAE,YAAAW,CAAY,EAAIZ,EAGlBgB,EAAU,MAAML,GAAkBC,EAAaX,CAAM,EAE3D,GAAI,CAACe,EACH,MAAAlB,EAAO,MAAM,CAAE,YAAAc,CAAY,EAAG,wBAAwB,EAChD,IAAI,MAAM,YAAYA,CAAW,6BAA6B,EAatE,OATe,MAAME,GACnB,CACE,UAAWE,EAAQ,GACnB,SAAU,GACV,YAAa,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,CACpD,EACAf,CACF,CAGF,OAASQ,EAAO,CACd,MAAAX,EAAO,MAAM,CAAE,MAAAW,CAAM,EAAG,+BAA+B,EACjDA,CACR,CACF,EAOaQ,GAAiB,SAAiC,CAC7D,IAAMf,EAAUgB,GAAQ,IAAI,cACtBf,EAAQe,GAAQ,IAAI,YAAcA,GAAQ,IAAI,eAC9CC,EAAeD,GAAQ,IAAI,gBAC3Bd,EAAQc,GAAQ,IAAI,WAEpBE,EAAwB,CAAC,EAO/B,GALKlB,GAASkB,EAAY,KAAK,yDAAyD,EACnFjB,GAAOiB,EAAY,KAAK,oDAAoD,EAC5ED,GAAcC,EAAY,KAAK,sCAAsC,EACrEhB,GAAOgB,EAAY,KAAK,sCAAsC,EAE/DA,EAAY,OAAS,EAAG,CAC1B,IAAMC,EAAe,CACnB,iDACA,wDACA,GAAGD,EAAY,IAAKP,GACX,OAAOA,CAAC,EAChB,EACD,GACA,kEACF,EAAE,KAAK;AAAA,CAAI,EAEX,MAAM,IAAI,MAAMQ,CAAY,CAC9B,CAEA,IAAMhB,EAAY,OAAO,SAASc,EAAe,EAAE,EAEnD,GAAI,OAAO,MAAMd,CAAS,EACxB,MAAM,IAAI,UAAU,6BAA6Bc,CAAY,yCAAyC,EAGxG,MAAO,CACL,QAASjB,EAAS,QAAQ,MAAO,EAAE,EACnC,MAAOC,EACP,UAAAE,EACA,MAAOD,CACT,CACF,EAOakB,GAAyB,SAAwC,CAC5E,GAAI,CAGF,OAFe,MAAML,GAAe,CAGtC,OAASR,EAAO,CACd,OAAAX,EAAO,KAAK,CAAE,MAAAW,CAAM,EAAG,6DAA6D,EAE7E,IACT,CACF,EDvRO,IAAMc,GAAU,MAQVC,GAAiBC,GACrBA,IAAS,SAAW,OAAS,MAezBC,GAAuB,MAAOD,EAAoB,YAA6B,CAC1F,IAAME,EAAaH,GAAcC,CAAI,EAErCG,GAAE,MAAQ,GAEV,MAAMA,qBACN,MAAMA,gBAAeD,CAAU,GAC/B,MAAMC,qBAAoBD,CAAU,GAEpCC,GAAE,MAAQ,EACZ,EAYaC,GAAsB,MAAOC,GAAkE,CAC1G,GAAM,CAAE,GAAAC,EAAI,WAAAC,EAAY,YAAAC,EAAa,KAAAR,EAAO,SAAU,EAAIK,EAGpDI,EAAcC,EAAeJ,CAAE,EAE/BK,EAAS,MAAMC,GACnB,CACE,KAAMH,EACN,UAAWF,EAAW,UACtB,YAAaC,GAAe,GAC5B,SAAU,GACV,SAAU,EACZ,EACAD,CACF,EAGMM,EAAiB,GAAGN,EAAW,OAAO,aAAaI,EAAO,QAAS,SAAS,aAAaA,EAAO,QAAS,EAAE,iCAG3GG,EAAc,MAAMC,GAAoB,CAAE,GAAAT,EAAI,eAAAO,EAAgB,KAAAb,EAAM,YAAAQ,CAAY,CAAC,EAEvF,MAAO,CACL,QAASQ,EAAaV,CAAE,EACxB,KAAAN,EACA,WAAYc,EAAY,WACxB,MAAOA,EAAY,MACnB,eAAAD,CACF,CACF,EAMaI,EAAsB,SAA0C,CAC3E,IAAMC,EAAe,IAAI,IAEnBX,EAAa,MAAMY,GAAuB,EAEhD,GAAI,CAACZ,EAAY,OAAOW,EAExB,GAAI,CACF,IAAME,EAAW,MAAMC,GAAmBd,CAAU,EAEpD,QAAWe,KAAWF,EAChBE,EAAQ,aACVJ,EAAa,IAAII,EAAQ,KAAMA,EAAQ,WAAW,CAGxD,MAAQ,CAER,CAEA,OAAOJ,CACT,EAMaK,GAAqB,CAACD,EAAiBtB,EAAmBwB,IAAsC,CAC3G,IAAMC,EAAUD,EAAmB,IAAI,OAAOA,EAAmBF,EAAQ,OAAS,CAAC,EAAI,MACjFI,EAAM,IAAI1B,CAAI,IAAI,OAAO,EAAE,EAEjC,MAAO,GAAGsB,CAAO,GAAGG,CAAO,GAAGC,CAAG,EACnC,EAMaC,EAAqBC,GACzBA,EAAM,YAAY,EAAE,WAAW,QAAQ,EAAI,SAAW,UAoBlDC,GAAsBC,GAC1BA,EAAS,QAASC,GAAW,CAClC,IAAMzB,EAAK0B,EAAgBD,CAAM,EAEjC,OAAKzB,EAEE,CAAC,CAAE,OAAAyB,EAAQ,GAAAzB,EAAI,MAAOU,EAAaV,CAAE,CAAE,CAAC,EAF/B,CAAC,CAGnB,CAAC,EAQU2B,GAAwBC,GAA+B,CAClE,GAAI,CACF,OAAOC,GAAiBC,GAAgBF,CAAU,CAAC,CACrD,OAASG,EAAO,CACd,MAAM,IAAIC,EAAeD,EAAO,CAC9B,UAAW,wBAAwBH,CAAU,IAC7C,YAAa,4EACf,CAAC,CACH,CACF,EAOaK,GAA0BR,GAA2B,CAChE,GAAIA,IAAWjC,GAAS,OAAOA,GAE/B,IAAMQ,EAAK0B,EAAgBD,CAAM,EAEjC,OAAOzB,EAAKU,EAAaV,CAAE,EAAIyB,CACjC,EAMaS,GAAuBV,GAC3BA,EAAS,QAASC,GAAW,CAClC,IAAMzB,EAAK0B,EAAgBD,CAAM,EAEjC,OAAOzB,EAAK,CAACU,EAAaV,CAAE,CAAC,EAAI,CAAC,CACpC,CAAC,EAGGmC,GAAmB,CAACV,EAAgBW,IACjCA,EAAQA,EAAM,IAAIX,CAAM,GAAK,UAAY,OAOrCY,EAA2BtC,GAAsD,CAC5F,GAAM,CAAE,SAAAyB,EAAU,aAAAZ,EAAc,MAAAwB,CAAM,EAAIrC,EAE1C,OAAOwB,GAAmBC,CAAQ,EAAE,IAAI,CAAC,CAAE,OAAAC,EAAQ,GAAAzB,EAAI,MAAAsC,CAAM,KACpD,CACL,MAAOb,EACP,MAAAa,EAEA,YAAa1B,EAAa,IAAIR,EAAeJ,CAAE,CAAC,EAChD,KAAMmC,GAAiBV,EAAQW,CAAK,CACtC,EACD,CACH,EDlMA,IAAMG,GAAkBC,GACfA,EACJ,IAAKC,IACG,CAAE,GAAAA,EAAI,GAAIC,EAAgBD,EAAG,WAAW,CAAE,EAClD,EACA,OAAQE,GACAA,EAAM,KAAO,IACrB,EACA,KAAK,CAACC,EAAGC,IACDC,GAAkBF,EAAE,GAAIC,EAAE,GAAI,CAAE,EAAGD,EAAE,GAAG,UAAW,EAAGC,EAAE,GAAG,SAAU,CAAC,CAC9E,EACA,IAAKF,GACGA,EAAM,EACd,EAQCI,GAAqB,SAAkC,CAC3D,IAAMC,EACJ,MAAMC,iHAEFC,EACJ,MAAMD,iHAEFE,EAAmB,CAAC,GAAG,KAAK,MAAMH,EAAW,MAAM,EAAG,GAAG,KAAK,MAAME,EAAU,MAAM,CAAC,EAGrFE,EAAO,IAAI,IAEjB,OAAOD,EAAI,OAAQV,GACbW,EAAK,IAAIX,EAAG,WAAW,EAAU,IAErCW,EAAK,IAAIX,EAAG,WAAW,EAEhB,GACR,CACH,EAOMY,GAAuB,SAAkC,CAC7D,GAAI,CACF,IAAMb,EAAM,MAAMO,GAAmB,EAErC,GAAIP,EAAI,SAAW,EACjB,MAAM,IAAIc,EAAe,OAAW,CAClC,UAAW,wBACX,YAAa,6DACf,CAAC,EAGH,OAAOf,GAAeC,CAAG,CAC3B,OAASe,EAAO,CACd,MAAIA,aAAiBD,EAAsBC,GAE3CC,EAAO,MAAM,CAAE,MAAAD,CAAM,EAAG,mCAA8B,EAEhD,IAAID,EAAeC,EAAO,CAAE,UAAW,mBAAoB,CAAC,EACpE,CACF,EAUaE,GAAgB,UACf,MAAMJ,GAAqB,GAE5B,IAAKZ,GACPA,EAAG,WACX,EASUiB,EAAwB,UACvB,MAAML,GAAqB,GAE5B,IAAKZ,IACP,CACL,OAAQA,EAAG,YACX,MAAOA,EAAG,MACV,UAAWA,EAAG,SAChB,EACD,EAWUkB,GAAsB,MAAOC,GAAiD,CACzF,GAAM,CAAE,OAAAC,EAAQ,KAAAC,CAAK,EAAIF,EAEzB,GAAI,CACFX,EAAE,MAAQ,GACV,MAAMA,eAAeY,CAAM,WAAWC,CAAI,GAC1Cb,EAAE,MAAQ,EACZ,OAASM,EAAgB,CACvB,MAAAC,EAAO,MAAM,CAAE,MAAAD,EAAO,OAAAM,CAAO,EAAG,sCAAsCA,CAAM,EAAE,EACxEN,CACR,CACF,EAUaQ,GAAsB,MACjCH,GACmD,CACnD,GAAM,CAAE,GAAAI,EAAI,eAAAC,EAAgB,KAAAC,EAAM,YAAAC,CAAY,EAAIP,EAC5CQ,EAAUC,GAAcL,EAAIE,CAAI,EAChCI,EAAaC,GAAcL,CAAI,EAE/BM,EAAaC,GAAiBT,CAAE,EAEhCF,EAAOK,GAAeA,EAAY,KAAK,IAAM,GAAK,GAAGF,CAAc;AAAA;AAAA,EAAOE,CAAW,GAAK,GAAGF,CAAc;AAAA,EAEjH,GAAI,CACFhB,EAAE,MAAQ,GAEV,MAAMA,eAAeqB,CAAU,GAC/B,MAAMrB,oBAAoBqB,CAAU,GACpC,MAAMrB,oBAAoBuB,CAAU,GACpC,MAAMvB,uBAAuBuB,CAAU,GACvC,MAAMvB,+DACN,MAAMA,oBAAoBuB,CAAU,GAKpC,IAAME,GAFW,MAAMzB,yBAAyBmB,CAAO,WAAWN,CAAI,WAAWQ,CAAU,WAAWE,CAAU,IAExF,OAAO,KAAK,EAEpC,aAAMvB,eAAeqB,CAAU,GAE/BrB,EAAE,MAAQ,GAEH,CACL,WAAAuB,EACA,MAAOE,CACT,CACF,OAASnB,EAAgB,CACvB,MAAAC,EAAO,MAAM,CAAE,MAAAD,EAAO,WAAAiB,CAAW,EAAG,iCAAiCA,CAAU,EAAE,EAE3EjB,CACR,CACF,EGjLO,IAAMoB,EAA0B,MAAOC,GAAqD,CACjG,GAAM,CAAE,UAAAC,EAAW,eAAAC,CAAe,EAAIF,EAEtC,GAAI,MAAMG,GAAuB,EAC/B,MAAM,IAAIC,EAAe,OAAW,CAClC,UAAAH,EACA,YAAa,wEACb,cAAe,2CACjB,CAAC,EAGH,GAAIC,EAAgB,CAClB,IAAMG,EAAgB,MAAMC,GAAiB,EAE7C,GAAID,IAAkBH,EACpB,MAAM,IAAIE,EAAe,OAAW,CAClC,UAAAH,EACA,YAAa,aAAaC,CAAc,sBAAsBA,CAAc,IAC5E,cAAe,sBAAsBG,CAAa,gBAAgBH,CAAc,GAClF,CAAC,CAEL,CAEA,GAAI,CAAE,MAAMK,GAAmB,EAC7B,MAAM,IAAIH,EAAe,OAAW,CAClC,UAAAH,EACA,YAAa,2CACb,cAAe,sCACjB,CAAC,CAEL,ECtDA,OAAOO,OAAa,eCmBb,IAAMC,GAAN,cAAmC,KAAM,CACrC,KAAO,uBAEhB,aAAc,CACZ,MAAM,kBAAkB,CAC1B,CACF,ECxBA,OAAOC,OAAa,eAcb,IAAMC,GAAa,CAAE,QAAS,EAAM,EAO9BC,GAAO,CAClBC,EACAC,EAAiCC,GAAS,CACxCL,GAAQ,OAAO,MAAMK,CAAI,CAC3B,KAEIJ,GAAW,SAAWE,GAAUA,EAAO,mBAAqB,MAC9DC,EAAM,GAAG,KAAK,UAAUD,EAAO,kBAAmB,KAAM,CAAC,CAAC;AAAA,CAAI,EAGzDA,GAIIG,GAAiBC,GAAuB,CAC5BA,EAAI,QAAQ,KAAMC,GAChCA,EAAO,OAAS,QACxB,GAGCD,EAAI,OAAO,SAAU,4EAA4E,EAGnGA,EAAI,SAAS,QAAQD,EAAa,CACpC,EFlBA,IAAMG,GAAoB,IAAM,CAC9B,GAAI,CAACC,GAAQ,MAAM,OAASC,GAAW,QACrC,MAAM,IAAIC,EAAe,OAAW,CAClC,UAAW,+BACX,YACE,mKACJ,CAAC,CAEL,EASaC,GAAoB,MAAOC,GAA+C,CACrFL,GAAkB,EAClB,GAAM,CAAE,gBAAAM,CAAgB,EAAI,KAAM,QAAO,oBAAc,EACjDC,EAAQ,MAAMD,EAAgBD,CAAK,EAEzC,GAAIE,IAAU,KAAM,MAAM,IAAIC,GAE9B,OAAOD,CACT,EAOaE,GAAsB,MACjCJ,EACAK,IACsB,CACtBV,GAAkB,EAClB,GAAM,CAAE,qBAAAW,CAAqB,EAAI,KAAM,QAAO,oBAAc,EACtDC,EAAS,MAAMD,EAAqBN,EAAOK,CAAI,EAErD,GAAIE,IAAW,KAAM,MAAM,IAAIJ,GAE/B,OAAOI,CACT,EN9CO,IAAMC,GAAa,MAAOC,GAAyB,CACxD,GAAM,CAAE,IAAAC,EAAK,iBAAAC,CAAiB,EAAIF,EAElC,MAAMG,EAAwB,CAAE,UAAW,kCAAmC,eAAgB,KAAM,CAAC,EAIrG,IAAMC,GADS,MAAMC,EAAsB,GAExC,OAAQC,GACAC,EAAkBD,EAAG,KAAK,IAAM,SACxC,EACA,IAAKA,GACGA,EAAG,MACX,EAEH,GAAIF,EAAe,SAAW,EAC5B,OAAAI,EAAO,KAAK,6CAAmC,EAE/CC,EAAY,MAAM,EAEX,CACL,QAASC,EACP,KAAK,UAAU,CAAE,iBAAkB,EAAG,aAAc,EAAG,eAAgB,CAAC,EAAG,cAAe,CAAE,EAAG,KAAM,CAAC,CACxG,EACA,kBAAmB,CAAE,iBAAkB,EAAG,aAAc,EAAG,eAAgB,CAAC,EAAG,cAAe,CAAE,CAClG,EAGF,IAAIC,EAAoC,CAAC,EAEzC,GAAIV,EACFU,EAA0BP,MACrB,CACLK,EAAY,eAAe,EAE3B,IAAMG,EAAe,MAAMC,EAAoB,EAE/CF,EAA0B,MAAMG,GAC9BC,EAAwB,CAAE,SAAUX,EAAgB,aAAAQ,CAAa,CAAC,EAClE,CAAE,SAAU,EAAK,CACnB,CACF,CAGoBD,EAAwB,SAAWP,EAAe,OAGpEK,EAAY,UAAU,QAAS,EAAI,EAEnCA,EAAY,UAAU,aAAcO,GAAoBL,CAAuB,CAAC,EAGlF,MAAMM,EACJf,EACA,2DAA2DS,EAAwB,KAAK,IAAI,CAAC,GAC/F,EAGKT,GACHO,EAAY,UAAU,QAAS,EAAI,EAGrCS,EAAE,MAAQ,GAEV,MAAMA,oBACN,MAAMA,kBACN,MAAMA,uBAEN,IAAMC,EAA2B,CAAC,EAGlC,QAAWC,KAAUT,EACH,MAAMU,GAASD,CAAM,GAGnCD,EAAe,KAAKC,CAAM,EAM9B,GAFAF,EAAE,MAAQ,GAENC,EAAe,OAAS,EAAG,CAC7BX,EAAO,KAAK;AAAA,gBAASW,EAAe,MAAM;AAAA,CAA8C,EACxFX,EAAO,KAAK,oDAA6C,EACzD,QAAWY,KAAUD,EACnBX,EAAO,KACL,oBAAoBY,CAAM;AAAA;AAAA,aAAgDA,CAAM,uBAAuBA,CAAM;AAAA,kBAA6CA,CAAM;AAAA,CAClK,EAEFZ,EAAO,KACL,UAAKG,EAAwB,OAASQ,EAAe,MAAM,IAAIR,EAAwB,MAAM,iCAC/F,CACF,MACEH,EAAO,KAAK;AAAA,CAAwC,EAGtDC,EAAY,MAAM,EAElB,IAAMa,EAAoB,CACxB,iBAAkBX,EAAwB,OAASQ,EAAe,OAClE,aAAcA,EAAe,OAC7B,eAAAA,EACA,cAAeR,EAAwB,MACzC,EAEA,MAAO,CACL,QAASD,EAAY,KAAK,UAAUY,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAEMD,GAAW,MAAOD,GAAqC,CAC3D,GAAI,CACF,aAAMF,eAAeE,CAAM,GAE3B,MAAMF,oBAAoBE,CAAM,GAEhC,MAAMF,kCAEN,MAAMA,oBAAoBE,CAAM,GAEhC,MAAMF,kBAENV,EAAO,KAAK,gCAAgCY,CAAM,EAAE,EAE7C,EACT,OAASG,EAAgB,CACvB,IAAMC,EAAM,IAAIC,EAAeF,EAAO,CACpC,UAAW,kBAAkBH,CAAM,GACnC,YAAa,8DACf,CAAC,EAED,OAAAZ,EAAO,MAAM,CAAE,MAAAe,EAAO,OAAAH,EAAQ,IAAKI,EAAI,OAAQ,CAAC,EAEhD,MAAMN,4BAEC,EACT,CACF,EAGaQ,GAAoBC,EAAc,CAC7C,KAAM,eACN,YACE,8WACF,YAAa,CACX,IAAKC,GACF,QAAQ,EACR,SAAS,EACT,SACC,6HACF,CACJ,EACA,aAAc,CACZ,iBAAkBA,GAAE,OAAO,EAAE,SAAS,6BAA6B,EACnE,aAAcA,GAAE,OAAO,EAAE,SAAS,yBAAyB,EAC3D,eAAgBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,uCAAuC,EACpF,cAAeA,GAAE,OAAO,EAAE,SAAS,oCAAoC,CACzE,EACA,QAAS7B,EACX,CAAC,ESzLD,OAAS,KAAA8B,OAAS,MAClB,OAAS,KAAAC,MAAS,KCUlB,IAAMC,GAAoB,CAACC,EAAgBC,IAAoC,CAC7E,GAAI,SAAOD,GAAU,UAAYA,EAAM,SAAW,GAElD,OAAOA,EAAM,MAAM,CAACC,CAAG,EAAE,KAAK,CAChC,EAUaC,GAAiBC,GAAkC,CAC9D,GAAIA,IAAU,MAAQ,OAAOA,GAAU,SACrC,MAAO,CAAE,QAAS,OAAOA,CAAK,CAAE,EAGlC,IAAMC,EAAMD,EACNE,EAAwB,CAAC,EAE3BF,aAAiB,OACnBE,EAAO,KAAOF,EAAM,KACpBE,EAAO,QAAUF,EAAM,SACd,OAAOC,EAAI,SAAY,WAChCC,EAAO,QAAUD,EAAI,SAGvB,IAAME,EAAWF,EAAI,UAEjB,OAAOE,GAAa,UAAYA,IAAa,QAAMD,EAAO,SAAWC,GAEzE,IAAMC,EAASR,GAAkBK,EAAI,OAAQ,GAAwB,EAEjEG,IAAQF,EAAO,OAASE,GAE5B,IAAMC,EAAST,GAAkBK,EAAI,OAAQ,GAAwB,EAErE,OAAII,IAAQH,EAAO,OAASG,GAErBH,CACT,ECrDA,OAAS,KAAAI,OAAS,KAuBX,IAAMC,GAAkB,MAAOC,GAAiD,CACrF,GAAM,CAAE,SAAAC,EAAU,YAAAC,EAAa,SAAAC,EAAU,YAAAC,EAAc,EAAM,EAAIJ,EAE3DK,EAAU,MAAM,QAAQ,WAC5BJ,EAAS,IAAI,MAAOK,GAAW,CAC7B,IAAMC,EAAe,GAAGL,CAAW,IAAII,CAAM,GAEvCE,EAAQC,GAAwB,CAAE,SAAAN,EAAU,OAAAG,CAAO,CAAC,EAE1D,aAAMI,GAA0BF,CAAK,EAErC,MAAMG,yBAAwBJ,CAAY,GAEnCD,CACT,CAAC,CACH,EAEMM,EAAoB,CAAC,EAE3B,OAAW,CAACC,EAAOC,CAAM,IAAKT,EAAQ,QAAQ,EAC5C,GAAIS,EAAO,SAAW,YACpBF,EAAQ,KAAKE,EAAO,KAAK,MACpB,CACL,IAAMR,EAASL,EAASY,CAAK,EACvBE,EAAM,IAAIC,EAAeF,EAAO,OAAQ,CAC5C,UAAW,uBAAuBR,CAAM,GACxC,YAAa,2EACf,CAAC,EAEDW,EAAO,MAAM,CAAE,MAAOH,EAAO,OAAQ,IAAKC,EAAI,OAAQ,CAAC,CACzD,CAOF,OAAIX,GAAeQ,EAAQ,SAAWX,EAAS,QAC7C,MAAMU,uBAGDC,CACT,EFhBA,IAAMM,GAAU,MAAUC,EAAmBC,EAAqBC,IAAqC,CACrG,GAAI,CACF,OAAO,MAAMA,EAAG,CAClB,OAASC,EAAO,CACd,MAAAC,EAAO,MAAM,CAAE,IAAKC,GAAcF,CAAK,CAAE,EAAG,oBAAeH,CAAS,EAAE,EAChE,IAAIM,EAAeH,EAAO,CAAE,UAAAH,EAAW,YAAAC,CAAY,CAAC,CAC5D,CACF,EAOMM,GAAgB,MAAOC,GAA2C,CACtE,IAAMC,EAAS,MAAMC,sBAAsBF,CAAI,mDAG/C,OAFY,KAAK,MAAMC,EAAO,MAAM,EAEzB,CAAC,GAAK,IACnB,EAQME,GAA4B,MAAOC,GAA4C,CACnF,IAAMC,EAAgBC,GAAcF,CAAE,EAChCH,EAAS,MAAMC,yFAMrB,OALY,KAAK,MAAMD,EAAO,MAAM,EAClB,KAAMM,GACfA,EAAG,QAAUF,CACrB,GAEe,IAClB,EAQMG,GAAuB,SAAsC,CACjE,IAAMP,EAAS,MAAMC,sFAGrB,OAFY,KAAK,MAAMD,EAAO,MAAM,EAEzB,CAAC,GAAK,IACnB,EAOMQ,GAA2B,MAAOC,GAA6C,CACnF,IAAMC,EAAwBC,GAAqBF,CAAO,EACpDH,EAAK,MAAMR,GAAcY,CAAqB,EAEpD,GAAI,CAACJ,EACH,MAAAX,EAAO,MAAM,iCAA4Be,CAAqB,GAAG,EAC3D,IAAIb,EAAe,OAAW,CAClC,UAAW,mBAAmBa,CAAqB,GACnD,YAAa,2CAA2CA,CAAqB,gBAC/E,CAAC,EAGH,MAAO,CAAE,sBAAAA,EAAuB,eAAgBJ,EAAG,KAAM,CAC3D,EAEMM,GAA6B,SAAqC,CACtE,IAAMC,EAAiB,MAAMC,EAAsB,EAE7CC,EAAWF,EAAe,IAAKP,GAC5BA,EAAG,MACX,EAEKU,EAAe,IAAI,IACvBH,EAAe,IAAKP,GACX,CAACA,EAAG,OAAQW,EAAkBX,EAAG,KAAK,CAAC,CAC/C,CACH,EAEAY,EAAY,eAAe,EAE3B,IAAMC,EAAe,MAAMC,EAAoB,EAEzCV,EAAwB,MAAMW,GAClCC,EAAwB,CAAE,SAAAP,EAAU,aAAAI,EAAc,MAAOH,CAAa,CAAC,CACzE,EAEMO,EAASV,EAAe,KAAMP,GAC3BA,EAAG,SAAWI,CACtB,EAED,GAAI,CAACa,EACH,MAAA5B,EAAO,MAAM,yBAAoBe,CAAqB,yBAAyB,EACzE,IAAIb,EAAe,OAAW,CAClC,UAAW,mBAAmBa,CAAqB,GACnD,YAAa,iCAAiCA,CAAqB,iBACrE,CAAC,EAGH,MAAO,CAAE,sBAAAA,EAAuB,eAAgBa,EAAO,KAAM,CAC/D,EAQMC,GAAiC,MAAOC,GAAyC,CAGrF,GAAI,EAFqB,MAAMC,EAAoB,SAAS,GAEtC,SAASD,CAAa,EAAG,OAE/C,GAAM,CAACE,EAAaC,CAAQ,EAAI,MAAM,QAAQ,IAAI,CAACC,EAAe,EAAGC,EAAY,CAAC,CAAC,EAC7EC,EAAc,GAAGJ,CAAW,GAAGK,CAAoB,GAIzD,IAFgB,MAAMC,GAAgB,CAAE,SAAU,CAACR,CAAa,EAAG,YAAAM,EAAa,SAAAH,CAAS,CAAC,GAE9E,SAAW,EACrB,MAAM,IAAI/B,EAAe,OAAW,CAClC,UAAW,uBAAuB4B,CAAa,gBAC/C,YAAa,qCAAqCM,CAAW,IAAIN,CAAa,uCAChF,CAAC,CAEL,EAOMS,GAAiB,MAAOC,GAA4C,CACxE,GAAM,CAAE,sBAAAzB,EAAuB,YAAA0B,CAAY,EAAID,EAEzCE,EAAcD,IAAgB,SAAW,OAAS,MAElDE,EAAY,MAAMxC,GAAcY,CAAqB,EAE3D,GAAI,CAAC4B,EACH,MAAM,IAAIzC,EAAe,OAAW,CAClC,UAAW,0BAA0Ba,CAAqB,GAC1D,YAAa,gCACf,CAAC,EAGH,GAAI4B,EAAU,QAAU,SAAU,CAChC3C,EAAO,KAAK,qBAAgBe,CAAqB,iCAA4B,EAE7E,MACF,CAEA,GAAI4B,EAAU,QAAU,SACtB,MAAM,IAAIzC,EAAe,OAAW,CAClC,UAAW,oBAAoBa,CAAqB,SAAS2B,CAAW,GACxE,YAAa,mEACf,CAAC,EAGH,MAAM/C,GACJ,oBAAoBoB,CAAqB,SAAS2B,CAAW,GAC7D,qBAAqB3B,CAAqB,0CAC1C,SAAY,CACV,MAAMT,gBAAgBS,CAAqB,mCAC7C,CACF,CACF,EAEM6B,GAAoB,MAAOpC,GAAmC,CAClE,IAAMqC,EAAgBC,EAAatC,CAAE,EAC/BC,EAAgBC,GAAcF,CAAE,EAChCuC,EAAe,MAAMnC,GAAqB,EAMhD,GAAImC,EAAc,CAChB,IAAMC,EAAWD,EAAa,OAE9B,OAAIA,EAAa,QAAUtC,IACzBT,EAAO,KACL,qCAAgCgD,CAAQ,MAAMD,EAAa,KAAK,wBAAwBF,CAAa,EACvG,EACA,MAAMlD,GACJ,+BAA0BqD,CAAQ,QAAQvC,CAAa,IACvD,+BAA+BuC,CAAQ,aAAavC,CAAa,IACjE,SAAY,CACV,MAAMH,eAAe0C,CAAQ,YAAYvC,CAAa,EACxD,CACF,GAGKuC,CACT,CAEA,MAAMrD,GACJ,sCAAiCkD,CAAa,GAC9C,4HACA,SAAY,CACV,MAAMvC,gDAAgDG,CAAa,YACrE,CACF,EAEA,IAAMwC,EAAU,MAAMrC,GAAqB,EAE3C,GAAI,CAACqC,EACH,MAAM,IAAI/C,EAAe,OAAW,CAClC,UAAW,qBAAqB2C,CAAa,GAC7C,YAAa,oEACf,CAAC,EAGH,OAAOI,EAAQ,MACjB,EAEMC,GAAmB,MAAO1C,GAAiC,CAC/D,IAAMqC,EAAgBC,EAAatC,CAAE,EAGrC,GAFsB,MAAMD,GAA0BC,CAAE,EAErC,CACjBR,EAAO,KAAK,oBAAe6C,CAAa,2CAAsC,EAE9E,MACF,CAEA,IAAMG,EAAW,MAAMJ,GAAkBpC,CAAE,EAE3C,MAAMb,GACJ,gBAAgBqD,CAAQ,0BAAqBH,CAAa,GAC1D,qBAAqBG,CAAQ,0CAC7B,SAAY,CACV,MAAM1C,gBAAgB0C,CAAQ,mBAChC,CACF,CACF,EAEMG,GAAyB,SAA2B,CACxD7C,EAAE,MAAQ,GAEV,MAAMX,GACJ,uCACA,mFACA,SAAY,CACV,MAAMW,gEACR,CACF,EAEAA,EAAE,MAAQ,EACZ,EAEM8C,GAAkB,SAA2B,CACjD,MAAMzD,GACJ,0BACA,kHACA,SAAY,CACV,MAAMW,oGACR,CACF,CACF,EAYM+C,GAA+B,MAAOC,GAAkC,CAC5E,GAAI,CACF,MAAMC,GAAkBD,CAAM,CAChC,OAASvD,EAAO,CACdC,EAAO,KAAK,CAAE,IAAKC,GAAcF,CAAK,CAAE,EAAG,iCAAiCuD,CAAM,iBAAiB,CACrG,CAEA,GAAI,CACF,MAAME,GAAmBF,CAAM,CACjC,OAASvD,EAAO,CACdC,EAAO,KAAK,CAAE,IAAKC,GAAcF,CAAK,CAAE,EAAG,kCAAkCuD,CAAM,iBAAiB,CACtG,CACF,EAEMG,GAA2B,MAAOjD,GAAiC,CACvE,IAAMkD,EAAa,MAAMC,GAAuB,EAEhD,GAAI,CAACD,EAAY,CACf1D,EAAO,KAAK,kEAA2D,EAEvE,MACF,CAEA,GAAI,CAEF,IAAM4D,EAAcC,EAAerD,CAAE,EAErC,MAAMsD,GAAmB,CAAE,YAAAF,CAAY,EAAGF,CAAU,CACtD,OAAS3D,EAAO,CACdC,EAAO,MAAM,CAAE,IAAKC,GAAcF,CAAK,CAAE,EAAG,+CAA+C,CAC7F,CACF,EASagE,GAAmB,MAAOvB,GAA+B,CACpE,GAAM,CAAE,QAAA1B,EAAS,iBAAAkD,CAAiB,EAAIxB,EAItC,MAAMyB,EAAwB,CAAE,UAAW,iBAAkB,CAAC,EAE9D,GAAM,CAAE,sBAAAlD,EAAuB,eAAAmD,CAAe,EAAIpD,EAC9C,MAAMD,GAAyBC,CAAO,EACtC,MAAMG,GAA2B,EAI/BkD,EAAYC,EAAgBrD,CAAqB,EAEvD,GAAI,CAACoD,EACH,MAAM,IAAIjE,EAAe,OAAW,CAClC,UAAW,mBAAmBa,CAAqB,GACnD,YAAa,4EACf,CAAC,EAGH,IAAMsD,EAAkBvB,EAAaqB,CAAS,EAE9C5C,EAAY,UAAU,YAAa8C,CAAe,EAClDrE,EAAO,KAAK,cAAcmE,EAAU,OAAS,OAAS,gBAAkB,SAAS,IAAIpD,CAAqB,EAAE,EAE5G,IAAM0B,EAA2BnB,EAAkB4C,CAAc,EAEjE,MAAMI,EACJN,EACA,4CAA4CjD,CAAqB,iBACnE,EAGAQ,EAAY,UAAU,QAAS,EAAI,EAEnCjB,EAAE,MAAQ,GAEV,MAAMuB,GAA+Bd,CAAqB,EAC1D,MAAMwB,GAAe,CAAE,sBAAAxB,EAAuB,YAAA0B,CAAY,CAAC,EAEvDA,IAAgB,UAClB,MAAMS,GAAiBiB,CAAS,EAGlC,MAAMhB,GAAuB,EAC7B,MAAMC,GAAgB,EACtB,MAAMC,GAA6BtC,CAAqB,EAExDT,EAAE,MAAQ,GAEV,MAAMmD,GAAyBU,CAAS,EAExCnE,EAAO,KAAK,0BAA0Be,CAAqB,iBAAiB,EAE5EQ,EAAY,MAAM,EAElB,IAAMgD,EAAoB,CACxB,cAAexD,EACf,QAASsD,EACT,KAAM5B,EACN,QAAS,EACX,EAEA,MAAO,CACL,QAAS+B,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAGaE,GAA0BC,EAAc,CACnD,KAAM,qBACN,YACE,wyBACF,YAAa,CACX,QAASC,GACN,OAAO,EACP,SACC,yIACF,CACJ,EACA,aAAc,CACZ,cAAeA,GAAE,OAAO,EAAE,SAAS,uCAAuC,EAC1E,QAASA,GAAE,OAAO,EAAE,SAAS,gCAAgC,EAC7D,KAAMA,GAAE,KAAK,CAAC,UAAW,QAAQ,CAAC,EAAE,SAAS,cAAc,EAC3D,QAASA,GAAE,QAAQ,EAAE,SAAS,qCAAqC,CACrE,EACA,QAASZ,EACX,CAAC,EGtcD,OAAOa,OAAa,oBACpB,OAAOC,OAAY,mBACnB,OAAS,KAAAC,OAAS,MAClB,OAAS,KAAAC,OAAS,KAoClB,IAAMC,GAAgB,MAAOC,GAA8C,CACzE,GAAM,CAAE,iBAAAC,EAAkB,OAAAC,EAAQ,IAAAC,CAAI,EAAIH,EAE1C,GAAIC,EAAkB,MAAO,GAE7BG,EAAY,eAAe,EAE3B,IAAMC,EAAS,MAAMC,GAAQ,CAAE,QAAS,UAAUJ,CAAM,WAAMC,CAAG,IAAK,QAAS,EAAM,CAAC,EAEtF,OAAIE,GAAQD,EAAY,UAAU,QAAS,EAAI,EAExCC,CACT,EAKaE,GAAqB,MAAOP,GAAiC,CACxE,GAAM,CAAE,QAAAQ,EAAS,IAAAL,EAAK,cAAAM,EAAe,iBAAAR,CAAiB,EAAID,EAEtDU,EAAwB,GAE5B,GAAIF,EACFE,EAAwBF,IAAY,MAAQ,MAAQG,GAAqBH,CAAO,MAC3E,CACLJ,EAAY,eAAe,EAE3B,IAAMQ,EAAiB,MAAMC,EAAsB,EAE7CC,EAAWF,EAAe,IAAKG,GAC5BA,EAAG,MACX,EAEKC,EAAe,IAAI,IACvBJ,EAAe,IAAKG,GACX,CAACA,EAAG,OAAQE,EAAkBF,EAAG,KAAK,CAAC,CAC/C,CACH,EAEMG,EAAe,MAAMC,EAAoB,EAE/CT,EAAwB,MAAMU,GAAkB,CAC9C,CAAE,MAAO,MAAO,MAAO,KAAM,EAC7B,GAAGC,EAAwB,CAAE,SAAAP,EAAU,aAAAI,EAAc,MAAOF,CAAa,CAAC,CAC5E,CAAC,CACH,CAEA,IAAMM,EAAkBC,GAAuBb,CAAqB,EAEpEN,EAAY,UAAU,YAAakB,CAAe,EAElD,GAAM,CAAE,aAAAE,CAAa,EAAI,MAAMC,EAAkB,EAE7CC,EAAc,GAoBlB,GAlBIvB,EACFuB,EAAcvB,GAEdC,EAAY,eAAe,EAE3BsB,EAAc,MAAMC,GAAO,CACzB,QAAS,+BACT,QAASH,EAAa,IAAKrB,IAClB,CACL,KAAMA,EACN,MAAOA,CACT,EACD,CACH,CAAC,GAGHC,EAAY,UAAU,QAASsB,CAAW,EAEtC,CAACF,EAAa,SAASE,CAAW,EACpC,MAAM,IAAIE,EAAe,OAAW,CAClC,UAAW,6BACX,YAAa,gBAAgBJ,EAAa,KAAK,IAAI,CAAC,GACpD,cAAe,wBAAwBE,CAAW,EACpD,CAAC,EAGH,IAAMG,EAAsBpB,GAAiB,GAM7C,GAJIoB,GACFzB,EAAY,UAAU,mBAAoB,EAAI,EAG5C,CAAE,MAAML,GAAc,CAAE,iBAAAE,EAAkB,OAAQS,EAAuB,IAAKgB,CAAY,CAAC,EAAI,CACjGI,EAAO,KAAK,sBAAsB,EAElC,IAAMC,EAAoB,CACxB,cAAerB,EACf,QAASY,EACT,YAAaI,EACb,oBAAqBG,EACrB,QAAS,EACX,EAEA,MAAO,CACL,QAASG,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,CAEA,GAAI,CACFE,GAAE,MAAQ,GAIV,MAAMA,0CAAyCvB,CAAqB,mBAAmBgB,CAAW,IAFxEG,EAAsB,CAAC,KAAM,4BAA4B,EAAI,CAAC,CAE+B,GAEvHI,GAAE,MAAQ,GAEVH,EAAO,KACL,0EAA0EpB,CAAqB,qBAAqBgB,CAAW,EACjI,EAEAtB,EAAY,MAAM,EAElB,IAAM2B,EAAoB,CACxB,cAAerB,EACf,QAASY,EACT,YAAaI,EACb,oBAAqBG,EACrB,QAAS,EACX,EAEA,MAAO,CACL,QAASG,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,OAASG,EAAgB,CACvB,MAAAJ,EAAO,MAAM,CAAE,MAAAI,CAAM,EAAG,iCAA4B,EAC9C,IAAIN,EAAeM,EAAO,CAC9B,UAAW,6BACX,YAAa,2EACf,CAAC,CACH,CACF,EAGaC,GAA4BC,EAAc,CACrD,KAAM,wBACN,YACE,+gBACF,YAAa,CACX,QAASC,GACN,OAAO,EACP,SACC,iOACF,EACF,IAAKA,GACF,OAAO,EACP,SACC,uIACF,EACF,cAAeA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,sCAAsC,CACvF,EACA,aAAc,CACZ,cAAeA,GAAE,OAAO,EAAE,SAAS,sCAAsC,EACzE,QAASA,GAAE,OAAO,EAAE,SAAS,+BAA+B,EAC5D,YAAaA,GAAE,OAAO,EAAE,SAAS,6BAA6B,EAC9D,oBAAqBA,GAAE,QAAQ,EAAE,SAAS,0CAA0C,EACpF,QAASA,GAAE,QAAQ,EAAE,SAAS,uCAAuC,CACvE,EACA,QAAS9B,EACX,CAAC,EC7MD,OAAO+B,OAAc,qBACrB,OAAOC,OAAa,oBACpB,OAAOC,OAAY,mBACnB,OAAOC,OAAQ,mBACf,OAAS,WAAAC,OAAe,YACxB,OAAOC,OAAU,OACjB,OAAS,KAAAC,MAAS,MAClB,OAAS,KAAAC,OAAS,KAsClB,IAAMC,GAAgB,MAAOC,GAA8C,CACzE,GAAM,CAAE,iBAAAC,EAAkB,OAAAC,EAAQ,IAAAC,CAAI,EAAIH,EAE1C,GAAIC,EAAkB,MAAO,GAE7BG,EAAY,eAAe,EAE3B,IAAMC,EAAS,MAAMC,GAAQ,CAAE,QAAS,UAAUJ,CAAM,WAAMC,CAAG,IAAK,QAAS,EAAM,CAAC,EAEtF,OAAIE,GAAQD,EAAY,UAAU,QAAS,EAAI,EAExCC,CACT,EAMaE,GAA0B,MAAOP,GAAsC,CAClF,GAAM,CAAE,QAAAQ,EAAS,IAAAL,EAAK,SAAAM,EAAU,cAAAC,EAAe,iBAAAT,CAAiB,EAAID,EAEhEW,EAAwB,GAE5B,GAAIH,EACFG,EAAwBH,IAAY,MAAQ,MAAQI,GAAqBJ,CAAO,MAC3E,CACLJ,EAAY,eAAe,EAE3B,IAAMS,EAAiB,MAAMC,EAAsB,EAE7CC,EAAWF,EAAe,IAAKG,GAC5BA,EAAG,MACX,EAEKC,GAAe,IAAI,IACvBJ,EAAe,IAAKG,GACX,CAACA,EAAG,OAAQE,EAAkBF,EAAG,KAAK,CAAC,CAC/C,CACH,EAEMG,EAAe,MAAMC,EAAoB,EAE/CT,EAAwB,MAAMU,GAAkB,CAC9C,CAAE,MAAO,MAAO,MAAO,KAAM,EAC7B,GAAGC,EAAwB,CAAE,SAAAP,EAAU,aAAAI,EAAc,MAAOF,EAAa,CAAC,CAC5E,CAAC,CACH,CAEA,IAAMM,EAAkBC,GAAuBb,CAAqB,EAEpEP,EAAY,UAAU,YAAamB,CAAe,EAElD,GAAM,CAAE,aAAAE,CAAa,EAAI,MAAMC,EAAkB,EAE7CC,EAAc,GAoBlB,GAlBIxB,EACFwB,EAAcxB,GAEdC,EAAY,eAAe,EAE3BuB,EAAc,MAAMC,GAAO,CACzB,QAAS,+BACT,QAASH,EAAa,IAAKtB,IAClB,CACL,KAAMA,EACN,MAAOA,CACT,EACD,CACH,CAAC,GAGHC,EAAY,UAAU,QAASuB,CAAW,EAEtC,CAACF,EAAa,SAASE,CAAW,EACpC,MAAM,IAAIE,EAAe,OAAW,CAClC,UAAW,kCACX,YAAa,gBAAgBJ,EAAa,KAAK,IAAI,CAAC,GACpD,cAAe,wBAAwBE,CAAW,EACpD,CAAC,EAIH,IAAMG,EAAoB,MAAMC,GAA0B,EAE1D,GAAID,EAAkB,SAAW,EAC/B,MAAM,IAAID,EAAe,OAAW,CAClC,UAAW,kCACX,YAAa,kFACb,cAAe,oCACjB,CAAC,EAGH,IAAIG,EAA6B,CAAC,EAoBlC,GAlBIvB,GAAYA,EAAS,OAAS,EAChCuB,EAAmBvB,GAEnBL,EAAY,eAAe,EAE3B4B,EAAmB,MAAMC,GAAS,CAChC,QAAS,0EACT,QAASH,EAAkB,IAAKI,IACvB,CACL,KAAMA,EACN,MAAOA,CACT,EACD,CACH,CAAC,GAGH9B,EAAY,UAAU,aAAc4B,CAAgB,EAEhDA,EAAiB,SAAW,EAC9B,MAAM,IAAIH,EAAe,OAAW,CAClC,UAAW,kCACX,YAAa,mCAAmCC,EAAkB,KAAK,IAAI,CAAC,GAC5E,cAAe,sBACjB,CAAC,EAIH,IAAMK,EAAkBH,EAAiB,OAAQE,GACxC,CAACJ,EAAkB,SAASI,CAAG,CACvC,EAED,GAAIC,EAAgB,OAAS,EAC3B,MAAM,IAAIN,EAAe,OAAW,CAClC,UAAW,kCACX,YAAa,uBAAuBC,EAAkB,KAAK,IAAI,CAAC,GAChE,cAAe,qBAAqBK,EAAgB,KAAK,IAAI,CAAC,EAChE,CAAC,EAGH,IAAMC,EAAsB1B,GAAiB,GAEzC0B,GACFhC,EAAY,UAAU,mBAAoB,EAAI,EAGhD,IAAMiC,EAAeC,GAAqB,CACxC,IAAMC,EAAoB,CACxB,cAAe5B,EACf,QAASY,EACT,YAAaI,EACb,SAAUK,EACV,oBAAqBI,EACrB,QAAAE,CACF,EAEA,MAAO,CACL,QAASE,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAEA,GAAI,CAAE,MAAMxC,GAAc,CAAE,iBAAAE,EAAkB,OAAQU,EAAuB,IAAKgB,CAAY,CAAC,EAC7F,OAAAc,EAAO,KAAK,sBAAsB,EAE3BJ,EAAY,EAAK,EAG1B,GAAI,CACFK,GAAE,MAAQ,GAGV,IAAMC,EAAeX,EAAiB,QAASE,IACtC,CAAC,KAAM,GAAGA,EAAG,OAAO,CAC5B,EAGD,aAAMQ,wDAAuD/B,CAAqB,mBAAmBgB,CAAW,IAAIgB,CAAY,IAFtGP,EAAsB,CAAC,KAAM,4BAA4B,EAAI,CAAC,CAE6D,GAErJM,GAAE,MAAQ,GAEVD,EAAO,KACL,wFAAwF9B,CAAqB,kBAAkBgB,CAAW,eAAeK,EAAiB,KAAK,IAAI,CAAC,EACtL,EAEA5B,EAAY,MAAM,EAEXiC,EAAY,EAAI,CACzB,OAASO,EAAgB,CACvB,MAAAH,EAAO,MAAM,CAAE,MAAAG,CAAM,EAAG,iCAA4B,EAC9C,IAAIf,EAAee,EAAO,CAC9B,UAAW,kCACX,YAAa,yFACf,CAAC,CACH,CACF,EAMMb,GAA4B,SAA+B,CAC/D,IAAMc,EAAc,MAAMC,EAAe,EAEnCC,EAAeC,GAAQH,EAAa,gDAAgD,EAEpFI,EAAU,MAAMC,GAAG,SAASH,EAAc,OAAO,EAGjDI,EAFSC,GAAK,MAAMH,CAAO,EAEX,GAAG,kBAAkB,OACrCxC,EAAqB,CAAC,EAE5B,OAAW,CAAC4C,EAAKC,CAAK,IAAK,OAAO,QAAQH,CAAM,EAEzCG,EAA2B,OAAS,WAAaD,IAAQ,yBAC5D5C,EAAS,KAAK4C,CAAG,EAIrB,OAAO5C,CACT,EAGa8C,GAAiCC,EAAc,CAC1D,KAAM,6BACN,YACE,8iBACF,YAAa,CACX,QAASC,EACN,OAAO,EACP,SACC,iOACF,EACF,IAAKA,EACF,OAAO,EACP,SACC,uIACF,EACF,SAAUA,EACP,MAAMA,EAAE,OAAO,CAAC,EAChB,SACC,8KACF,EACF,cAAeA,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,sCAAsC,CACvF,EACA,aAAc,CACZ,cAAeA,EAAE,OAAO,EAAE,SAAS,sCAAsC,EACzE,QAASA,EAAE,OAAO,EAAE,SAAS,+BAA+B,EAC5D,YAAaA,EAAE,OAAO,EAAE,SAAS,6BAA6B,EAC9D,SAAUA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,SAAS,iCAAiC,EACxE,oBAAqBA,EAAE,QAAQ,EAAE,SAAS,0CAA0C,EACpF,QAASA,EAAE,QAAQ,EAAE,SAAS,uCAAuC,CACvE,EACA,QAASlD,EACX,CAAC,ECrSD,OAAS,KAAAmD,OAAS,MAWX,IAAMC,GAAgB,SAAY,CAIvC,IAAMC,GAHa,MAAMC,EAAsB,GAGnB,QAASC,GAAO,CAC1C,IAAMC,EAAKC,EAAgBF,EAAG,MAAM,EAEpC,OAAKC,EAEE,CACL,CAEE,QAASE,EAAaF,CAAE,EAExB,QAASG,EAAeH,CAAE,EAC1B,KAAMI,EAAkBL,EAAG,KAAK,CAClC,CACF,EAVgB,CAAC,CAWnB,CAAC,EAEKM,EAAmB,MAAMC,EAAoB,EAE7CC,EAAmB,KAAK,IAC5B,GAAGV,EAAS,IAAKW,GACRA,EAAE,QAAQ,MAClB,CACH,EAEMC,EAAiBZ,EAAS,IAAKa,GAAY,CAC/C,IAAMC,EAAQC,GAAmBF,EAAQ,QAASA,EAAQ,KAAMH,CAAgB,EAC1EM,EAAcR,EAAiB,IAAIK,EAAQ,OAAO,EAExD,OAAIG,EACK,GAAGF,CAAK,KAAKE,CAAW,GAG1BF,CACT,CAAC,EAEDG,EAAO,KAAK;AAAA,CAA0B,EACtCA,EAAO,KAAK;AAAA,EAAKL,EAAe,KAAK;AAAA,CAAI,CAAC;AAAA,CAAI,EAE9C,IAAMM,EAAoB,CACxB,SAAUlB,EAAS,IAAKa,IACf,CACL,QAASA,EAAQ,QACjB,KAAMA,EAAQ,KACd,YAAaL,EAAiB,IAAIK,EAAQ,OAAO,GAAK,IACxD,EACD,EACD,MAAOb,EAAS,MAClB,EAEA,MAAO,CACL,QAASmB,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAGaE,GAAuBC,EAAc,CAChD,KAAM,kBACN,YACE,8JACF,YAAa,CAAC,EACd,aAAc,CACZ,SAAUC,GACP,MACCA,GAAE,OAAO,CACP,QAASA,GAAE,OAAO,EAAE,SAAS,iBAAiB,EAC9C,KAAMA,GAAE,KAAK,CAAC,UAAW,QAAQ,CAAC,EAAE,SAAS,cAAc,EAC3D,YAAaA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0BAA0B,CACxE,CAAC,CACH,EACC,SAAS,8BAA8B,EAC1C,MAAOA,GAAE,OAAO,EAAE,SAAS,4BAA4B,CACzD,EACA,QAASvB,EACX,CAAC,ECzFD,OAAOwB,OAAa,oBACpB,OAAOC,OAAY,mBACnB,OAAOC,OAAa,eACpB,OAAS,KAAAC,MAAS,MAClB,OAAS,YAAAC,OAAgB,KCJzB,OAAS,KAAAC,OAAS,KCSX,IAAMC,GAAgBC,GACpBA,EAAW,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM,EAUrCC,GAAgBC,GACpB,CAAC,GAAGA,CAAQ,EAAE,KAAK,CAACC,EAAGC,IAAM,CAClC,GAAM,CAACC,EAAMC,EAAMC,CAAM,EAAIR,GAAaI,CAAC,EACrC,CAACK,EAAMC,EAAMC,CAAM,EAAIX,GAAaK,CAAC,EAE3C,OAAIC,IAASG,GAAcH,GAAQ,IAAMG,GAAQ,GAC7CF,IAASG,GAAcH,GAAQ,IAAMG,GAAQ,IAEzCF,GAAU,IAAMG,GAAU,EACpC,CAAC,ECvBI,IAAMC,GAAa,OASpBC,GAAa,0BAEbC,GAAqBC,GAClBA,EAAI,QAAQ,eAAgB,EAAE,EAGjCC,GAAYD,GAA+B,CAC/C,IAAME,EAAUH,GAAkBC,EAAI,KAAK,CAAC,EACtCG,EAAQL,GAAW,KAAKI,CAAO,EAErC,OAAKC,EAEE,CAAC,OAAOA,EAAM,CAAC,CAAC,EAAG,OAAOA,EAAM,CAAC,CAAC,EAAG,OAAOA,EAAM,CAAC,CAAC,CAAC,EAFzC,IAGrB,EAEMC,GAAaC,GACV,GAAGA,EAAE,CAAC,CAAC,IAAIA,EAAE,CAAC,CAAC,IAAIA,EAAE,CAAC,CAAC,GAGnBC,GAAwBC,GAA+C,CAClF,IAAMC,EAAM,CAAC,GAAID,EAAQ,gBAAkB,CAAC,EAAI,GAAIA,EAAQ,cAAgB,CAAC,CAAE,EACzEE,EAAmB,CAAC,EACpBC,EAAO,IAAI,IAEjB,QAAWV,KAAOQ,EAAK,CACrB,IAAMH,EAAIJ,GAASD,CAAG,EAEtB,GAAI,CAACK,EAAG,SAER,IAAMM,EAAMP,GAAUC,CAAC,EAEnBK,EAAK,IAAIC,CAAG,IAEhBD,EAAK,IAAIC,CAAG,EACZF,EAAO,KAAKJ,CAAC,EACf,CAEA,OAAOO,GACLH,EAAO,IAAKJ,GACHD,GAAUC,CAAC,CACnB,CACH,EAAE,IAAKQ,GACEC,GAAa,IAAID,CAAC,EAAE,CAC5B,CACH,EAEaE,GAAN,cAAmC,KAAM,CAC9C,aAAc,CACZ,MAAM,mFAAmF,EACzF,KAAK,KAAO,sBACd,CACF,EASaC,GAAqB,CAACC,EAAiBC,IAA8B,CAChF,GAAID,EAAM,SAAW,EAAG,MAAM,IAAIF,GAElC,IAAMI,EAAMF,EAAMA,EAAM,OAAS,CAAC,EAElC,GAAIC,IAAS,SAAU,CACrB,GAAM,CAACE,EAAOC,CAAK,EAAIF,EAEjBG,EAAsBL,EAAM,OAAO,CAACM,EAAKlB,IACzCA,EAAE,CAAC,IAAMe,GAASf,EAAE,CAAC,IAAMgB,EAAc,KAAK,IAAIE,EAAKlB,EAAE,CAAC,CAAC,EAExDkB,EACN,CAAC,EAEJ,MAAO,GAAGH,CAAK,IAAIC,CAAK,IAAIC,EAAsB,CAAC,EACrD,CAEA,GAAM,CAACF,EAAOC,CAAK,EAAIF,EAEvB,MAAO,GAAGC,CAAK,IAAIC,EAAQ,CAAC,IAC9B,EAEMG,GAAeC,GACZA,EAAM,KAAK,EAAE,YAAY,IAAM5B,GAuClC6B,GAAiBC,GACdA,IAAU,WAAaA,IAAU,SAGpCC,GAAuBC,GACpB,SAAUA,EAYNC,GAAoB9B,GAA8B,CAC7D,IAAM+B,EAAO/B,EAAI,KAAK,EAEtB,GAAI+B,IAAS,GAAI,MAAM,IAAI,MAAM,uBAAuB,EAExD,IAAMC,EAAaD,EAAK,QAAQ,GAAG,EAC/BN,EAAQM,EACRb,EAAoB,UACpBe,EAAc,GAElB,GAAID,IAAe,GAAI,CACrBP,EAAQM,EAAK,MAAM,EAAGC,CAAU,EAAE,KAAK,EACvC,IAAME,EAAOH,EAAK,MAAMC,EAAa,CAAC,EAChCG,EAAcD,EAAK,QAAQ,GAAG,EAC9BE,EAAUD,IAAgB,GAAKD,EAAK,KAAK,EAAIA,EAAK,MAAM,EAAGC,CAAW,EAAE,KAAK,EAEnFF,EAAcE,IAAgB,GAAK,GAAKD,EAAK,MAAMC,EAAc,CAAC,EAAE,KAAK,EACzE,IAAME,EAAYD,EAAQ,YAAY,EAEtC,GAAI,CAACV,GAAcW,CAAS,EAC1B,MAAM,IAAI,MAAM,yBAAyBD,CAAO,oCAAoC,EAGtFlB,EAAOmB,CACT,CAMA,GAAIb,GAAYC,CAAK,GAAKxB,GAASwB,CAAK,IAAM,KAAM,CAClD,IAAMa,EAAqB,CAAE,QAASb,EAAO,KAAAP,CAAK,EAElD,OAAIe,IAAgB,KAAIK,EAAM,YAAcL,GAErCK,CACT,CAEA,IAAMA,EAA2B,CAAE,KAAMb,EAAO,KAAAP,CAAK,EAErD,OAAIe,IAAgB,KAAIK,EAAM,YAAcL,GAErCK,CACT,EAYaC,GAAqBD,GAAgC,CAChE,IAAMb,EAAQa,EAAM,GAAG,IAEvB,OAAIA,EAAM,cAAgB,QAAaA,EAAM,cAAgB,GACpD,GAAGb,CAAK,IAAIa,EAAM,IAAI,IAAIA,EAAM,WAAW,GAGhDA,EAAM,OAAS,SAAiB,GAAGb,CAAK,UAErCA,CACT,EAEMe,GAAkB,CAACC,EAA4CR,IAC5DA,IAAgB,QAAaA,IAAgB,GAAK,CAAE,GAAGQ,EAAM,YAAAR,CAAY,EAAIQ,EAGhFC,GAAqBb,GAA2C,CACpE,IAAMc,EAAOd,EAAM,KAAK,KAAK,EAG7B,OAAAe,GAAaD,CAAI,EAEVH,GAAgB,CAAE,GAAI,CAAE,KAAM,OAAQ,KAAAG,EAAM,IAAKA,CAAK,EAAG,KAAMd,EAAM,IAAK,EAAGA,EAAM,WAAW,CACvG,EAUagB,GAAwB,CAACC,EAAyB7B,IAAoC,CACjG,IAAM8B,EAAoB,CAAC,GAAG9B,CAAK,EAEnC,OAAO6B,EAAQ,IAAKR,GAAU,CAC5B,GAAIV,GAAoBU,CAAK,EAC3B,OAAOI,GAAkBJ,CAAK,EAGhC,IAAMU,EAAUV,EAAM,QAAQ,KAAK,EAEnC,GAAIU,IAAY,GACd,MAAM,IAAI,MAAM,oCAAoC,EAGtD,GAAIxB,GAAYwB,CAAO,EAAG,CACxB,IAAMC,EAAOjC,GAAmB+B,EAAST,EAAM,IAAI,EAEnD,OAAAS,EAAQ,KAAKjC,GAAa,IAAImC,CAAI,EAAE,CAAC,EAE9BT,GAAgB,CAAE,GAAIU,GAAgBD,CAAI,EAAG,KAAMX,EAAM,IAAK,EAAGA,EAAM,WAAW,CAC3F,CAEA,IAAM7B,EAASR,GAAS+C,CAAO,EAE/B,GAAI,CAACvC,EACH,MAAM,IAAI,MAAM,oBAAoBuC,CAAO,sDAAsD,EAGnG,IAAMG,EAAW,GAAG1C,EAAO,CAAC,CAAC,IAAIA,EAAO,CAAC,CAAC,IAAIA,EAAO,CAAC,CAAC,GAEvD,OAAAsC,EAAQ,KAAKtC,CAAM,EAEZ+B,GAAgB,CAAE,GAAIU,GAAgBC,CAAQ,EAAG,KAAMb,EAAM,IAAK,EAAGA,EAAM,WAAW,CAC/F,CAAC,CACH,EAEac,GAAgBN,GACpBA,EAAQ,KAAMO,GACZ,CAACzB,GAAoByB,CAAC,GAAK7B,GAAY6B,EAAE,OAAO,CACxD,EFvQI,IAAMC,GAA0BC,GAC9BA,EACJ,MAAM;AAAA,CAAI,EACV,IAAKC,GAAS,CACb,IAAMC,EAAMD,EAAK,QAAQ,GAAI,EAE7B,OAAIC,IAAQ,GAAW,KAEhBC,EAAgBF,EAAK,MAAMC,EAAM,CAAC,CAAC,CAC5C,CAAC,EACA,OAAQE,GACAA,IAAO,MAAQA,EAAG,OAAS,SACnC,EACA,IAAKA,GACGA,EAAG,GACX,EAGCC,GAAkB,SAA+B,CACrD,IAAMC,EAAgBC,GAAE,MAExB,GAAI,CACFA,GAAE,MAAQ,GACV,IAAMC,EAAS,MAAMD,8CAErB,OAAOR,GAAuBS,EAAO,MAAM,CAC7C,QAAE,CACAD,GAAE,MAAQD,CACZ,CACF,EAEMG,GAAwB,SAA+B,CAC3D,IAAMC,EAAS,MAAMC,GAAuB,EAE5C,OAAKD,GAEY,MAAME,GAAmBF,CAAM,GAEhC,IAAKG,GACZA,EAAE,IACV,EANmB,CAAC,CAOvB,EAWaC,GAAuB,SAA+B,CACjE,GAAM,CAACC,EAAgBC,CAAU,EAAI,MAAM,QAAQ,WAAW,CAACX,GAAgB,EAAGI,GAAsB,CAAC,CAAC,EAE1G,OAAIM,EAAe,SAAW,YAC5BE,EAAO,KAAK,CAAE,MAAOF,EAAe,MAAO,EAAG,iEAAiE,EAG7GC,EAAW,SAAW,YACxBC,EAAO,KAAK,CAAE,MAAOD,EAAW,MAAO,EAAG,wDAAwD,EAG7FE,GAAqB,CAC1B,eAAgBH,EAAe,SAAW,YAAcA,EAAe,MAAQ,CAAC,EAChF,aAAcC,EAAW,SAAW,YAAcA,EAAW,MAAQ,CAAC,CACxE,CAAC,CACH,EDtDA,IAAMG,GAAsB,oBAEtBC,GAAiB,CAACC,EAAiBC,IAAqC,CAC5E,GAAI,CACF,OAAOC,GAAmBF,EAAOC,CAAI,CACvC,OAASE,EAAK,CACZ,GAAIA,aAAeC,GAAsB,OAAO,KAEhD,MAAMD,CACR,CACF,EAEME,GAAgB,CAACC,EAAyBN,IAAoC,CAClF,GAAI,CACF,OAAOO,GAAsBD,EAASN,CAAK,CAC7C,OAASG,EAAK,CACZ,MAAIA,aAAeC,GACX,IAAII,EAAeL,EAAK,CAC5B,UAAW,0BACX,YAAa,4FACf,CAAC,EAGCA,aAAeM,GACX,IAAID,EAAeL,EAAK,CAC5B,UAAW,wBACX,YACE,yGACJ,CAAC,EAGGA,CACR,CACF,EAEMO,GAAwB,MAAOC,EAAmBV,IAAuC,CAC7F,IAAMW,EAAab,GAAeY,EAASV,CAAI,EACzCY,EAAcD,EAAa,KAAKA,CAAU,IAAM,GAChDE,GAAiB,MAAMC,GAAS,mBAAmBjB,EAAmB,IAAIe,CAAW,IAAI,GAAG,KAAK,EACjGG,EAAeF,IAAkB,GAAMF,GAAc,GAAME,EAEjE,OAAIE,IAAiB,KACnBC,EAAO,MAAM,iCAAiC,EAC9CC,GAAQ,KAAK,CAAC,GAGTF,CACT,EAEMG,GAAqB,SAA6B,CACtD,IAAMC,GAAQ,MAAML,GAAS,iDAAiD,GAAG,KAAK,EAElFK,IAAS,KACXH,EAAO,MAAM,8BAA8B,EAC3CC,GAAQ,KAAK,CAAC,GAGhB,GAAI,CACFG,GAAaD,CAAI,CACnB,OAASjB,EAAK,CACZ,IAAMmB,EAASnB,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAE9Dc,EAAO,MAAM,GAAGK,CAAM,aAAa,EACnCJ,GAAQ,KAAK,CAAC,CAChB,CAEA,OAAOE,CACT,EAEMG,GAA+B,MAAOC,GAAkE,CAC5GC,EAAY,eAAe,EAE3B,IAAIC,EAA6B,KAC3Bf,EAAoB,CAAC,EACrBgB,EAAgB,UAChBD,IAAc,OAChBA,EAAY,MAAMF,EAAY,EAC9Bb,EAAQ,KAAK,GAAGe,CAAS,GAGpBf,GAGHL,EAA0B,CAAC,EAC7BsB,EAAa,GAEjB,KAAOA,GAAY,CACjB,IAAMC,EAAUvB,EAAQ,OAAS,EAC3BwB,EAAO,MAAMC,GAA2B,CAC5C,QAAS,YAAYF,CAAO,2BAC5B,QAAS,CACP,CAAE,KAAM,0BAA2B,MAAO,SAAU,EACpD,CAAE,KAAM,mBAAoB,MAAO,MAAO,CAC5C,EACA,QAAS,SACX,CAAC,EAEK5B,EAAO,MAAM8B,GAAoB,CACrC,QAAS,YAAYF,CAAO,uBAC5B,QAAS,CACP,CAAE,KAAM,UAAW,MAAO,SAAU,EACpC,CAAE,KAAM,SAAU,MAAO,QAAS,CACpC,EACA,QAAS,SACX,CAAC,EAEGG,EAEJ,GAAIF,IAAS,OAAQ,CACnB,IAAMV,EAAO,MAAMD,GAAmB,EAEtCa,EAAW3B,GAAc,CAAC,CAAE,KAAAe,EAAM,KAAAnB,CAAK,CAAC,EAAG,CAAC,CAAC,EAAE,CAAC,CAClD,KAAO,CAEL,IAAMe,EAAe,MAAMN,GAAsB,MAAMiB,EAAc,EAAG1B,CAAI,EAE5E+B,EAAW3B,GAAc,CAAC,CAAE,QAASW,EAAc,KAAAf,CAAK,CAAC,EAAGU,CAAO,EAAE,CAAC,EAElEqB,EAAS,GAAG,OAAS,WACvBrB,EAAQ,KAAKsB,GAAa,IAAID,EAAS,GAAG,GAAG,EAAE,CAAC,CAEpD,CAEA,IAAME,GAAe,MAAMnB,GAAS,iDAAiD,GAAG,KAAK,EAE7FT,EAAQ,KAAK,CAAE,GAAG0B,EAAU,GAAIE,IAAgB,GAAK,CAAE,YAAAA,CAAY,EAAI,CAAC,CAAG,CAAC,EAE5EN,EAAa,MAAMO,GAAQ,CAAE,QAAS,uBAAwB,QAAS,EAAM,CAAC,CAChF,CAEA,OAAO7B,CACT,EAEM8B,GAAwBC,GAAgC,CAE5D,IAAMC,EAAQ,CADAD,EAAM,GAAG,OAAS,UAAY,IAAIA,EAAM,GAAG,GAAG,GAAKA,EAAM,GAAG,KACpDA,EAAM,IAAI,EAEhC,OAAIA,EAAM,aAAaC,EAAM,KAAKD,EAAM,WAAW,EAE5CC,EAAM,KAAK,QAAK,CACzB,EAEMC,GAAgBjC,GAAkC,CAItD,QAAW+B,KAAS/B,EAClBmB,EAAY,UAAU,YAAae,GAAkBH,CAAK,CAAC,CAE/D,EAOMI,GAAiB,MACrBC,EACAlB,IAC4B,CAC5B,GAAIkB,GAAiBA,EAAc,OAAS,EAAG,CAC7C,IAAM1C,EAAQ2C,GAAaD,CAAa,EAAI,MAAMlB,EAAY,EAAI,CAAC,EAC7DQ,EAAW3B,GAAcqC,EAAe1C,CAAK,EAEnD,OAAAuC,GAAaP,CAAQ,EAEdA,CACT,CAEA,IAAMY,EAAc,MAAMrB,GAA6BC,CAAW,EAElE,OAAAe,GAAaK,CAAW,EAEjBA,CACT,EAQaC,GAAgCvC,GAAkC,CAO7E,GANc,IAAI,IAChBA,EAAQ,IAAK+B,GACJA,EAAM,IACd,CACH,EAEU,KAAO,EACf,MAAM,IAAI7B,EAAe,OAAW,CAClC,UAAW,iBACX,YAAa,6DACb,cAAe,kEACjB,CAAC,CAEL,EAEMsC,GAAkB,MAAOxC,EAAyByC,IAA6C,CACnG,IAAMC,EAAU1C,EAAQ,IAAI8B,EAAoB,EAAE,KAAK;AAAA,KAAQ,EAE/D,MAAMa,EAAcF,EAAkB,wBAAwBzC,EAAQ,MAAM;AAAA,MAAqB0C,CAAO;AAAA,CAAI,EAE5GvB,EAAY,UAAU,QAAS,EAAI,CACrC,EAOMyB,GAAa,MACjBC,GACyE,CACzE,GAAM,CAAE,MAAAd,EAAO,WAAAe,CAAW,EAAID,EACxBE,EAAQC,EAAajB,EAAM,EAAE,EAC7BkB,EAAelB,EAAM,GAAG,OAAS,UAAY,IAAIA,EAAM,GAAG,GAAG,GAAKA,EAAM,GAAG,KAEjF,GAAI,CACF,MAAMmB,GAAqBnB,EAAM,IAAI,EAErC,IAAMoB,EAAS,MAAMC,GAAoB,CACvC,GAAIrB,EAAM,GACV,WAAAe,EACA,YAAaf,EAAM,YACnB,KAAMA,EAAM,IACd,CAAC,EAED,OAAApB,EAAO,KAAK,wCAAmCsC,CAAY,KAAKlB,EAAM,IAAI,GAAG,EAC7EpB,EAAO,KAAK,yBAAkBwC,EAAO,KAAK,EAAE,EAC5CxC,EAAO,KAAK,4BAAqBwC,EAAO,cAAc;AAAA,CAAI,EAEnD,CAAE,OAAAA,CAAO,CAClB,OAASE,EAAO,CACd,IAAMxD,EAAM,IAAIK,EAAemD,EAAO,CACpC,UAAW,kBAAkBJ,CAAY,KAAKlB,EAAM,IAAI,IACxD,YAAa,mEACf,CAAC,EAED,OAAApB,EAAO,MAAM,UAAKd,EAAI,OAAO;AAAA,CAAI,EAE1B,CAAE,QAAS,CAAE,QAASkD,EAAO,MAAOlD,EAAI,OAAQ,CAAE,CAC3D,CACF,EAEMyD,GAAkB,CAACC,EAAeC,EAAsBC,IAA+B,CACvFD,IAAiBD,EACnB5C,EAAO,KAAK,cAAS4C,CAAK,gDAAgD,EACjEC,EAAe,GACxB7C,EAAO,KAAK,iBAAO6C,CAAY,OAAOD,CAAK,8CAA8C,EACzF5C,EAAO,KAAK,WAAM8C,CAAY,qBAAqB,GAEnD9C,EAAO,MAAM,cAAS4C,CAAK,uCAAuC,CAEtE,EASaG,GAAgB,MAAOb,GAA4B,CAC9D,GAAM,CAAE,SAAUT,EAAe,iBAAAK,CAAiB,EAAII,EAEhDC,EAAa,MAAMa,GAAe,EAEpCjE,EAAyB,KAOvBM,EAAU,MAAMmC,GAAeC,EANjB,UACd1C,IAAU,OAAMA,EAAQ,MAAMkE,GAAqB,GAEhDlE,EAGsD,EAE/D,GAAIM,EAAQ,SAAW,EACrB,MAAM,IAAIE,EAAe,OAAW,CAClC,UAAW,iBACX,YAAa,uFACb,cAAe,sBACjB,CAAC,EAGHqC,GAA6BvC,CAAO,EAEpC,MAAM6D,EAAwB,CAC5B,UAAW,iBACX,eAAgBC,GAAc9D,EAAQ,CAAC,EAAG,IAAI,CAChD,CAAC,EAED,MAAMwC,GAAgBxC,EAAS,EAAQyC,CAAiB,EAExD,IAAMsB,EAAmC,CAAC,EACpCC,EAA0B,CAAC,EAEjC,QAAWjC,KAAS/B,EAAS,CAC3B,GAAM,CAAE,OAAAmD,EAAQ,QAAAc,CAAQ,EAAI,MAAMrB,GAAW,CAAE,MAAAb,EAAO,WAAAe,CAAW,CAAC,EAE9DK,GAAQY,EAAQ,KAAKZ,CAAM,EAC3Bc,GAASD,EAAO,KAAKC,CAAO,CAClC,CAEAX,GAAgBtD,EAAQ,OAAQ+D,EAAQ,OAAQC,EAAO,MAAM,EAE7D7C,EAAY,MAAM,EAElB,IAAM+C,EAAoB,CACxB,gBAAiBH,EAAQ,IAAKI,GACrBA,EAAE,UACV,EACD,aAAcJ,EAAQ,OACtB,aAAcC,EAAO,OACrB,SAAUD,EACV,eAAgBC,CAClB,EAEA,MAAO,CACL,QAASI,EAAY,KAAK,UAAUF,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAGaG,GAAuBC,EAAc,CAChD,KAAM,iBACN,YACE,4zCACF,YAAa,CACX,SAAUC,EACP,MACCA,EACG,OAAO,CACN,QAASA,EACN,OAAO,EACP,SAAS,EACT,SACC,mHACF,EACF,KAAMA,EACH,OAAO,EACP,SAAS,EACT,SACC,mIACF,EACF,KAAMA,EACH,KAAK,CAAC,UAAW,QAAQ,CAAC,EAC1B,SAAS,EACT,QAAQ,SAAS,EACjB,SAAS,6EAA6E,EACzF,YAAaA,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4CAA4C,CAC1F,CAAC,EACA,OACExC,GACSA,EAAM,UAAY,SAAgBA,EAAM,OAAS,QAE3D,CACE,QAAS,gGACX,CACF,EACC,UAAWA,GACHA,EAAM,OAAS,OAClB,CAAE,KAAMA,EAAM,KAAM,KAAMA,EAAM,KAAM,GAAIA,EAAM,YAAc,CAAE,YAAaA,EAAM,WAAY,EAAI,CAAC,CAAG,EACvG,CACE,QAASA,EAAM,QACf,KAAMA,EAAM,KACZ,GAAIA,EAAM,YAAc,CAAE,YAAaA,EAAM,WAAY,EAAI,CAAC,CAChE,CACL,CACL,EACC,IAAI,CAAC,EACL,SACC,gIACF,CACJ,EACA,aAAc,CACZ,gBAAiBwC,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,SAAS,sCAAsC,EACpF,aAAcA,EAAE,OAAO,EAAE,SAAS,yCAAyC,EAC3E,aAAcA,EAAE,OAAO,EAAE,SAAS,gCAAgC,EAClE,SAAUA,EACP,MACCA,EAAE,OAAO,CACP,QAASA,EAAE,OAAO,EAAE,SAAS,gBAAgB,EAC7C,KAAMA,EAAE,KAAK,CAAC,UAAW,QAAQ,CAAC,EAAE,SAAS,cAAc,EAC3D,WAAYA,EAAE,OAAO,EAAE,SAAS,qBAAqB,EACrD,MAAOA,EAAE,OAAO,EAAE,SAAS,eAAe,EAC1C,eAAgBA,EAAE,OAAO,EAAE,SAAS,kBAAkB,CACxD,CAAC,CACH,EACC,SAAS,yDAAyD,EACrE,eAAgBA,EACb,MACCA,EAAE,OAAO,CACP,QAASA,EAAE,OAAO,EAAE,SAAS,4BAA4B,EACzD,MAAOA,EAAE,OAAO,EAAE,SAAS,eAAe,CAC5C,CAAC,CACH,EACC,SAAS,kDAAkD,CAChE,EACA,QAASb,EACX,CAAC,EIhbD,OAAS,KAAAc,OAAS,MAClB,OAAS,YAAAC,OAAgB,KAyBzB,IAAMC,GAAsB,CAACC,EAAwBC,IAC5C,GAAGD,EAAW,OAAO,aAAaC,EAAQ,SAAS,aAAaA,EAAQ,EAAE,iCAG7EC,GAAc,CAACC,EAAwBC,IACpCA,EAAY,KAAK,IAAM,GAAK,GAAGD,CAAc;AAAA;AAAA,EAAOC,CAAW,GAAK,GAAGD,CAAc;AAAA,EAGxFE,GAAoB,SAA4D,CACpF,IAAMC,EAAiB,MAAMC,EAAsB,EAC7CC,EAAWF,EAAe,IAAKG,GAC5BA,EAAG,MACX,EACKC,EAAQ,IAAI,IAChBJ,EAAe,IAAKG,GACX,CAACA,EAAG,OAAQE,EAAkBF,EAAG,KAAK,CAAC,CAC/C,CACH,EACMG,EAAe,MAAMC,EAAoB,EAEzCC,EAAS,MAAMT,GAAwBU,EAAwB,CAAE,SAAAP,EAAU,aAAAI,EAAc,MAAAF,CAAM,CAAC,CAAC,EAEvG,MAAO,CAAE,OAAAI,EAAQ,KAAMJ,EAAM,IAAII,CAAM,GAAK,SAAU,CACxD,EAEME,GAAwB,MAAOC,GAAiD,CAEpF,IAAMC,GADiB,MAAMX,EAAsB,GACrB,KAAME,GAC3BA,EAAG,SAAWQ,CACtB,EAED,GAAI,CAACC,EACH,MAAM,IAAIC,EAAe,OAAW,CAClC,UAAW,wBAAwBF,CAAc,GACjD,YAAa,iCAAiCA,CAAc,iBAC9D,CAAC,EAGH,OAAON,EAAkBO,EAAO,KAAK,CACvC,EAEME,GAAoB,MAAOC,GAAqC,CACpE,IAAMC,EAAOD,IAAY,GAAK,2BAA6B,aAAaA,CAAO,IAEzEE,GADS,MAAMC,GAAS,qBAAqBF,CAAI;AAAA,kCAAqC,GACrE,QAAQ,MAAO,EAAE,EAExC,OAAOC,IAAY,GAAKF,EAAUE,CACpC,EAOaE,GAAkB,MAAOC,GAA8B,CAClE,GAAM,CAAE,QAASC,EAAY,YAAaC,EAAgB,iBAAAC,CAAiB,EAAIH,EAEzE1B,EAAa,MAAM8B,GAAe,EAEpCb,EAEAU,GACFV,EAAiBc,GAAqBJ,CAAU,EAChD,MAAMX,GAAsBC,CAAc,IAE1Ce,EAAY,eAAe,EAG3Bf,GAFe,MAAMZ,GAAkB,GAEf,QAK1B,IAAM4B,EAAYC,EAAgBjB,CAAc,EAEhD,GAAI,CAACgB,EACH,MAAM,IAAId,EAAe,OAAW,CAClC,UAAW,wBAAwBF,CAAc,GACjD,YAAa,4EACf,CAAC,EAGH,IAAMkB,EAAkBC,EAAaH,CAAS,EAE9CD,EAAY,UAAU,YAAaG,CAAe,EAGlD,IAAME,EAAcC,EAAeL,CAAS,EACtCM,EAAc,MAAMC,GAAkBH,EAAarC,CAAU,EAEnE,GAAI,CAACuC,EACH,MAAM,IAAIpB,EAAe,OAAW,CAClC,UAAW,wBAAwBkB,CAAW,GAC9C,YAAa,gCAAgCA,CAAW,qCAC1D,CAAC,EAGH,IAAMI,EAAsBF,EAAY,aAAe,GAEnDG,EAEAd,IAAmB,QACrBc,EAAiBd,EACjBI,EAAY,UAAU,gBAAiBU,CAAc,IAErDV,EAAY,eAAe,EAC3BU,EAAiB,MAAMtB,GAAkBqB,CAAmB,GAG9D,IAAME,EAAeC,GAAqB,CACxC,IAAMC,EAAoB,CACxB,QAASV,EACT,OAAQlB,EACR,eAAgBlB,GAAoBC,EAAYuC,CAAW,EAC3D,oBAAAE,EACA,eAAAC,EACA,QAAAE,CACF,EAEA,MAAO,CACL,QAASE,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAEA,GAAIH,IAAmBD,EACrB,OAAAM,EAAO,KAAK,oCAA+BV,CAAW,iBAAiBI,CAAmB,GAAG,EAC7FT,EAAY,MAAM,EAEXW,EAAY,EAAK,EAG1B,MAAMK,EACJnB,EACA,0BAA0BQ,CAAW;AAAA,WAAeI,CAAmB;AAAA,WAAeC,CAAc;AAAA,CACtG,EAEAV,EAAY,UAAU,QAAS,EAAI,EAEnC,MAAMiB,GAAkB,CAAE,UAAWV,EAAY,GAAI,YAAaG,CAAe,EAAG1C,CAAU,EAE9F,IAAMG,EAAiBJ,GAAoBC,EAAYuC,CAAW,EAC5DW,EAAOhD,GAAYC,EAAgBuC,CAAc,EAEvD,aAAMS,GAAoB,CAAE,OAAQlC,EAAgB,KAAAiC,CAAK,CAAC,EAE1DH,EAAO,KAAK,kCAA6BV,CAAW,EAAE,EACtDU,EAAO,KAAK,4BAAqB5C,CAAc,EAAE,EACjD4C,EAAO,KAAK,yBAAkB9B,CAAc;AAAA,CAAI,EAEhDe,EAAY,MAAM,EAEXW,EAAY,EAAI,CACzB,EAGaS,GAAyBC,EAAc,CAClD,KAAM,oBACN,YACE,qpBACF,YAAa,CACX,QAASC,GACN,OAAO,EACP,SAAS,wFAAwF,EACpG,YAAaA,GAAE,OAAO,EAAE,SAAS,uDAAuD,CAC1F,EACA,aAAc,CACZ,QAASA,GAAE,OAAO,EAAE,SAAS,iBAAiB,EAC9C,OAAQA,GAAE,OAAO,EAAE,SAAS,4EAA4E,EACxG,eAAgBA,GAAE,OAAO,EAAE,SAAS,sBAAsB,EAC1D,oBAAqBA,GAAE,OAAO,EAAE,SAAS,mCAAmC,EAC5E,eAAgBA,GAAE,OAAO,EAAE,SAAS,kCAAkC,EACtE,QAASA,GAAE,QAAQ,EAAE,SAAS,0CAA0C,CAC1E,EACA,QAAS7B,EACX,CAAC,ECzMD,OAAS,cAAA8B,OAAkB,UAC3B,OAAOC,OAAU,YACjB,OAAOC,OAAa,eACpB,OAAS,KAAAC,OAAS,MCHlB,OAAS,cAAAC,OAAkB,cAC3B,OAAS,gBAAAC,OAAoB,UAYtB,IAAMC,GAAUC,GACdH,GAAW,QAAQ,EAAE,OAAOC,GAAaE,CAAQ,CAAC,EAAE,OAAO,KAAK,ECdzE,OAAS,gBAAAC,GAAc,iBAAAC,OAAqB,UAC5C,OAAOC,OAAU,YACjB,OAAS,KAAAC,OAAS,MCFlB,OAAS,eAAAC,OAAmB,UAC5B,OAAOC,OAAU,YCaV,IAAMC,EAAa,SAEbC,GAA0C,IAAI,IAAI,CAC7D,eACA,OACA,SACA,OACA,kBACA,sBACA,UACA,UACA,SACA,WACF,CAAC,EAEYC,GAA2C,IAAI,IAAI,CAAC,sBAAuB,eAAgB,SAAS,CAAC,EAErGC,GAA4C,CAAC,cAAc,EAG3DC,GAAgBC,GACpBJ,GAAmB,IAAII,CAAI,EAIvBC,GAAiBD,GACxBH,GAAoB,IAAIG,CAAI,EACvB,GAGFF,GAAuB,KAAMI,GAC3BF,EAAK,SAASE,CAAM,CAC5B,EDrCH,IAAMC,GAAWC,GACRA,EAAa,MAAMC,GAAK,GAAG,EAAE,KAAK,GAAG,EAkBjCC,GAAkBC,GAAiC,CAC9D,IAAMC,EAAgB,CAAC,EAEjBC,EAAQC,GAAsB,CAClC,QAAWC,KAASC,GAAYF,EAAK,CAAE,cAAe,EAAK,CAAC,EAAG,CAC7D,IAAMG,EAAOR,GAAK,KAAKK,EAAKC,EAAM,IAAI,EAEtC,GAAIA,EAAM,YAAY,EAAG,CACvB,GAAIG,GAAaH,EAAM,IAAI,EACzB,SAGFF,EAAKI,CAAI,EACT,QACF,CAEIE,GAAcJ,EAAM,IAAI,GAI5BH,EAAI,KAAKL,GAAQE,GAAK,SAASE,EAAYM,CAAI,CAAC,CAAC,CACnD,CACF,EAEA,OAAAJ,EAAKF,CAAU,EAERC,EAAI,KAAK,CAClB,ED/CO,IAAMQ,GAAgB,sBAQhBC,GAAyB,EAQhCC,GAAiBC,GAAE,OAAO,CAC9B,cAAeA,GAAE,OAAO,EAAE,SAAS,EACnC,OAAQA,GAAE,OAAO,EACjB,OAAQA,GAAE,OAAO,EACjB,SAAUA,GAAE,OAAO,EACnB,UAAWA,GAAE,OAAO,EACpB,MAAOA,GAAE,OAAOA,GAAE,OAAO,EAAGA,GAAE,OAAO,CAAC,CACxC,CAAC,EAcYC,GAAmBC,IACtBA,EAAS,eAAiBJ,IAA0BA,GASjDK,GAAiBC,GAA+C,CAC3E,IAAMC,EAAgC,CAAC,EAEvC,QAAWC,KAAOC,GAAeH,CAAU,EACzC,GAAI,CACFC,EAAMC,CAAG,EAAIE,GAAOC,GAAK,KAAKL,EAAYE,CAAG,CAAC,CAChD,OAASI,EAAO,CACd,IAAMC,EAASD,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EAEpE,MAAM,IAAI,MAAM,4BAA4BJ,CAAG,0CAA0CK,CAAM,EAAE,CACnG,CAGF,OAAON,CACT,EAMaO,GAAgBR,GAAuC,CAClE,IAAMS,EAAeJ,GAAK,KAAKL,EAAYP,EAAa,EAEpDiB,EAEJ,GAAI,CACFA,EAAMC,GAAaF,EAAc,MAAM,CACzC,MAAQ,CACN,MAAM,IAAI,MAAM,WAAWhB,EAAa,OAAOgB,CAAY,EAAE,CAC/D,CAEA,IAAMG,EAASjB,GAAe,UAAU,KAAK,MAAMe,CAAG,CAAC,EAEvD,GAAI,CAACE,EAAO,QACV,MAAM,IAAI,MAAM,WAAWnB,EAAa,OAAOgB,CAAY,KAAKb,GAAE,cAAcgB,EAAO,KAAK,CAAC,EAAE,EAGjG,OAAOA,EAAO,IAChB,EAWaC,GAAgB,CAACb,EAAoBc,IAA6D,CAC7G,IAAMb,EAAQF,GAAcC,CAAU,EAEhCF,EAA2B,CAC/B,cAAeJ,GACf,OAAQoB,EAAK,OACb,OAAQA,EAAK,OACb,SAAU,IAAI,KAAK,EAAE,YAAY,EACjC,UAAW,OAAO,KAAKb,CAAK,EAAE,OAC9B,MAAAA,CACF,EAEA,OAAAc,GAAcV,GAAK,KAAKL,EAAYP,EAAa,EAAG,GAAG,KAAK,UAAUK,EAAU,KAAM,CAAC,CAAC;AAAA,CAAI,EAErFA,CACT,EAOakB,GAAoB,CAAChB,EAAoBF,IAA2C,CAC/F,IAAMmB,EAAWnB,EAAS,MACpBoB,EAAcf,GAAeH,CAAU,EAEvCmB,EAAqB,CAAC,EACtBC,EAAkB,CAAC,EAEzB,QAAWlB,KAAOgB,EAAa,CAC7B,GAAI,EAAEhB,KAAOe,GAAW,CACtBG,EAAM,KAAKlB,CAAG,EACd,QACF,CAEIE,GAAOC,GAAK,KAAKL,EAAYE,CAAG,CAAC,IAAMe,EAASf,CAAG,GACrDiB,EAAS,KAAKjB,CAAG,CAErB,CAEA,IAAMmB,EAAY,IAAI,IAAIH,CAAW,EAC/BI,EAAU,OAAO,KAAKL,CAAQ,EAAE,OAAQf,GACrC,CAACmB,EAAU,IAAInB,CAAG,CAC1B,EAED,MAAO,CAAE,SAAAiB,EAAU,MAAAC,EAAO,QAAAE,CAAQ,CACpC,EF3IA,IAAMC,GAAe,GASfC,GAAS,CAACC,EAAeC,IAAyB,CACtD,GAAIA,EAAK,SAAW,EAIpB,CAAAC,EAAO,KAAK;AAAA,EAAKF,CAAK,KAAKC,EAAK,MAAM,IAAI,EAE1C,QAAWE,KAAOF,EAAK,MAAM,EAAGH,EAAY,EAC1CI,EAAO,KAAK,KAAKC,CAAG,EAAE,EAGpBF,EAAK,OAASH,IAChBI,EAAO,KAAK,eAAUD,EAAK,OAASH,EAAY,OAAO,EAE3D,EAiBaM,GAAc,MAAOC,EAA8B,CAAC,IAAM,CACrE,IAAMC,EAAOD,EAAQ,KAAQ,MAAME,EAAe,EAC5CC,EAAaC,GAAK,KAAKH,EAAMI,CAAU,EAEvCC,EAAO,CAAE,SAAU,CAAC,EAAe,MAAO,CAAC,EAAe,QAAS,CAAC,EAAe,UAAW,CAAE,EAEtG,GAAI,CAACC,GAAWJ,CAAU,EAAG,CAC3BN,EAAO,KAAK,oBAAUQ,CAAU,yCAAoC,EACpE,IAAMG,EAAoB,CAAE,OAAQ,UAAgC,GAAI,GAAM,GAAGF,CAAK,EAEtF,MAAO,CAAE,QAASG,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAAG,kBAAAA,CAAkB,CAC/F,CAEA,GAAI,CAACD,GAAWH,GAAK,KAAKD,EAAYO,EAAa,CAAC,EAAG,CACrDb,EAAO,MAAM,kBAAaQ,CAAU,IAAIK,EAAa,0CAA0C,EAC/F,IAAMF,EAAoB,CAAE,OAAQ,mBAAyC,GAAI,GAAO,GAAGF,CAAK,EAEhG,MAAO,CAAE,QAASG,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAAG,kBAAAA,CAAkB,CAC/F,CAEA,IAAMG,EAAWC,GAAaT,CAAU,EAExC,GAAIU,GAAgBF,CAAQ,EAAG,CAC7Bd,EAAO,MACL,UAAKQ,CAAU,IAAIK,EAAa,+BAA+B,OAAOC,EAAS,aAAa,CAAC,8CAC/F,EACA,IAAMH,EAAoB,CAAE,OAAQ,iBAAuC,GAAI,GAAO,GAAGF,CAAK,EAE9F,MAAO,CAAE,QAASG,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAAG,kBAAAA,CAAkB,CAC/F,CAEA,GAAM,CAAE,SAAAM,EAAU,MAAAC,EAAO,QAAAC,CAAQ,EAAIC,GAAkBd,EAAYQ,CAAQ,EACrEO,EAAUJ,EAAS,OAAS,GAAKC,EAAM,OAAS,GAAKC,EAAQ,OAAS,EAExEE,GACFrB,EAAO,MAAM,UAAKQ,CAAU,sBAAsBA,CAAU,IAAIK,EAAa,GAAG,EAChFb,EAAO,MAAM,uFAAkF,EAC/FH,GAAO,WAAYoB,CAAQ,EAC3BpB,GAAO,0BAA2BqB,CAAK,EACvCrB,GAAO,qCAAsCsB,CAAO,GAEpDnB,EAAO,KAAK,UAAKQ,CAAU,uBAAuBM,EAAS,SAAS,UAAU,EAGhF,IAAMH,EAAoB,CACxB,OAASU,EAAU,QAAU,QAC7B,GAAI,CAACA,EACL,UAAWP,EAAS,UACpB,SAAAG,EACA,MAAAC,EACA,QAAAC,CACF,EAEA,MAAO,CAAE,QAASP,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAAG,kBAAAA,CAAkB,CAC/F,EAEMW,GAA0B,CAC9B,OAAQC,GAAE,KAAK,CAAC,QAAS,QAAS,UAAW,mBAAoB,gBAAgB,CAAC,EAClF,GAAIA,GAAE,QAAQ,EAAE,SAAS,4EAAuE,EAChG,UAAWA,GAAE,OAAO,EACpB,SAAUA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAC5B,MAAOA,GAAE,MAAMA,GAAE,OAAO,CAAC,EACzB,QAASA,GAAE,MAAMA,GAAE,OAAO,CAAC,CAC7B,EAGaC,GAAqBC,EAAc,CAC9C,KAAM,eACN,YACE,6QACF,YAAa,CAAC,EACd,aAAcH,GACd,QAAS,IACApB,GAAY,CAAE,IAAKwB,GAAQ,IAAI,CAAE,CAAC,CAE7C,CAAC,EK9HD,OAAS,KAAAC,OAAS,MCAlB,OAAOC,OAAQ,mBACf,OAAOC,OAAU,YACjB,OAAS,iBAAAC,OAAqB,WAC9B,OAAS,KAAAC,OAAS,MAuBX,IAAMC,GAAmB,MAAOC,GAA2C,CAChF,IAAMC,EAAaC,GAAK,KAAKF,EAASG,EAAkB,EAEpDC,EAEJ,GAAI,CACFA,EAAO,MAAMC,GAAG,KAAKJ,CAAU,CACjC,MAAQ,CACN,MAAM,IAAI,MAAM,GAAGE,EAAkB,iBAAiBF,CAAU,EAAE,CACpE,CAOA,IAAMK,GADY,MAAM,OAFN,GAAGC,GAAcN,CAAU,EAAE,IAAI,UAAU,OAAOG,EAAK,OAAO,CAAC,KAGtD,QAE3B,GAAIE,IAAc,OAChB,MAAM,IAAI,MAAM,GAAGH,EAAkB,OAAOF,CAAU,wBAAwB,EAGhF,IAAMO,EAAiB,OAAOF,GAAc,WAAa,MAAOA,EAA4B,EAAIA,EAE1FG,EAASC,GAAmB,UAAUF,CAAc,EAE1D,GAAI,CAACC,EAAO,QACV,MAAM,IAAI,MAAM,WAAWN,EAAkB,OAAOF,CAAU,KAAKU,GAAE,cAAcF,EAAO,KAAK,CAAC,EAAE,EAGpG,OAAOA,EAAO,IAChB,ECzDA,OAAS,cAAAG,GAAY,aAAAC,GAAW,iBAAAC,OAAqB,UACrD,OAAOC,MAAU,YACjB,OAAS,KAAAC,OAAS,KAiBlB,IAAMC,GAAmB,CACvB,eACA,OACA,gBACA,SACA,eACA,OACA,kBACA,sBACA,UACA,UACA,SACA,YACA,SACF,EAEMC,GAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAahBC,GAAe,IACZF,GAAiB,IAAKG,GACpB,aAAaA,CAAO,EAC5B,EAIUC,GAAkB,MAAOC,GAAwC,CAC5E,GAAI,CAGF,OAFe,MAAMC,GAAE,CAAE,IAAKD,CAAW,CAAC,uBAE5B,OAAO,KAAK,CAC5B,MAAQ,CACN,MAAO,SACT,CACF,EAGaE,GAAgB,MAAOC,EAAgBC,IAAqC,CACvF,GAAI,CAACC,GAAWF,CAAM,EACpB,MAAO,GAGLE,GAAWD,CAAM,GACnB,MAAMH,YAAWG,CAAM,GAGzB,IAAME,EAAc,GAAGH,CAAM,IACvBI,EAAc,GAAGH,CAAM,IAE7B,aAAMH,cAAaJ,GAAa,CAAC,IAAIS,CAAW,IAAIC,CAAW,GAExD,EACT,EAGaC,GAAW,MAAOL,EAAgBC,IAAqC,CAClF,GAAI,CAACC,GAAWF,CAAM,EACpB,MAAO,GAGT,IAAMM,EAAYC,EAAK,QAAQN,CAAM,EAErC,OAAKC,GAAWI,CAAS,GACvBE,GAAUF,EAAW,CAAE,UAAW,EAAK,CAAC,EAG1C,MAAMR,QAAOE,CAAM,IAAIC,CAAM,GAEtB,EACT,EAMaQ,GAAgB,MAAOT,EAAgBC,IAAsC,CACxF,GAAI,CAACC,GAAWF,CAAM,EACpB,MAAO,CAAC,EAGV,GAAI,CAACE,GAAWD,CAAM,EACpB,MAAO,CAAC,mBAAmBA,CAAM,EAAE,EAGrC,IAAME,EAAc,GAAGH,CAAM,IACvBI,EAAc,GAAGH,CAAM,IAG7B,OAFe,MAAMH,kCAAiCJ,GAAa,CAAC,IAAIS,CAAW,IAAIC,CAAW,IAEpF,OACX,MAAM;AAAA,CAAI,EACV,IAAKM,GACGA,EAAK,KAAK,CAClB,EACA,OAAO,OAAO,CACnB,EAOaC,GAAkB,CAACC,EAAoBC,IAAmD,CACrG,IAAMC,EAAaP,EAAK,KAAKK,EAAYG,CAAU,EAE9Cb,GAAWY,CAAU,IAI1BE,GAAcT,EAAK,KAAKO,EAAY,WAAW,EAAGrB,EAAa,EAC/DwB,GAAcH,EAAYD,CAAI,EAChC,EAOaK,GAAoB,CAACC,EAAsBC,IAC/Cb,EAAK,KAAKc,GAAYF,CAAY,EAAGC,CAAI,EAIrCE,GAAgB,CAACC,EAAsBC,IAC7CA,EAIED,EAAW,OAAQH,GACjBI,EAAM,SAASJ,CAAI,CAC3B,EALQG,EAoBLE,GAAkB,CAACC,EAAcC,EAAeC,EAAeC,EAAaC,IAA2B,CAC3G,IAAMC,EAAexB,EAAK,QAAQmB,CAAI,EAChCM,EAAWzB,EAAK,QAAQoB,CAAK,EAEnC,GAAIK,IAAaD,GAAgB,CAACC,EAAS,WAAWD,EAAexB,EAAK,GAAG,EAC3E,MAAM,IAAI0B,EAAe,OAAW,CAClC,UAAW,oBAAoBH,CAAQ,GACvC,cAAe,GAAGF,CAAK,uBAAuBC,CAAG,EACnD,CAAC,CAEL,EAGaK,GAAU,MACrBC,EACAtC,EACAsB,EACAiB,EACAvB,IAC8B,CAC9B,IAAMW,EAAmC,CAAC,EAE1C,QAAWJ,KAAQgB,EAAS,CAC1B,IAAMxB,EAAaM,GAAkBC,EAAcC,CAAI,EAEvD,GAAI,CAAClB,GAAWU,CAAU,EAAG,CAC3BY,EAAM,KAAK,CAAE,KAAAJ,EAAM,OAAQ,EAAG,MAAOe,EAAO,KAAK,OAAQ,QAAS,EAAK,CAAC,EACxE,QACF,CAEA,IAAIE,EAAS,EAEb,QAAWC,KAAQH,EAAO,KAAM,CAC9B,IAAMnC,EAASO,EAAK,KAAKV,EAAYyC,EAAK,MAAM,EAC1CrC,EAASM,EAAK,KAAKK,EAAY0B,EAAK,MAAM,EAEhDb,GAAgB5B,EAAYG,EAAQ,SAAUsC,EAAK,OAAQA,EAAK,IAAI,EACpEb,GAAgBb,EAAYX,EAAQ,SAAUqC,EAAK,OAAQA,EAAK,IAAI,GAEzDA,EAAK,OAAS,YAAc,MAAMvC,GAAcC,EAAQC,CAAM,EAAI,MAAMI,GAASL,EAAQC,CAAM,IAGxGoC,GAEJ,CAEA1B,GAAgBC,EAAYC,CAAI,EAChCW,EAAM,KAAK,CAAE,KAAAJ,EAAM,OAAAiB,EAAQ,MAAOF,EAAO,KAAK,OAAQ,QAAS,EAAM,CAAC,CACxE,CAEA,MAAO,CAAE,OAAQtB,EAAK,OAAQ,OAAQA,EAAK,OAAQ,MAAAW,CAAM,CAC3D,EAOae,GAAc,MACzBpB,EACAiB,EACAvB,IACkD,CAClD,IAAMW,EAA8C,CAAC,EAErD,QAAWJ,KAAQgB,EAAS,CAC1B,IAAMxB,EAAaM,GAAkBC,EAAcC,CAAI,EACjDoB,EAAU,CAACtC,GAAWU,CAAU,EAEjC4B,GACH7B,GAAgBC,EAAYC,CAAI,EAGlCW,EAAM,KAAK,CAAE,KAAAJ,EAAM,QAAAoB,CAAQ,CAAC,CAC9B,CAEA,OAAOhB,CACT,EASaiB,GAAU,MACrBN,EACAtC,EACAsB,EACAiB,IAC+B,CAC/B,IAAMM,EAA6B,CAAC,EAC9BC,EAAgBR,EAAO,KAAK,OAAQG,GACjCA,EAAK,WAAa,EAC1B,EAED,QAAWlB,KAAQgB,EAAS,CAC1B,IAAMxB,EAAaM,GAAkBC,EAAcC,CAAI,EAEvD,GAAKlB,GAAWU,CAAU,EAI1B,QAAW0B,KAAQK,EAAe,CAChC,IAAM3C,EAASO,EAAK,KAAKV,EAAYyC,EAAK,MAAM,EAC1CrC,EAASM,EAAK,KAAKK,EAAY0B,EAAK,MAAM,EAC1CM,EAAU,MAAMnC,GAAcT,EAAQC,CAAM,EAE9C2C,EAAQ,OAAS,GACnBF,EAAQ,KAAK,CAAE,KAAAtB,EAAM,OAAQkB,EAAK,OAAQ,QAAAM,CAAQ,CAAC,CAEvD,CACF,CAEA,OAAOF,CACT,EFvRA,IAAMG,GAAe,GAYRC,GAAa,MAAOC,EAA6B,CAAC,IAAM,CACnE,IAAMC,EAAaD,EAAQ,KAAQ,MAAME,EAAe,EAElDC,EAAS,MAAMC,GAAiBH,CAAU,EAC1CI,EAAU,MAAMC,GAAkB,EAClCC,EAAUC,GAAcH,EAAQ,QAASL,EAAQ,KAAK,EAEtDS,EAAU,MAAMC,GAAQP,EAAQF,EAAYI,EAAQ,aAAcE,CAAO,EAE/E,GAAIE,EAAQ,SAAW,EACrBE,EAAO,KAAK,8BAAuB,MAEnC,SAAWC,KAASH,EAAS,CAC3BE,EAAO,KAAK,UAAKC,EAAM,IAAI,WAAMA,EAAM,MAAM,KAAKA,EAAM,QAAQ,MAAM,cAAc,EAEpF,QAAWC,KAAUD,EAAM,QAAQ,MAAM,EAAGd,EAAY,EACtDa,EAAO,KAAK,MAAME,CAAM,EAAE,EAGxBD,EAAM,QAAQ,OAASd,IACzBa,EAAO,KAAK,gBAAWC,EAAM,QAAQ,OAASd,EAAY,OAAO,CAErE,CAGF,IAAMgB,EAAoB,CAAE,GAAIL,EAAQ,SAAW,EAAG,QAASA,EAAQ,OAAQ,QAAAA,CAAQ,EAEvF,MAAO,CAAE,QAASM,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAAG,kBAAAA,CAAkB,CAC/F,EAEME,GAAyB,CAC7B,GAAIC,GAAE,QAAQ,EAAE,SAAS,iDAAiD,EAC1E,QAASA,GAAE,OAAO,EAClB,QAASA,GAAE,MACTA,GAAE,OAAO,CACP,KAAMA,GAAE,OAAO,EACf,OAAQA,GAAE,OAAO,EACjB,QAASA,GAAE,MAAMA,GAAE,OAAO,CAAC,CAC7B,CAAC,CACH,CACF,EAGaC,GAAoBC,EAAc,CAC7C,KAAM,cACN,YACE,4LACF,YAAa,CACX,MAAOF,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,qCAAqC,CACtF,EACA,aAAcD,GACd,QAAUI,GACDrB,GAAW,CAAE,MAAOqB,EAAO,KAAM,CAAC,CAE7C,CAAC,EG5ED,OAAOC,OAAU,YACjB,OAAS,KAAAC,OAAS,MAkBX,IAAMC,GAAiB,MAAOC,GAAmC,CACtE,IAAMC,EAAaD,EAAQ,KAAQ,MAAME,EAAe,EAElDC,EAAU,MAAMC,GAAkB,EAClCC,EAAUC,GAAcH,EAAQ,QAASH,EAAQ,KAAK,EAEtDO,EAAS,MAAMC,GAAgBP,CAAU,EACzCQ,EAASC,GAAK,SAAST,CAAU,EAEjCU,EAAQ,MAAMC,GAAYT,EAAQ,aAAcE,EAAS,CAAE,OAAAI,EAAQ,OAAAF,CAAO,CAAC,EAEjF,QAAWM,KAAQF,EACjBG,EAAO,KACLD,EAAK,QAAU,iBAAOA,EAAK,IAAI,yCAAsC,aAAMA,EAAK,IAAI,oBACtF,EAGF,IAAME,EAAoB,CAAE,OAAAN,EAAQ,OAAAF,EAAQ,MAAAI,CAAM,EAElD,MAAO,CAAE,QAASK,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAAG,kBAAAA,CAAkB,CAC/F,EAEME,GAA6B,CACjC,OAAQC,GAAE,OAAO,EACjB,OAAQA,GAAE,OAAO,EACjB,MAAOA,GAAE,MAAMA,GAAE,OAAO,CAAE,KAAMA,GAAE,OAAO,EAAG,QAASA,GAAE,QAAQ,CAAE,CAAC,CAAC,CACrE,EAGaC,GAAwBC,EAAc,CACjD,KAAM,kBACN,YACE,iJACF,YAAa,CACX,MAAOF,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,qCAAqC,CACtF,EACA,aAAcD,GACd,QAAUI,GACDtB,GAAe,CAAE,MAAOsB,EAAO,MAAO,iBAAkBA,EAAO,gBAAiB,CAAC,CAE5F,CAAC,EC3DD,OAAOC,OAAa,oBACpB,OAAOC,OAAU,YACjB,OAAOC,OAAa,eACpB,OAAS,KAAAC,OAAS,MA2BX,IAAMC,GAAa,MAAOC,GAA+B,CAC9D,IAAMC,EAAaD,EAAQ,KAAQ,MAAME,EAAe,EAElDC,EAAS,MAAMC,GAAiBH,CAAU,EAC1CI,EAAU,MAAMC,GAAkB,EAClCC,EAAUC,GAAcH,EAAQ,QAASL,EAAQ,KAAK,EAEvDA,EAAQ,kBACK,MAAMS,GACpB,CAAE,QAAS,0BAA0BC,GAAK,SAAST,CAAU,CAAC,SAASM,EAAQ,MAAM,WAAY,EACjG,CAAE,OAAQI,GAAQ,MAAO,CAC3B,GAGEA,GAAQ,KAAK,CAAC,EAIlB,IAAMC,EAAS,MAAMC,GAAgBZ,CAAU,EACzCa,EAASJ,GAAK,SAAST,CAAU,EAEjCc,EAAoB,MAAMC,GAAQb,EAAQF,EAAYI,EAAQ,aAAcE,EAAS,CAAE,OAAAO,EAAQ,OAAAF,CAAO,CAAC,EAE7G,QAAWK,KAAQF,EAAkB,MAC/BE,EAAK,QACPC,EAAO,KAAK,iBAAOD,EAAK,IAAI,wCAAmC,EAE/DC,EAAO,KAAK,UAAKD,EAAK,IAAI,YAAYA,EAAK,MAAM,IAAIA,EAAK,KAAK,mBAAmB,EAItF,MAAO,CAAE,QAASE,EAAY,KAAK,UAAUJ,EAAmB,KAAM,CAAC,CAAC,EAAG,kBAAAA,CAAkB,CAC/F,EAEMK,GAAyB,CAC7B,OAAQC,GAAE,OAAO,EACjB,OAAQA,GAAE,OAAO,EACjB,MAAOA,GAAE,MACPA,GAAE,OAAO,CACP,KAAMA,GAAE,OAAO,EACf,OAAQA,GAAE,OAAO,EACjB,MAAOA,GAAE,OAAO,EAChB,QAASA,GAAE,QAAQ,CACrB,CAAC,CACH,CACF,EAGaC,GAAoBC,EAAc,CAC7C,KAAM,cACN,YACE,uLACF,YAAa,CACX,MAAOF,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,qCAAqC,CACtF,EACA,aAAcD,GACd,QAAUI,GACDzB,GAAW,CAAE,MAAOyB,EAAO,MAAO,iBAAkBA,EAAO,gBAAiB,CAAC,CAExF,CAAC,ECzFD,OAAS,KAAAC,OAAS,MAUX,IAAMC,GAAU,SAAY,CACjC,IAAMC,EAAaC,GAAY,QAE/BC,EAAO,KAAKF,CAAU,EAEtB,IAAMG,EAAoB,CAAE,QAASH,CAAW,EAEhD,MAAO,CACL,QAASI,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAGaE,GAAiBC,EAAc,CAC1C,KAAM,UACN,YAAa,4CACb,YAAa,CAAC,EACd,aAAc,CACZ,QAASC,GAAE,OAAO,EAAE,SAAS,qDAAqD,CACpF,EACA,QAASR,EACX,CAAC,EC/BD,OAAOS,OAAa,oBACpB,OAAS,KAAAC,OAAS,MAClB,OAAS,KAAAC,OAAS,KCHlB,OAAOC,OAAQ,mBACf,OAAOC,OAAU,YA2BV,IAAMC,GAA8B,MACzCC,GAC+C,CAC/C,GAAM,CAAE,cAAAC,EAAe,YAAAC,CAAY,EAAIF,EAEjCG,EAAeL,GAAK,QAAQG,CAAa,EAE3CG,EAEJ,GAAI,CACFA,EAAM,MAAMP,GAAG,SAASI,EAAe,OAAO,CAChD,OAASI,EAAO,CACd,MAAM,IAAI,MAAM,sCAAsCJ,CAAa,KAAMI,EAAgB,OAAO,EAAE,CACpG,CAEA,IAAIC,EAEJ,GAAI,CACFA,EAAS,KAAK,MAAMF,CAAG,CACzB,OAASC,EAAO,CACd,MAAM,IAAI,MACR,mBAAmBJ,CAAa,iDAAkDI,EAAgB,OAAO,EAC3G,CACF,CAEA,IAAME,EAAkBD,EAAO,SAAW,CAAC,EACrCE,EAAwB,IAAI,IAChCD,EAAgB,IAAKE,GACZX,GAAK,QAAQK,EAAcM,EAAM,IAAI,CAC7C,CACH,EAEMC,EAAkB,CAAC,EACnBC,EAAoB,CAAC,EAE3B,QAAWC,KAAcV,EAAa,CACpC,IAAMW,EAAef,GAAK,QAAQc,CAAU,EAE5C,GAAIJ,EAAsB,IAAIK,CAAY,EAAG,CAC3CF,EAAQ,KAAKC,CAAU,EACvB,QACF,CAEA,IAAME,EAAehB,GAAK,SAASK,EAAcU,CAAY,EAE7DN,EAAgB,KAAK,CAAE,KAAMO,CAAa,CAAC,EAC3CN,EAAsB,IAAIK,CAAY,EACtCH,EAAM,KAAKE,CAAU,CACvB,CAEA,OAAAN,EAAO,QAAUC,EAEjB,MAAMV,GAAG,UAAUI,EAAe,GAAG,KAAK,UAAUK,EAAQ,KAAM,CAAC,CAAC;AAAA,EAAM,OAAO,EAE1E,CAAE,MAAAI,EAAO,QAAAC,CAAQ,CAC1B,ECnFA,OAAS,KAAAI,OAAS,KASX,IAAMC,GAAe,MAAOC,GAAyC,CAC1E,MAAMF,YAAWE,CAAa,EAChC,ECXA,OAAOC,OAAQ,mBACf,OAAOC,OAAU,YCDjB,OAAOC,OAAQ,mBACf,OAAOC,OAAU,YA2BV,IAAMC,GAAmC,MAC9CC,GACoD,CACpD,GAAM,CAAE,cAAAC,EAAe,YAAAC,CAAY,EAAIF,EAEjCG,EAAeL,GAAK,QAAQG,CAAa,EAE3CG,EAEJ,GAAI,CACFA,EAAM,MAAMP,GAAG,SAASI,EAAe,OAAO,CAChD,OAASI,EAAO,CACd,MAAM,IAAI,MAAM,sCAAsCJ,CAAa,KAAMI,EAAgB,OAAO,EAAE,CACpG,CAEA,IAAIC,EAEJ,GAAI,CACFA,EAAS,KAAK,MAAMF,CAAG,CACzB,OAASC,EAAO,CACd,MAAM,IAAI,MACR,mBAAmBJ,CAAa,iDAAkDI,EAAgB,OAAO,EAC3G,CACF,CAEA,IAAME,EAAkBD,EAAO,SAAW,CAAC,EACrCE,EAAsB,IAAI,IAC9BN,EAAY,IAAKO,GACRX,GAAK,QAAQW,CAAU,CAC/B,CACH,EAEMC,EAAuB,IAAI,IAE3BC,EAAkBJ,EAAgB,OAAQK,GAAU,CACxD,IAAMC,EAAoBf,GAAK,QAAQK,EAAcS,EAAM,IAAI,EAE/D,OAAIJ,EAAoB,IAAIK,CAAiB,GAC3CH,EAAqB,IAAIG,CAAiB,EAEnC,IAGF,EACT,CAAC,EAEDP,EAAO,QAAUK,EAEjB,MAAMd,GAAG,UAAUI,EAAe,GAAG,KAAK,UAAUK,EAAQ,KAAM,CAAC,CAAC;AAAA,EAAM,OAAO,EAEjF,IAAMQ,EAAoB,CAAC,EACrBC,EAAqB,CAAC,EAE5B,QAAWN,KAAcP,EAAa,CACpC,IAAMc,EAAelB,GAAK,QAAQW,CAAU,EAExCC,EAAqB,IAAIM,CAAY,EACvCF,EAAQ,KAAKL,CAAU,EAEvBM,EAAS,KAAKN,CAAU,CAE5B,CAEA,MAAO,CAAE,QAAAK,EAAS,SAAAC,CAAS,CAC7B,EDvDO,IAAME,GAAkC,MAC7CC,GACmD,CACnD,GAAM,CAAE,cAAAC,EAAe,YAAAC,EAAa,gBAAAC,CAAgB,EAAIH,EAElDI,EAAeC,GAAK,QAAQJ,CAAa,EACzCK,EAAcD,GAAK,QAAQ,GAAGH,CAAW,UAAU,EAEnDK,EAAM,MAAMC,GAAG,SAASP,EAAe,OAAO,EAG9CQ,EAFS,KAAK,MAAMF,CAAG,EAEE,SAAW,CAAC,EAErCG,EAAuB,IAAI,IAC/BP,EAAgB,IAAKQ,GACZN,GAAK,QAAQ,GAAGH,CAAW,IAAIS,CAAM,EAAE,CAC/C,CACH,EAEMC,EAAgC,CAAC,EAEvC,QAAWC,KAASJ,EAAiB,CACnC,IAAMK,EAAoBT,GAAK,QAAQD,EAAcS,EAAM,IAAI,GAEvCC,IAAsBR,GAAeQ,EAAkB,WAAW,GAAGR,CAAW,GAAG,IAEpF,CAACI,EAAqB,IAAII,CAAiB,GAChEF,EAAoB,KAAKE,CAAiB,CAE9C,CAEA,IAAIC,EAAoB,CAAC,EAErBH,EAAoB,OAAS,IAM/BG,GALe,MAAMC,GAAiC,CACpD,cAAAf,EACA,YAAaW,CACf,CAAC,GAEgB,SAGnB,IAAMK,EAAqBd,EAAgB,IAAKQ,GACvC,GAAGT,CAAW,IAAIS,CAAM,EAChC,EAEK,CAAE,MAAAO,CAAM,EACZD,EAAmB,OAAS,EACxB,MAAME,GAA4B,CAAE,cAAAlB,EAAe,YAAagB,CAAmB,CAAC,EACpF,CAAE,MAAO,CAAC,CAAc,EAE9B,MAAO,CAAE,MAAAC,EAAO,QAAAH,CAAQ,CAC1B,EEzFA,OAAOK,OAAU,YAMV,IAAMC,GAA6B,CAACC,EAAqBC,IAC1DH,GAAK,WAAWE,CAAW,EACtBA,EAGFF,GAAK,QAAQG,EAAaD,CAAW,ECwBvC,IAAME,GAAsB,MAAOC,GAAuE,CAC/G,GAAM,CAAE,YAAAC,EAAa,YAAAC,EAAa,gBAAAC,EAAiB,aAAAC,CAAa,EAAIJ,EAEpE,GAAI,CAACI,EAAa,oBAChB,OAAAC,EAAO,KAAK,+DAAqD,EAE1D,CAAE,IAAK,GAAO,MAAO,EAAG,QAAS,CAAE,EAG5C,IAAMC,EAAgBC,GAA2BH,EAAa,oBAAqBH,CAAW,EAE9F,GAAI,CACF,GAAM,CAAE,MAAAO,EAAO,QAAAC,CAAQ,EAAI,MAAMC,GAAgC,CAC/D,cAAAJ,EACA,YAAAJ,EACA,gBAAAC,CACF,CAAC,EAED,OAAIA,EAAgB,OAAS,GAC3B,MAAMQ,GAAaL,CAAa,EAG3B,CAAE,IAAK,GAAM,MAAOE,EAAM,OAAQ,QAASC,EAAQ,MAAO,CACnE,OAASG,EAAO,CACd,OAAAP,EAAO,KAAK,CAAE,MAAAO,CAAM,EAAG,6DAAmDN,CAAa,EAAE,EAElF,CAAE,IAAK,GAAO,MAAO,EAAG,QAAS,CAAE,CAC5C,CACF,EC/DA,OAAS,KAAAO,OAAS,KAmBX,IAAMC,GAA2B,MACtCC,GAC4C,CAC5C,GAAM,CAAE,YAAAC,CAAY,EAAID,EAExB,QAAWE,KAAcD,EACvB,MAAMH,eAAcI,CAAU,GAGhC,MAAO,CAAE,MAAOD,CAAY,CAC9B,EC7BA,OAAS,KAAAE,OAAS,KA2BX,IAAMC,GAAmB,MAAOC,GAAiE,CACtG,GAAM,CAAE,YAAAC,EAAa,YAAAC,EAAa,gBAAAC,CAAgB,EAAIH,EAEtD,GAAIG,EAAgB,SAAW,EAC7B,MAAO,CAAE,IAAK,GAAO,MAAO,EAAG,QAAS,CAAE,EAG5C,IAAMC,EAAQ,CACZH,EACA,GAAGE,EAAgB,IAAKE,GACf,GAAGH,CAAW,IAAIG,CAAM,EAChC,CACH,EAEA,GAAI,CACF,aAAMC,SAAQF,CAAK,GAEZ,CAAE,IAAK,GAAM,MAAOD,EAAgB,OAAQ,QAAS,CAAE,CAChE,OAASI,EAAO,CACd,OAAAC,EAAO,KAAK,CAAE,MAAAD,CAAM,EAAG,2CAAiC,EAEjD,CAAE,IAAK,GAAO,MAAO,EAAG,QAAS,CAAE,CAC5C,CACF,EClDA,OAAS,KAAAE,OAAS,KAiCX,IAAMC,GAAoB,MAAOC,GAAmE,CACzG,GAAM,CAAE,YAAAC,EAAa,YAAAC,EAAa,kBAAAC,CAAkB,EAAIH,EAElDI,EAAQ,CACZH,EACA,GAAGE,EAAkB,IAAKE,GACjB,GAAGH,CAAW,IAAIG,CAAM,EAChC,CACH,EAEA,GAAI,CACF,aAAMC,iBAAgBF,CAAK,GAE3BG,EAAO,KACL,4CAAuCJ,EAAkB,MAAM,8EACjE,EAEO,CAAE,IAAK,EAAK,CACrB,OAASK,EAAO,CACd,OAAAD,EAAO,KAAK,CAAE,MAAAC,CAAM,EAAG,8CAAoC,EAEpD,CAAE,IAAK,EAAM,CACtB,CACF,EC5CO,IAAMC,GAAeC,GAAwB,CAClD,MAAM,IAAI,MAAM,qBAAqB,KAAK,UAAUA,CAAK,CAAC,EAAE,CAC9D,ECUO,IAAMC,GAAwB,MACnCC,GAC4C,CAC5C,GAAM,CAAE,YAAAC,EAAa,YAAAC,EAAa,SAAAC,CAAS,EAAIH,EAEzCI,EAAS,MAAMC,EAAkB,EACjCC,EAAOC,EAAsBH,CAAM,EAEnCI,EAAcL,EAAS,IAAKM,GACzB,GAAGP,CAAW,IAAIO,CAAM,EAChC,EAEKC,EAA2C,CAAC,EAElD,QAAWC,KAAOL,EAChB,OAAQK,EAAI,SAAU,CACpB,IAAK,SAAU,CACb,GAAI,CAACA,EAAI,OAAO,oBAAqB,CACnCC,EAAO,KAAK,6FAAmF,EAE/FF,EAAS,KAAK,CAAE,IAAK,GAAO,SAAU,SAAU,MAAO,EAAG,QAAS,CAAE,CAAC,EACtE,KACF,CAEA,IAAMG,EAAgBC,GAA2BH,EAAI,OAAO,oBAAqBV,CAAW,EAEtF,CAAE,MAAAc,EAAO,QAAAC,CAAQ,EAAI,MAAMC,GAA4B,CAAE,cAAAJ,EAAe,YAAAL,CAAY,CAAC,EAErFU,EAAgBF,EAAQ,OAAS,EAAI,KAAKA,EAAQ,MAAM,oBAAsB,GAEpFJ,EAAO,KAAK,gBAAWG,EAAM,MAAM,iBAAiBF,CAAa,GAAGK,CAAa,EAAE,EAEnF,MAAMC,GAAaN,CAAa,EAEhCH,EAAS,KAAK,CAAE,IAAK,GAAM,SAAU,SAAU,MAAOK,EAAM,OAAQ,QAASC,EAAQ,MAAO,CAAC,EAC7F,KACF,CACA,IAAK,MAAO,CACV,GAAM,CAAE,MAAAD,CAAM,EAAI,MAAMK,GAAyB,CAAE,YAAAZ,CAAY,CAAC,EAEhEI,EAAO,KAAK,gBAAWG,EAAM,MAAM,6BAA6B,EAEhEL,EAAS,KAAK,CAAE,IAAK,GAAM,SAAU,MAAO,MAAOK,EAAM,OAAQ,QAAS,CAAE,CAAC,EAC7E,KACF,CACA,QACEM,GAAYV,CAAG,CAEnB,CAGF,OAAOD,CACT,ECrDO,IAAMY,GAAmB,MAAOC,GAAmE,CACxG,IAAMC,EAAS,MAAMC,EAAkB,EACjCC,EAAOC,EAAsBH,CAAM,EAEnCI,EAAsC,CAAC,EAE7C,QAAWC,KAAOH,EAChB,OAAQG,EAAI,SAAU,CACpB,IAAK,SAAU,CACb,IAAMC,EAAU,MAAMC,GAAoB,CAAE,GAAGR,EAAM,aAAcM,EAAI,MAAO,CAAC,EAE/ED,EAAS,KAAK,CAAE,GAAGE,EAAS,SAAU,QAAS,CAAC,EAChD,KACF,CACA,IAAK,MAAO,CACV,IAAMA,EAAU,MAAME,GAAiBT,CAAI,EAE3CK,EAAS,KAAK,CAAE,GAAGE,EAAS,SAAU,KAAM,CAAC,EAC7C,KACF,CACA,QACEG,GAAYJ,CAAG,CAEnB,CAGF,OAAOD,CACT,EC1CO,IAAMM,GAAoBC,GAA8C,CAC7E,OAAQA,EAAU,CAChB,IAAK,SACH,MAAO,SAET,IAAK,MACH,MAAO,MAET,KAAK,OACH,MAAO,MAET,QACE,OAAOC,GAAYD,CAAQ,CAE/B,CACF,ECsBO,IAAME,GAA2B,MACtCC,GAC+C,CAC/C,GAAM,CAAE,YAAAC,EAAa,YAAAC,EAAa,iBAAAC,EAAkB,iBAAAC,EAAkB,oBAAAC,CAAoB,EAAIL,EAE9F,GAAII,EAAiB,SAAW,EAC9B,MAAO,CAAC,EAGV,IAAME,EAAS,MAAMC,EAAkB,EACjCC,EAAOC,EAAsBH,CAAM,EAEnCI,EAAcN,EAAiB,IAAKO,GACjC,GAAGT,CAAW,IAAIS,CAAM,EAChC,EAEKC,EAAoBT,EAAiB,OAAQQ,GAC1C,CAACP,EAAiB,SAASO,CAAM,CACzC,EAEKE,EAA8C,CAAC,EAErD,QAAWC,KAAON,EAChB,OAAQM,EAAI,SAAU,CACpB,IAAK,SAAU,CACbD,EAAS,KAAK,MAAME,GAAiB,CAAE,IAAAD,EAAK,YAAAb,EAAa,YAAAS,CAAY,CAAC,CAAC,EACvE,KACF,CACA,IAAK,MAAO,CACVG,EAAS,KAAK,MAAMG,GAAc,CAAE,YAAAf,EAAa,YAAAC,EAAa,kBAAAU,EAAmB,oBAAAP,CAAoB,CAAC,CAAC,EACvG,KACF,CACA,QACEY,GAAYH,CAAG,CAEnB,CAGF,OAAOD,CACT,EASME,GAAmB,MAAOf,GAAyE,CACvG,GAAM,CAAE,IAAAc,EAAK,YAAAb,EAAa,YAAAS,CAAY,EAAIV,EAE1C,GAAI,CAACc,EAAI,OAAO,oBACd,MAAO,CAAE,SAAU,SAAU,UAAW,GAAM,QAAS,CAAC,CAAE,EAG5D,IAAMI,EAAgBC,GAA2BL,EAAI,OAAO,oBAAqBb,CAAW,EAE5F,GAAI,CACF,GAAM,CAAE,QAAAmB,CAAQ,EAAI,MAAMC,GAAiC,CAAE,cAAAH,EAAe,YAAAR,CAAY,CAAC,EAEzF,OAAIU,EAAQ,OAAS,GACnBE,EAAO,KAAK,kBAAaF,EAAQ,MAAM,mBAAmBF,CAAa,EAAE,EAGpE,CAAE,SAAU,SAAU,UAAW,GAAM,QAAAE,CAAQ,CACxD,OAASG,EAAO,CACd,OAAAD,EAAO,KAAK,CAAE,MAAAC,CAAM,EAAG,qDAA2CL,CAAa,EAAE,EAE1E,CAAE,SAAU,SAAU,UAAW,GAAM,QAAS,CAAC,CAAE,CAC5D,CACF,EAeMF,GAAgB,MAAOhB,GAAsE,CACjG,GAAM,CAAE,YAAAC,EAAa,YAAAC,EAAa,kBAAAU,EAAmB,oBAAAP,CAAoB,EAAIL,EAE7E,OAAKK,GAQL,MAAMmB,GAAkB,CAAE,YAAAvB,EAAa,YAAAC,EAAa,kBAAAU,CAAkB,CAAC,EAEhE,CAAE,SAAU,MAAO,UAAW,GAAM,QAAS,CAAC,CAAE,IATrDU,EAAO,KACL,6HACF,EAEO,CAAE,SAAU,MAAO,UAAW,GAAM,QAAS,CAAC,CAAE,EAM3D,ECzIO,IAAMG,GAAY,CAAC,YAAa,MAAM,Ef4B7C,IAAMC,GAAc,UACdC,GAAc,UAgBPC,GAAe,MAAOC,GAAoC,CACrE,GAAM,CAAE,iBAAAC,EAAkB,IAAAC,EAAK,SAAAC,EAAU,cAAAC,EAAe,KAAAC,CAAK,EAAIL,EAE3DM,EAAMN,EAAQ,KAAOA,EAAQ,OAEnC,MAAMO,EAAwB,CAAE,UAAW,mBAAoB,eAAgB,KAAM,CAAC,EAEtF,GAAI,CACF,IAAMC,EAAmB,MAAMC,EAAoB,SAAS,EACtDC,EAAc,MAAMC,EAAe,EAEnCC,EAAc,GAAGF,CAAW,GAAGG,CAAoB,GAEzD,MAAMC,GAAwB,GAAGF,CAAW,IAAId,EAAW,EAAE,EAC7D,MAAMgB,GAAwB,GAAGF,CAAW,IAAIf,EAAW,EAAE,EAE7D,IAAIkB,EAAoC,CAAC,EAEzC,GAAIZ,EACFY,EAA0BZ,EAAS,MAAM,GAAG,EAAE,IAAKa,GAC1CC,GAAiBC,GAAgBF,EAAE,KAAK,CAAC,CAAC,CAClD,MACI,CACL,IAAMG,EAAiB,MAAMC,EAAsB,EAE7CC,EAAiBF,EAAe,IAAKG,IAClCA,GAAG,MACX,EAED,GAAID,EAAe,SAAW,EAC5B,OAAAE,EAAO,KAAK,6CAAmC,EAE/CC,EAAY,MAAM,EAEX,CACL,QAASC,EAAY,KAAK,UAAU,CAAE,iBAAkB,CAAC,EAAG,MAAO,CAAE,EAAG,KAAM,CAAC,CAAC,EAChF,kBAAmB,CAAE,iBAAkB,CAAC,EAAG,MAAO,CAAE,CACtD,EAGF,GAAIvB,EACFa,EAA0BM,MACrB,CACLG,EAAY,eAAe,EAE3B,IAAME,GAAe,IAAI,IACvBP,EAAe,IAAKG,IACX,CAACA,GAAG,OAAQK,EAAkBL,GAAG,KAAK,CAAC,CAC/C,CACH,EAEMM,GAAe,MAAMC,EAAoB,EAE/Cd,EAA0B,MAAMe,GAC9BC,EAAwB,CAAE,SAAUV,EAAgB,aAAAO,GAAc,MAAOF,EAAa,CAAC,EACvF,CAAE,SAAU,EAAK,CACnB,CACF,CACF,CAGIxB,EACFsB,EAAY,UAAU,QAAS,EAAI,EAEnCA,EAAY,UAAU,aAAcQ,GAAoBjB,CAAuB,CAAC,EAIlF,MAAMkB,EAAchC,EAAkB,+DAA+D,EAGhGA,GACHuB,EAAY,UAAU,QAAS,EAAI,EAGrC,IAAMU,EAAS,MAAMC,EAAkB,EAKjCC,EAAmB9B,IAAQ+B,EAAsBH,CAAM,EAAE,OAAS,EAAI,YAAc,QAE1FV,EAAY,UAAU,QAASY,CAAO,EAEtC,IAAME,EACJlC,GACA8B,EAAO,WAAW,qBACjB,MAAMK,GAAQ,CAAE,QAAS,2CAA4C,CAAC,EAErE,OAAOnC,EAAkB,KAAe8B,EAAO,WAAW,sBAAwB,QACpFV,EAAY,eAAe,EAGzBc,EACFd,EAAY,UAAU,mBAAoB,EAAI,EAE9CA,EAAY,UAAU,sBAAuB,EAAI,EAGnD,IAAMgB,EACJnC,GAAQ6B,EAAO,WAAW,YAAe,MAAMK,GAAQ,CAAE,QAAS,iCAAkC,CAAC,EAEnG,OAAOlC,EAAS,KAAe6B,EAAO,WAAW,aAAe,QAClEV,EAAY,eAAe,EAGzBgB,EACFhB,EAAY,UAAU,SAAU,EAAI,EAEpCA,EAAY,UAAU,YAAa,EAAI,EAGzC,GAAM,CAAE,iBAAAiB,CAAiB,EAAIC,GAAoB,CAC/C,wBAAA3B,EACA,iBAAAP,CACF,CAAC,EAEKmC,EAAmB,MAAMC,GAAgBH,EAAkB7B,CAAW,EAQ5E,GANAiC,GAAWF,CAAgB,EAEvBP,IAAY,aACd,MAAMU,GAAsB,CAAE,YAAApC,EAAa,YAAAE,EAAa,SAAU+B,CAAiB,CAAC,EAGlFL,EACF,QAAWS,KAAUJ,EACnB,MAAMK,YAAWpC,CAAW,IAAImC,CAAM,GACtC,MAAMC,YAIV,GAAIR,EAAY,CACd,IAAMS,EAAW,MAAMC,EAAY,EAC7BC,EAAiB,MAAMC,GAAwB,EAErD,QAAWL,MAAUJ,EAAkB,CACrC,IAAMU,GAAQC,GAAwB,CAAE,SAAAL,EAAU,OAAAF,EAAO,CAAC,EAItDI,EAAe,IAAII,GAAsBF,EAAK,CAAC,GAInD,MAAMG,GAA4B,CAChC,IAAK,GAAG5C,CAAW,IAAImC,EAAM,GAC7B,MAAAM,EACF,CAAC,CACH,CACF,CAEA7B,EAAY,MAAM,EAElB,IAAMiC,GAAoB,CACxB,iBAAAd,EACA,MAAOA,EAAiB,MAC1B,EAEA,MAAO,CACL,QAASlB,EAAY,KAAK,UAAUgC,GAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,EACF,CACF,OAASC,EAAO,CAId,MAAIC,GAAqBD,CAAK,EAASA,GAEvCnC,EAAO,MAAM,CAAE,MAAAmC,CAAM,EAAG,iCAA4B,EAC9C,IAAIE,EAAeF,EAAO,CAC9B,UAAW,mBACX,YAAa,uEACf,CAAC,EACH,CACF,EAKM5C,GAA0B,MAAOF,GAAuC,CAC5E,MAAMoC,cAAapC,CAAW,EAChC,EAUM8B,GAAuBmB,GAAkE,CAC7F,GAAM,CAAE,wBAAA9C,EAAyB,iBAAAP,CAAiB,EAAIqD,EAEhDC,EAAqBtD,EAAiB,OAAQuC,GAC3CgB,GAAgBhB,CAAM,CAC9B,EAMD,MAAO,CAAE,iBAJgBhC,EAAwB,OAAQgC,GAChD,CAACe,EAAmB,SAASf,CAAM,CAC3C,CAEyB,CAC5B,EAKMH,GAAkB,MAAOoB,EAAoBpD,IAA2C,CAC5F,IAAMqD,EAAU,MAAM,QAAQ,WAC5BD,EAAS,IAAI,MAAOjB,GAAW,CAC7B,IAAMmB,EAAe,GAAGtD,CAAW,IAAImC,CAAM,GAE7C,aAAMC,sBAAqBkB,CAAY,IAAInB,CAAM,GACjD,MAAMC,GAAE,CAAE,IAAKkB,CAAa,CAAC,gBAEtBnB,CACT,CAAC,CACH,EAEMoB,EAAoB,CAAC,EAE3B,OAAW,CAACC,EAAOC,CAAM,IAAKJ,EAAQ,QAAQ,EAC5C,GAAII,EAAO,SAAW,YACpBF,EAAQ,KAAKE,EAAO,KAAK,MACpB,CACL,IAAMtB,EAASiB,EAASI,CAAK,EACvBE,EAAM,IAAIV,EAAeS,EAAO,OAAQ,CAC5C,UAAW,wBAAwBtB,CAAM,GACzC,YAAa,2DACf,CAAC,EAEDxB,EAAO,MAAM,CAAE,MAAO8C,EAAO,OAAQ,IAAKC,EAAI,OAAQ,CAAC,CACzD,CAGF,OAAOH,CACT,EAKMtB,GAAcsB,GAA4B,CAC9C,GAAIA,EAAQ,OAAS,EAAG,CACtB5C,EAAO,KAAK,+BAA0B,EACtC,QAAWwB,KAAUoB,EACnB5C,EAAO,KAAKwB,CAAM,EAEpBxB,EAAO,KAAK,EAAE,CAChB,MACEA,EAAO,KAAK,6CAAmC,CAEnD,EAGagD,GAAsBC,EAAc,CAC/C,KAAM,gBACN,YACE,wYACF,YAAa,CACX,IAAKC,GACF,QAAQ,EACR,SAAS,EACT,SACC,kMACF,EACF,SAAUA,GACP,OAAO,EACP,SAAS,EACT,SACC,kMACF,EACF,IAAKA,GACF,KAAKC,EAAS,EACd,SAAS,EACT,SACC,uWACF,EACF,OAAQD,GACL,KAAKC,EAAS,EACd,SAAS,EACT,SAAS,2EAA2E,EACvF,cAAeD,GACZ,QAAQ,EACR,SAAS,EACT,SACC,+LACF,EACF,KAAMA,GACH,QAAQ,EACR,SAAS,EACT,SACC,iXACF,CACJ,EACA,aAAc,CACZ,iBAAkBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,uCAAuC,EACtF,MAAOA,GAAE,OAAO,EAAE,SAAS,iCAAiC,CAC9D,EACA,QAAS1E,EACX,CAAC,EgBjWD,OAAS,KAAA4E,OAAS,MAmBX,IAAMC,GAAgB,SAAY,CACvC,IAAMC,EAAmB,MAAMC,EAAoB,SAAS,EAE5D,GAAID,EAAiB,SAAW,EAC9B,OAAAE,EAAO,KAAK,wCAA8B,EAEnC,CACL,QAASC,EAAY,KAAK,UAAU,CAAE,UAAW,CAAC,EAAG,MAAO,CAAE,EAAG,KAAM,CAAC,CAAC,EACzE,kBAAmB,CAAE,UAAW,CAAC,EAAG,MAAO,CAAE,CAC/C,EAGF,GAAM,CAACC,EAAgBC,CAAgB,EAAI,MAAM,QAAQ,IAAI,CAACC,EAAsB,EAAGC,EAAoB,CAAC,CAAC,EAEvGC,EAAe,IAAI,IACvBJ,EAAe,IAAKK,GACX,CAACA,EAAG,OAAQC,EAAkBD,EAAG,KAAK,CAAC,CAC/C,CACH,EAGME,EAA4BX,EAAiB,QAASY,GAAW,CACrE,IAAMC,EAAKC,EAAgBF,CAAM,EAEjC,GAAI,CAACC,EAAI,MAAO,CAAC,EAIjB,IAAME,EAAUC,EAAaH,CAAE,EACzBI,EAAOT,EAAa,IAAII,CAAM,GAAK,UACnCM,EAAcb,EAAiB,IAAIc,EAAeN,CAAE,CAAC,GAAK,KAEhE,MAAO,CAAC,CAAE,QAAAE,EAAS,KAAAE,EAAM,YAAAC,CAAY,CAAC,CACxC,CAAC,EAGKE,EAAmB,KAAK,IAC5B,GAAGT,EAAU,IAAKU,GACTA,EAAE,QAAQ,MAClB,CACH,EAEMC,EAAiBX,EAAU,IAAKY,GAAa,CACjD,IAAMC,EAAQC,GAAmBF,EAAS,QAASA,EAAS,KAAMH,CAAgB,EAElF,OAAIG,EAAS,YACJ,GAAGC,CAAK,KAAKD,EAAS,WAAW,GAGnCC,CACT,CAAC,EAEDtB,EAAO,KAAK,6BAAsB,EAClCA,EAAO,KAAK;AAAA,EAAKoB,EAAe,KAAK;AAAA,CAAI,CAAC;AAAA,CAAI,EAE9C,IAAMI,EAAoB,CACxB,UAAAf,EACA,MAAOA,EAAU,MACnB,EAEA,MAAO,CACL,QAASR,EAAY,KAAK,UAAUuB,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,EAGaC,GAAuBC,EAAc,CAChD,KAAM,iBACN,YACE,qIACF,YAAa,CAAC,EACd,aAAc,CACZ,UAAWC,GACR,MACCA,GAAE,OAAO,CACP,QAASA,GAAE,OAAO,EAAE,SAAS,iBAAiB,EAC9C,KAAMA,GAAE,KAAK,CAAC,UAAW,QAAQ,CAAC,EAAE,SAAS,cAAc,EAC3D,YAAaA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0BAA0B,CACxE,CAAC,CACH,EACC,SAAS,oCAAoC,EAChD,MAAOA,GAAE,OAAO,EAAE,SAAS,qBAAqB,CAClD,EACA,QAAS9B,EACX,CAAC,ECxGD,OAAS,KAAA+B,OAAS,MAqCX,IAAMC,GAAkB,SAAY,CACzC,GAAI,CACF,IAAMC,EAAc,MAAMC,EAAe,EACnCC,EAAc,GAAGF,CAAW,GAAGG,CAAoB,GACnDC,EAAkB,MAAMC,EAAoB,SAAS,EACrDC,EAAW,MAAMC,EAAY,EAG7BC,EAAa,MAAMC,GAAU,CAAE,gBAAAL,EAAiB,SAAAE,CAAS,CAAC,EAI1D,CAACI,EAAa,CAAE,OAAQC,CAAW,CAAC,EAAI,MAAM,QAAQ,IAAI,CAC9DC,GAAiB,CAAE,YAAAZ,EAAa,YAAAE,EAAa,gBAAAE,CAAgB,CAAC,EAC9DS,GAAW,CAAE,YAAAX,EAAa,gBAAAE,EAAiB,SAAAE,CAAS,CAAC,CACvD,CAAC,EAEKQ,EAAgC,CACpC,WAAAN,EACA,WAAAG,EACA,aAAcD,EACX,OAAQK,GACAA,EAAQ,GAChB,EACA,IAAKA,GACGA,EAAQ,QAChB,EACH,gBAAiBL,EAAY,OAAO,CAACM,EAAKD,IACjCC,EAAMD,EAAQ,MACpB,CAAC,EACJ,kBAAmBL,EAAY,OAAO,CAACM,EAAKD,IACnCC,EAAMD,EAAQ,QACpB,CAAC,CACN,EAEA,OAAAE,GAAWH,EAAQ,CACjB,OAAQJ,EAAY,KAAMK,GACjBA,EAAQ,GAChB,CACH,CAAC,EAEDG,EAAY,MAAM,EAEX,CACL,QAASC,EAAY,KAAK,UAAUL,EAAQ,KAAM,CAAC,CAAC,EACpD,kBAAmB,CAAE,GAAGA,CAAO,CACjC,CACF,OAASM,EAAO,CACd,MAAAC,EAAO,MAAM,CAAE,MAAAD,CAAM,EAAG,kCAA6B,EAC/C,IAAIE,EAAeF,EAAO,CAC9B,UAAW,mBACX,YAAa,8DACf,CAAC,CACH,CACF,EAaaX,GAAY,MAAOc,GAA2C,CACzE,GAAM,CAAE,gBAAAnB,EAAiB,SAAAE,CAAS,EAAIiB,EAEtC,GAAInB,EAAgB,SAAW,EAC7B,MAAO,CAAC,EAGV,IAAMoB,EAAa,MAAMC,GAAwB,EAE3CC,EAAmB,CAAC,EAE1B,QAAWC,KAAUvB,EAAiB,CACpC,IAAMwB,EAAQC,GAAwB,CAAE,SAAAvB,EAAU,OAAAqB,CAAO,CAAC,EAIrDH,EAAW,IAAIM,GAAsBF,CAAK,CAAC,IAIhD,MAAMG,GAA0BH,CAAK,EACrCF,EAAO,KAAKE,CAAK,EACnB,CAEA,OAAOF,CACT,EAkBab,GAAa,MAAOU,GAAqD,CACpF,GAAM,CAAE,YAAArB,EAAa,gBAAAE,EAAiB,SAAAE,CAAS,EAAIiB,EAE7CS,EAAmB,CAAC,EAE1B,QAAWL,KAAUvB,EAAiB,CACpC,IAAMwB,EAAQC,GAAwB,CAAE,SAAAvB,EAAU,OAAAqB,CAAO,CAAC,EAE1D,GAAI,CACF,MAAMM,GAA4B,CAAE,IAAK,GAAG/B,CAAW,IAAIyB,CAAM,GAAI,MAAAC,CAAM,CAAC,EAC5EI,EAAO,KAAKJ,CAAK,CACnB,OAASR,EAAO,CACdC,EAAO,KAAK,CAAE,MAAAD,EAAO,MAAAQ,CAAM,EAAG,oDAA0CD,CAAM,EAAE,CAClF,CACF,CAEA,MAAO,CAAE,OAAAK,CAAO,CAClB,EAMMf,GAAa,CAACH,EAA+BoB,IAAqC,CAKtF,GAJIpB,EAAO,WAAW,OAAS,GAC7BO,EAAO,KAAK,oBAAaP,EAAO,WAAW,MAAM,oBAAoB,EAGnEoB,EAAQ,OAAQ,CAClB,IAAMC,EAAYrB,EAAO,aACtB,IAAKsB,GACGC,GAAiBD,CAAQ,CACjC,EACA,KAAK,IAAI,EAERtB,EAAO,gBAAkB,GAC3BO,EAAO,KAAK,gBAAWP,EAAO,eAAe,iBAAiBqB,CAAS,eAAe,EAGpFrB,EAAO,kBAAoB,GAC7BO,EAAO,KAAK,qBAAcP,EAAO,iBAAiB,4BAA4BqB,CAAS,eAAe,CAE1G,CAEA,GAAIrB,EAAO,WAAW,OAAS,EAAG,CAChCO,EAAO,KAAK,kCAA6B,EACzC,QAAWO,KAASd,EAAO,WACzBO,EAAO,KAAKO,CAAK,CAErB,CAGE,CAACM,EAAQ,QACTpB,EAAO,WAAW,SAAW,GAC7BA,EAAO,WAAW,SAAW,GAC7BA,EAAO,kBAAoB,GAC3BA,EAAO,oBAAsB,GAE7BO,EAAO,KAAK,gCAAsB,CAEtC,EAGaiB,GAAyBC,EAAc,CAClD,KAAM,mBACN,YACE,+YACF,YAAa,CAAC,EACd,aAAc,CACZ,WAAYC,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,mEAAmE,EAC5G,WAAYA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,oDAAoD,EAC7F,aAAcA,GACX,MAAMA,GAAE,OAAO,CAAC,EAChB,SAAS,sFAAsF,EAClG,gBAAiBA,GAAE,OAAO,EAAE,SAAS,sEAAsE,EAC3G,kBAAmBA,GAChB,OAAO,EACP,SAAS,iFAAiF,CAC/F,EACA,QAASzC,EACX,CAAC,ECnOD,OAAS,KAAA0C,OAAS,MAkCX,IAAMC,GAAkB,MAAOC,GAAoC,CACxE,GAAM,CAAE,iBAAAC,EAAkB,IAAAC,EAAK,SAAAC,CAAS,EAAIH,EAE5C,MAAMI,EAAwB,CAAE,UAAW,mBAAoB,eAAgB,KAAM,CAAC,EAEtF,GAAI,CACF,IAAMC,EAAmB,MAAMC,EAAoB,SAAS,EAE5D,GAAID,EAAiB,SAAW,EAC9B,OAAAE,EAAO,KAAK,4CAAkC,EAE9CC,EAAY,MAAM,EAEX,CACL,QAASC,EAAY,KAAK,UAAU,CAAE,iBAAkB,CAAC,EAAG,MAAO,CAAE,EAAG,KAAM,CAAC,CAAC,EAChF,kBAAmB,CAAE,iBAAkB,CAAC,EAAG,MAAO,CAAE,CACtD,EAGF,IAAMC,EAAc,MAAMC,EAAe,EAEnCC,EAAc,GAAGF,CAAW,GAAGG,CAAoB,GAErDC,EAAoC,CAAC,EAEzC,GAAIZ,EACFY,EAA0BT,UACjBF,EACTW,EAA0BX,EAAS,MAAM,GAAG,EAAE,IAAKY,GAC1CC,GAAiBC,GAAgBF,EAAE,KAAK,CAAC,CAAC,CAClD,MACI,CACLP,EAAY,eAAe,EAE3B,GAAM,CAACU,EAAcC,CAAM,EAAI,MAAM,QAAQ,IAAI,CAACC,EAAoB,EAAGC,EAAsB,CAAC,CAAC,EAE3FC,EAAe,IAAI,IACvBH,EAAO,IAAKI,GACH,CAACA,EAAG,OAAQC,EAAkBD,EAAG,KAAK,CAAC,CAC/C,CACH,EAEAT,EAA0B,MAAMW,GAC9BC,EAAwB,CAAE,SAAUrB,EAAkB,aAAAa,EAAc,MAAOI,CAAa,CAAC,EACzF,CAAE,SAAU,EAAK,CACnB,CACF,CAGA,IAAMK,EAAcb,EAAwB,SAAWT,EAAiB,OAEpEsB,EACFnB,EAAY,UAAU,QAAS,EAAI,EAEnCA,EAAY,UAAU,aAAcoB,GAAoBd,CAAuB,CAAC,EAIlF,MAAMe,EAAc5B,EAAkB,+DAA+D,EAGhGA,GACHO,EAAY,UAAU,QAAS,EAAI,EAGrC,IAAMsB,EAAW,MAAMC,EAAY,EAE7BC,EAAmB,MAAMC,GAAgB,CAC7C,SAAUnB,EACV,YAAAF,EACA,SAAAkB,EACA,YAAaH,CACf,CAAC,EAKD,MAAMO,GAAyB,CAC7B,YAAAxB,EACA,YAAAE,EACA,iBAAAP,EACA,iBAAA2B,EACA,oBAAqB,CAAC/B,CACxB,CAAC,EAEDkC,GAAWH,CAAgB,EAE3BxB,EAAY,MAAM,EAElB,IAAM4B,EAAoB,CACxB,iBAAAJ,EACA,MAAOA,EAAiB,MAC1B,EAEA,MAAO,CACL,QAASvB,EAAY,KAAK,UAAU2B,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,OAASC,EAAO,CAId,MAAIC,GAAqBD,CAAK,EAASA,GAEvC9B,EAAO,MAAM,CAAE,MAAA8B,CAAM,EAAG,iCAA4B,EAC9C,IAAIE,EAAeF,EAAO,CAC9B,UAAW,mBACX,YAAa,2EACf,CAAC,EACH,CACF,EAKMF,GAAcK,GAA4B,CAC9C,GAAIA,EAAQ,OAAS,EAAG,CACtBjC,EAAO,KAAK,2BAAsB,EAClC,QAAWkC,KAAUD,EACnBjC,EAAO,KAAKkC,CAAM,EAEpBlC,EAAO,KAAK,EAAE,CAChB,MACEA,EAAO,KAAK,4CAAkC,CAElD,EAGamC,GAAyBC,EAAc,CAClD,KAAM,mBACN,YACE,wbACF,YAAa,CACX,IAAKC,GACF,QAAQ,EACR,SAAS,EACT,SACC,qLACF,EACF,SAAUA,GACP,OAAO,EACP,SAAS,EACT,SACC,kMACF,CACJ,EACA,aAAc,CACZ,iBAAkBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,uCAAuC,EACtF,MAAOA,GAAE,OAAO,EAAE,SAAS,iCAAiC,CAC9D,EACA,QAAS7C,EACX,CAAC,ECzLD,OAAS,KAAA8C,OAAS,MAClB,OAAS,KAAAC,OAAS,KAuBX,IAAMC,GAAgB,MAAOC,GAA8B,CAChE,GAAM,CAAE,iBAAAC,CAAiB,EAAID,EAE7B,MAAME,EAAwB,CAAE,UAAW,iBAAkB,eAAgB,KAAM,CAAC,EAEpF,GAAI,CACF,IAAMC,EAAmB,MAAMC,EAAoB,SAAS,EACtDC,EAAc,MAAMC,EAAe,EAEnCC,EAAc,GAAGF,CAAW,GAAGG,CAAoB,GAEnDC,EAAiB,MAAMC,GAAc,EAG3C,MAAMC,EAAcV,EAAkB,+DAA+D,EAGhGA,GACHW,EAAY,UAAU,QAAS,EAAI,EAGrC,GAAM,CAAE,iBAAAC,CAAiB,EAAIC,GAAoB,CAC/C,eAAAL,EACA,iBAAAN,CACF,CAAC,EAEKY,EAAW,MAAMC,EAAY,EAE7BC,EAAmB,MAAMC,GAAgB,CAC7C,SAAUL,EACV,YAAAN,EACA,SAAAQ,CACF,CAAC,EAID,MAAMI,GAAyB,CAC7B,YAAAd,EACA,YAAAE,EACA,iBAAAJ,EACA,iBAAAc,EACA,oBAAqB,EACvB,CAAC,EAEDG,GAAWH,CAAgB,EAE3BL,EAAY,MAAM,EAElB,IAAMS,EAAoB,CACxB,iBAAAJ,EACA,MAAOA,EAAiB,MAC1B,EAEA,MAAO,CACL,QAASK,EAAY,KAAK,UAAUD,EAAmB,KAAM,CAAC,CAAC,EAC/D,kBAAAA,CACF,CACF,OAASE,EAAO,CAId,MAAIC,GAAqBD,CAAK,EAASA,GAEvCE,EAAO,MAAM,CAAE,MAAAF,CAAM,EAAG,iCAA4B,EAC9C,IAAIG,EAAeH,EAAO,CAC9B,UAAW,6BACX,YAAa,wDACf,CAAC,EACH,CACF,EAUMT,GAAuBa,GAAkE,CAC7F,GAAM,CAAE,eAAAlB,EAAgB,iBAAAN,CAAiB,EAAIwB,EAU7C,MAAO,CAAE,iBARkBxB,EAAiB,OAAQyB,GAC3CC,GAAgBD,CAAM,CAC9B,EAE2C,OAAQA,GAC3C,CAACnB,EAAe,SAASmB,CAAM,CACvC,CAEyB,CAC5B,EAWMV,GAAkB,MAAOS,GAAiD,CAC9E,GAAM,CAAE,SAAAG,EAAU,YAAAvB,EAAa,SAAAQ,CAAS,EAAIY,EAEtCI,EAAoB,CAAC,EAE3B,QAAWH,KAAUE,EACnB,GAAI,CACF,IAAME,EAAe,GAAGzB,CAAW,IAAIqB,CAAM,GAEvCK,EAAQC,GAAwB,CAAE,SAAAnB,EAAU,OAAAa,CAAO,CAAC,EAE1D,MAAMO,GAA0BF,CAAK,EAErC,MAAMG,yBAAwBJ,CAAY,GAC1CD,EAAQ,KAAKH,CAAM,CACrB,OAASL,EAAO,CACd,IAAMc,EAAM,IAAIX,EAAeH,EAAO,CACpC,UAAW,6BAA6BK,CAAM,GAC9C,YAAa,gFACf,CAAC,EAEDH,EAAO,MAAM,CAAE,MAAAF,EAAO,OAAAK,EAAQ,IAAKS,EAAI,OAAQ,CAAC,CAClD,CAGF,OAAON,CACT,EAKMX,GAAcW,GAA4B,CAC9C,GAAIA,EAAQ,OAAS,EAAG,CACtBN,EAAO,KAAK,2BAAsB,EAClC,QAAWG,KAAUG,EACnBN,EAAO,KAAKG,CAAM,EAEpBH,EAAO,KAAK,EAAE,CAChB,MACEA,EAAO,KAAK,4CAAkC,CAElD,EAGaa,GAAuBC,EAAc,CAChD,KAAM,iBACN,YACE,+PACF,YAAa,CAAC,EACd,aAAc,CACZ,iBAAkBC,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,mCAAmC,EAClF,MAAOA,GAAE,OAAO,EAAE,SAAS,yCAAyC,CACtE,EACA,QAASzC,EACX,CAAC,ECpHM,IAAM0C,GAAc,CACzB,CAAE,IAAK,UAAW,MAAO,SAAU,EACnC,CAAE,IAAK,UAAW,MAAO,oBAAqB,EAC9C,CAAE,IAAK,YAAa,MAAO,WAAY,EACvC,CAAE,IAAK,cAAe,MAAO,aAAc,EAC3C,CAAE,IAAK,gBAAiB,MAAO,eAAgB,EAC/C,CAAE,IAAK,SAAU,MAAO,QAAS,EACjC,CAAE,IAAK,QAAS,MAAO,qBAAsB,CAC/C,EAmEaC,GAAwC,CAcnD,CACE,QAAS,MACT,UAAW,UACX,QAAS,KACT,WAAY,GACZ,UAAW,CAAC,KAAK,EACjB,YAAa,EACf,EAGA,CACE,QAAS,YACT,UAAW,UACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,UAAW,WAAW,CACpC,EACA,CACE,QAAS,eACT,UAAW,UACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,UAAW,MAAM,CAC/B,EACA,CACE,QAAS,iBACT,UAAW,UACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,UAAW,QAAQ,CACjC,EACA,CACE,QAAS,oBACT,UAAW,UACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,UAAW,WAAW,CACpC,EACA,CACE,QAAS,qBACT,UAAW,UACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,UAAW,YAAY,CACrC,EACA,CACE,QAAS,0BACT,UAAW,UACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,UAAW,iBAAiB,CAC1C,EAGA,CACE,QAAS,kBACT,UAAW,UACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,UAAW,SAAS,CAClC,EAGA,CACE,QAAS,gBACT,UAAW,YACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,YAAa,KAAK,CAChC,EACA,CACE,QAAS,iBACT,UAAW,YACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,YAAa,MAAM,CACjC,EACA,CACE,QAAS,mBACT,UAAW,YACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,YAAa,QAAQ,CACnC,EAIA,CACE,QAAS,mBACT,UAAW,YACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,YAAa,QAAQ,CACnC,EACA,CACE,QAAS,iBACT,UAAW,YACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,YAAa,MAAM,CACjC,EASA,CACE,QAAS,aACT,UAAW,cACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,YAAY,CAC1B,EACA,CAAE,QAAS,WAAY,UAAW,cAAe,QAASC,GAAgB,WAAY,GAAM,UAAW,CAAC,UAAU,CAAE,EACpH,CACE,QAAS,WACT,UAAW,cACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,UAAU,EACtB,iBAAkB,EACpB,EACA,CACE,QAAS,YACT,UAAW,cACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,WAAW,EACvB,iBAAkB,EACpB,EAGA,CACE,QAAS,cACT,UAAW,gBACX,QAAS,KACT,WAAY,GACZ,UAAW,CAAC,SAAU,MAAM,CAC9B,EACA,CACE,QAAS,cACT,UAAW,gBACX,QAAS,KACT,WAAY,GACZ,UAAW,CAAC,SAAU,MAAM,EAC5B,gBAAiB,EACnB,EAGA,CACE,QAAS,eACT,UAAW,SACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,SAAU,OAAO,CAC/B,EACA,CACE,QAAS,cACT,UAAW,SACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,SAAU,MAAM,CAC9B,EACA,CACE,QAAS,gBACT,UAAW,SACX,QAAS,KACT,WAAY,GACZ,UAAW,CAAC,SAAU,QAAQ,CAChC,EAKA,CAAE,QAAS,OAAQ,UAAW,QAAS,QAAS,KAAM,WAAY,GAAO,UAAW,CAAC,MAAM,CAAE,EAC7F,CAAE,QAAS,SAAU,UAAW,QAAS,QAASC,GAAe,WAAY,GAAO,UAAW,CAAC,QAAQ,CAAE,EAC1G,CAAE,QAAS,QAAS,UAAW,QAAS,QAASC,GAAc,WAAY,GAAM,UAAW,CAAC,OAAO,CAAE,EACtG,CAAE,QAAS,UAAW,UAAW,QAAS,QAASC,GAAgB,WAAY,GAAM,UAAW,CAAC,SAAS,CAAE,EAI5G,CAAE,QAAS,SAAU,UAAW,KAAM,QAAS,KAAM,WAAY,GAAO,UAAW,CAAC,QAAQ,CAAE,EAC9F,CAAE,QAAS,SAAU,UAAW,KAAM,QAAS,KAAM,WAAY,GAAO,UAAW,CAAC,QAAQ,CAAE,EAG9F,CAAE,QAAS,eAAgB,UAAW,KAAM,QAAS,KAAM,WAAY,GAAO,UAAW,CAAC,cAAc,CAAE,EAI1G,CAAE,QAAS,cAAe,UAAW,KAAM,QAAS,KAAM,WAAY,GAAO,UAAW,CAAC,aAAa,CAAE,EAGxG,CAAE,QAAS,MAAO,UAAW,KAAM,QAAS,KAAM,WAAY,GAAO,UAAW,CAAC,KAAK,CAAE,EAGxF,CACE,QAAS,kBACT,UAAW,KACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,SAAU,UAAU,CAClC,EACA,CACE,QAAS,cACT,UAAW,KACX,QAASC,GACT,WAAY,GACZ,UAAW,CAAC,SAAU,MAAM,CAC9B,CACF,EAGaC,GAAqB,IACzBxB,GAAe,QAASyB,GACtBA,EAAM,YAAcA,EAAM,QAAU,CAACA,EAAM,OAAO,EAAI,CAAC,CAC/D,EAaUC,GAAuBC,GAC3B3B,GAAe,OAAQyB,GACrBA,EAAM,YAAcE,CAC5B,EAYUC,GAAwBC,GAC5B7B,GAAe,KAAMyB,GACnBA,EAAM,cAAgB,IAAQA,EAAM,UAAU,KAAK,GAAG,IAAMI,CACpE",
|
|
6
|
-
"names": ["path", "process", "z", "fs", "pathExists", "target", "path", "fs", "path", "pathToFileURL", "z", "PACKAGE_CONFIG_FILE", "readPackageJson", "packageDir", "raw", "fs", "path", "loadPackageConfig", "baseline", "DEFAULT_RULES", "configPath", "pathExists", "stat", "rawExport", "pathToFileURL", "resolvedExport", "parsed", "packageConfigSchema", "z", "resolvePackageConfig", "fs", "path", "yaml", "WORKSPACE_FILE", "listChildDirs", "dir", "fs", "entry", "path", "expandSegment", "dirs", "segment", "next", "candidate", "pathExists", "expandGlob", "projectRoot", "pattern", "discoverPackages", "raw", "patterns", "yaml", "found", "checkConfig", "packageDir", "baseline", "DEFAULT_RULES", "rules", "loadPackageConfig", "PACKAGE_CONFIG_FILE", "err", "fs", "path", "checkFiles", "packageDir", "requiredFiles", "file", "stat", "checkScripts", "scripts", "requiredScripts", "script", "value", "fs", "path", "TURBO_FILE", "checkTurbo", "packageDir", "requiredTasks", "parsed", "raw", "err", "tasks", "task", "defined", "validatePackage", "packageDir", "baseline", "DEFAULT_RULES", "pkgJson", "readPackageJson", "packageName", "path", "configCheck", "rules", "checkConfig", "checks", "checkScripts", "checkFiles", "checkTurbo", "passed", "check", "textContent", "text", "defineMcpTool", "tool", "path", "collectReferencedApps", "presets", "discovered", "apps", "preset", "app", "resolvePreset", "buildRoutePkgLookup", "root", "routesByApp", "loadErrors", "routes", "loadDev", "path", "route", "route_", "error", "toFailChecks", "issues", "issue", "checkDevPresets", "getInfraKitConfig", "keyIssues", "validatePresetKeys", "checks", "apiApps", "discoverApiApps", "uiApps", "discoverUiApps", "a", "routePkg", "ctx", "failChecks", "validatePresetProxy", "findPackageRoot", "start", "current", "path", "pathExists", "resolveTargets", "options", "getProjectRoot", "ROOT_DEFAULT_RULES", "discoverPackages", "dir", "process", "logResult", "result", "header", "logger", "check", "icon", "audit", "targets", "results", "target", "validatePackage", "presetResult", "checkDevPresets", "allPassed", "structuredContent", "textContent", "auditInputSchema", "z", "auditOutputSchema", "auditMcpTool", "defineMcpTool", "params", "fs", "os", "path", "z", "$", "fs", "path", "hasManagedBlock", "content", "startMarker", "endMarker", "startIdx", "endIdx", "removeManagedBlock", "before", "after", "buildManagedBlock", "startMarker", "body", "endMarker", "upsertManagedBlock", "content", "placement", "block", "present", "hasManagedBlock", "startIdx", "endIdx", "base", "removeManagedBlock", "AGENTS_MARKER_START", "AGENTS_MARKER_END", "AGENTS_VERSION_PREFIX", "AGENTS_IMPORT_START", "AGENTS_IMPORT_END", "AGENTS_FILE", "CLAUDE_FILE", "buildVersionLine", "version", "buildAgentsBody", "assertNotSymlink", "filePath", "fs", "backupFile", "stamp", "writeManaged", "nextContent", "existed", "path", "readOr", "fallback", "assertBlockPresent", "start", "end", "content", "hasManagedBlock", "migrateLegacyAgentsFile", "agentsPath", "stripped", "removeManagedBlock", "writeAgentFiles", "mainConfigPath", "getInfraKitConfigPaths", "logger", "root", "package_default", "body", "claudePath", "claudeContent", "upsertManagedBlock", "written", "file", "fs", "os", "path", "process", "fs", "path", "process", "CONFIG_KEY_DOCS", "buildUserGlobalExample", "buildUserProjectExample", "projectName", "buildVendorExample", "fs", "os", "fileExists", "filePath", "tildify", "home", "seeded", "exampleSiblingPath", "jsonPath", "readFileOrNull", "filePath", "fs", "seedUserProjectConfig", "paths", "configPath", "examplePath", "path", "createdConfig", "fileExists", "example", "buildUserProjectExample", "refreshedExample", "resetMergedConfigCache", "seedCreatedMessage", "result", "tildify", "ensureUserProjectConfig", "process", "getInfraKitConfigPaths", "logger", "err", "fs", "path", "pathToFileURL", "yaml", "z", "fs", "os", "path", "z", "z", "FACTORY_CONFIG_FILE", "factoryConfigSchema", "getFactoryConfigPath", "path", "os", "USER_CONFIG_DIR_NAME", "FACTORY_CONFIG_FILE", "expandTilde", "p", "loadFactoryConfig", "configPath", "raw", "fs", "data", "err", "parsed", "factoryConfigSchema", "z", "legacyYmlPath", "jsonPath", "migrateLegacyConfig", "paths", "getInfraKitConfigPaths", "layers", "infraKitConfigSchema", "path", "infraKitOverrideConfigSchema", "migrated", "layer", "ymlExists", "jsonExists", "fileExists", "logger", "tildify", "raw", "fs", "parsed", "yaml", "result", "z", "err", "resetInfraKitConfigCache", "migrateUserGlobalConfigFilename", "dir", "pairs", "renamed", "from", "to", "fromExists", "toExists", "migrateFactoryConfigToJson", "newJson", "getFactoryConfigPath", "oldTs", "oldExists", "newExists", "stat", "pathToFileURL", "wasFunction", "resolved", "factoryConfigSchema", "stripLegacyIdeMode", "ide", "changed", "stripEntry", "entry", "config", "restConfig", "key", "nextIde", "normalizeLegacyIdeStructures", "jsonPaths", "normalized", "MARKER_START", "MARKER_END", "LEGACY_PAIRED", "LEGACY_SINGLE", "init", "zshrcPath", "path", "os", "existing", "fs", "removeExistingBlock", "updated", "upsertManagedBlock", "buildShellBody", "logger", "migrateLegacyConfig", "normalizeLegacyIdeStructures", "migrateUserGlobalConfigFilename", "migrateFactoryConfigToJson", "seedUserGlobalConfig", "writeAgentFiles", "reseedUserProjectConfig", "shell", "process", "userConfigDir", "userConfigPath", "buildUserGlobalExample", "buildVendorExample", "paths", "getInfraKitConfigPaths", "result", "seedUserProjectConfig", "seedCreatedMessage", "err", "isBlockLine", "line", "content", "removeManagedBlock", "start", "end", "legacyResult", "legacyIdx", "before", "afterLines", "i", "remaining", "runCmd", "buildShellBlock", "$", "DOPPLER_AUTH_TIMEOUT_MS", "validateDopplerCliAndAuth", "error", "PROJECT_NOT_FOUND_MARKER", "CONFIG_NOT_FOUND_MARKER", "classifyDopplerDownloadError", "stderr", "buildDopplerNotFoundMessage", "kind", "project", "config", "available", "lines", "label", "$", "DOPPLER_LIST_TIMEOUT_MS", "getDopplerProject", "envManagement", "getInfraKitConfig", "listDopplerProjects", "runDopplerNameList", "$", "listDopplerConfigs", "project", "run", "prevQuiet", "result", "parseDopplerNames", "stdout", "parsed", "entry", "name", "fs", "readOverrideSummary", "filePath", "exists", "raw", "parsed", "keys", "describeOverrides", "summary", "fs", "path", "shouldWriteWarm", "autoLoaded", "projectDir", "canonicalProjectRoot", "canonicalizeProjectRoot", "projectRoot", "fs", "writeWarmCache", "canonicalProjectDir", "contents", "dir", "getProjectWarmCacheDir", "atomicWriteFileSync", "path", "ENV_LOAD_FILE", "error", "logger", "evictStaleWarmCaches", "nowMs", "ttlSeconds", "DEFAULT_WARM_TTL_SECONDS", "root", "getWarmCacheRoot", "entries", "entry", "mtimeMs", "invalidateProjectWarmCache", "purge", "ENV_CLEAR_FILE", "checkCommand", "name", "command", "successMsg", "failMsg", "$", "checkZshrcInitialized", "zshrcPath", "path", "os", "fs", "content", "startIdx", "MARKER_START", "endIdx", "MARKER_END", "installedBlock", "expectedBlock", "buildShellBlock", "checkWarmCache", "root", "getProjectRoot", "canon", "canonicalizeProjectRoot", "warmFile", "getProjectWarmCacheDir", "ENV_LOAD_FILE", "ageSeconds", "ageMinutes", "ttlMinutes", "DEFAULT_WARM_TTL_SECONDS", "checkPnpmWorkspaceVirtualStore", "yamlPath", "err", "checkInfraKitConfigValid", "resetInfraKitConfigCache", "getInfraKitConfig", "checkDopplerProjectExists", "project", "getDopplerProject", "projects", "listDopplerProjects", "checkUserOverridePath", "paths", "getInfraKitConfigPaths", "exists", "summary", "readOverrideSummary", "suffix", "describeOverrides", "tildify", "checkLegacyUserGlobalConfig", "legacyPath", "IDE_PROBE_META", "probeIde", "provider", "meta", "checkIdeInstalled", "providers", "config", "resolveConfiguredIdes", "ide", "probes", "missing", "probe", "RTK_REQUIRED_INDEXES", "checkRtkConfigured", "statusLines", "l", "failed", "idx", "line", "checkAgentFiles", "mainConfigPath", "claudePath", "present", "hasManagedBlock", "AGENTS_MARKER_START", "AGENTS_MARKER_END", "installDaemonCmd", "bin", "formatPortlessCommand", "trustCmd", "CA_MISMATCH_CODES", "PROBE_BUG_CODE", "checkPortlessServing", "isProxyServing", "DEFAULT_DEV_PROXY_PORT", "checkPortlessCaChain", "handshake", "routes", "caPath", "servernames", "route", "servername", "entry", "codes", "code", "checkPortlessCaTrusted", "trusted", "checkPortlessStaleRoutes", "isListening", "stale", "checkPortless", "deps", "resolveBin", "resolvePortlessBin", "defaultIsProxyServing", "handshakeChainsToCa", "caTrusted", "caFingerprintMatches", "readRoutes", "listRoutes", "defaultIsListening", "readCaPath", "installed", "serving", "chain", "doctor", "checks", "logger", "check", "icon", "structuredContent", "c", "textContent", "doctorMcpTool", "defineMcpTool", "z", "fs", "path", "process", "z", "clearProjectWarmCache", "purge", "projectRoot", "getProjectRoot", "canon", "canonicalizeProjectRoot", "invalidateProjectWarmCache", "buildEnvClearLines", "varNames", "v", "INFRA_KIT_ENV_VAR", "INFRA_KIT_ENV_CONFIG_VAR", "INFRA_KIT_ENV_PROJECT_VAR", "INFRA_KIT_ENV_PROJECT_ROOT_VAR", "INFRA_KIT_ENV_LOADED_AT_VAR", "INFRA_KIT_ENV_AUTOLOADED_VAR", "INFRA_KIT_ENV_CLEARED_VAR", "envClear", "cacheDir", "getSessionCacheDir", "envLoadPath", "path", "ENV_LOAD_FILE", "hasLoadedEnv", "fs", "parseVarNamesFromEnvFile", "unsetLines", "clearFilePath", "ENV_CLEAR_FILE", "atomicWriteFileSync", "process", "structuredContent", "textContent", "envClearMcpTool", "defineMcpTool", "z", "z", "envList", "project", "getDopplerProject", "environments", "getInfraKitConfig", "logger", "env", "structuredContent", "textContent", "envListMcpTool", "defineMcpTool", "z", "select", "Buffer", "fs", "path", "process", "z", "$", "extractStderr", "cause", "stderr", "buildMessage", "ctx", "parts", "OperationError", "buildAutoLoadMarkerLines", "autoLoaded", "INFRA_KIT_ENV_AUTOLOADED_VAR", "shellSingleQuote", "INFRA_KIT_ENV_CLEARED_VAR", "buildEnvLoadFileLines", "pairs", "config", "project", "projectRoot", "loadedAt", "key", "value", "INFRA_KIT_ENV_VAR", "INFRA_KIT_ENV_CONFIG_VAR", "INFRA_KIT_ENV_PROJECT_VAR", "INFRA_KIT_ENV_PROJECT_ROOT_VAR", "INFRA_KIT_ENV_LOADED_AT_VAR", "resolveProjectRootSafe", "getProjectRoot", "writeEnvLoadFile", "beforeWrite", "projectDir", "validateDopplerCliAndAuth", "getDopplerProject", "downloadDopplerSecrets", "fileContents", "cacheDir", "getSessionCacheDir", "envFilePath", "path", "ENV_LOAD_FILE", "fs", "atomicWriteFileSync", "canonicalProjectRoot", "canonicalizeProjectRoot", "shouldWriteWarm", "writeWarmCache", "evictStaleWarmCaches", "envLoad", "args", "selectedConfig", "environments", "getInfraKitConfig", "commandEcho", "select", "env", "process", "result", "structuredContent", "textContent", "DOPPLER_MAX_OUTPUT_BYTES", "DOPPLER_DOWNLOAD_TIMEOUT_MS", "prevQuiet", "$", "error", "translateDopplerDownloadError", "assertDopplerOutputSize", "parseDopplerSecretsJson", "stderr", "extractStderr", "kind", "classifyDopplerDownloadError", "available", "listDopplerProjects", "listDopplerConfigs", "buildDopplerNotFoundMessage", "stdout", "bytes", "Buffer", "ENV_VAR_NAME_PATTERN", "parsed", "envLoadMcpTool", "defineMcpTool", "z", "path", "process", "z", "envStatus", "logger", "cacheDir", "getSessionCacheDir", "sessionId", "process", "INFRA_KIT_SESSION_VAR", "envLoadPath", "path", "ENV_LOAD_FILE", "sessionLoadedCount", "sessionTotalCount", "sessionConfig", "INFRA_KIT_ENV_CONFIG_VAR", "sessionProject", "INFRA_KIT_ENV_PROJECT_VAR", "sessionLoadedAt", "INFRA_KIT_ENV_LOADED_AT_VAR", "autoLoaded", "INFRA_KIT_ENV_AUTOLOADED_VAR", "cleared", "INFRA_KIT_ENV_CLEARED_VAR", "varNames", "parseVarNamesFromEnvFile", "v", "loadedAtDisplay", "origin", "missing", "clearedNote", "structuredContent", "textContent", "envStatusMcpTool", "defineMcpTool", "z", "z", "$", "$", "$", "$", "process", "assertJiraOk", "response", "context", "errorText", "logger", "createJiraVersion", "params", "config", "baseUrl", "token", "email", "projectId", "requestBody", "url", "credentials", "error", "getProjectVersions", "findVersionByName", "versionName", "v", "updateJiraVersion", "deliverJiraRelease", "version", "loadJiraConfig", "process", "projectIdStr", "missingVars", "errorMessage", "loadJiraConfigOptional", "DEV_REF", "getBaseBranch", "type", "prepareGitForRelease", "baseBranch", "$", "createSingleRelease", "args", "id", "jiraConfig", "description", "versionName", "formatJiraName", "result", "createJiraVersion", "jiraVersionUrl", "releaseInfo", "createReleaseBranch", "displayLabel", "getJiraDescriptions", "descriptions", "loadJiraConfigOptional", "versions", "getProjectVersions", "version", "formatVersionLabel", "maxVersionLength", "padding", "tag", "detectReleaseType", "title", "parseBranchChoices", "branches", "branch", "parseBranchName", "resolveReleaseBranch", "versionArg", "formatBranchName", "parseReleaseRef", "error", "OperationError", "releaseLabelFromBranch", "releaseBranchLabels", "deriveBranchType", "types", "formatBranchPickerItems", "label", "sortReleasePRs", "prs", "pr", "parseBranchName", "entry", "a", "b", "compareReleaseIds", "fetchAllReleasePRs", "releasePRs", "$", "hotfixPRs", "all", "seen", "loadSortedReleasePRs", "OperationError", "error", "logger", "getReleasePRs", "getReleasePRsWithInfo", "updateReleasePRBody", "args", "branch", "body", "createReleaseBranch", "id", "jiraVersionUrl", "type", "description", "prTitle", "formatPrTitle", "baseBranch", "getBaseBranch", "branchName", "formatBranchName", "prLink", "assertManagementContext", "args", "operation", "requiredBranch", "isInsideLinkedWorktree", "OperationError", "currentBranch", "getCurrentBranch", "isWorkingTreeClean", "process", "PromptCancelledError", "process", "jsonOutput", "emit", "result", "write", "text", "addJsonOption", "cmd", "option", "assertInteractive", "process", "jsonOutput", "OperationError", "pickReleaseBranch", "items", "runBranchPicker", "value", "PromptCancelledError", "pickReleaseBranches", "opts", "runBranchMultiPicker", "values", "ghMergeDev", "args", "all", "confirmedCommand", "assertManagementContext", "releasePRsList", "getReleasePRsWithInfo", "pr", "detectReleaseType", "logger", "commandEcho", "textContent", "selectedReleaseBranches", "descriptions", "getJiraDescriptions", "pickReleaseBranches", "formatBranchPickerItems", "releaseBranchLabels", "confirmOrExit", "$", "failedBranches", "branch", "mergeDev", "structuredContent", "error", "err", "OperationError", "ghMergeDevMcpTool", "defineMcpTool", "z", "z", "$", "readTrimmedString", "value", "max", "formatZxError", "error", "rec", "fields", "exitCode", "stderr", "stdout", "$", "removeWorktrees", "args", "branches", "worktreeDir", "repoName", "pruneFolder", "results", "branch", "worktreePath", "title", "buildCmuxWorkspaceTitle", "closeCmuxWorkspaceByTitle", "$", "removed", "index", "result", "err", "OperationError", "logger", "runStep", "operation", "remediation", "fn", "error", "logger", "formatZxError", "OperationError", "fetchPRByHead", "head", "result", "$", "fetchMergedRcPRForVersion", "id", "expectedTitle", "formatRcTitle", "pr", "fetchOpenDevToMainPR", "resolveTargetFromVersion", "version", "selectedReleaseBranch", "resolveReleaseBranch", "resolveTargetInteractively", "releasePRsInfo", "getReleasePRsWithInfo", "branches", "releaseTypes", "detectReleaseType", "commandEcho", "descriptions", "getJiraDescriptions", "pickReleaseBranch", "formatBranchPickerItems", "prInfo", "removeReleaseWorktreeIfPresent", "releaseBranch", "getCurrentWorktrees", "projectRoot", "repoName", "getProjectRoot", "getRepoName", "worktreeDir", "WORKTREES_DIR_SUFFIX", "removeWorktrees", "mergeReleasePR", "args", "releaseType", "mergeTarget", "releasePr", "resolveRcPRNumber", "selectedLabel", "displayLabel", "existingOpen", "rcNumber", "created", "ensureRcPRMerged", "dispatchDeployWorkflow", "syncMainIntoDev", "removeDeliveredReleaseBranch", "branch", "deleteLocalBranch", "deleteRemoteBranch", "deliverJiraReleaseSafely", "jiraConfig", "loadJiraConfigOptional", "versionName", "formatJiraName", "deliverJiraRelease", "ghReleaseDeliver", "confirmedCommand", "assertManagementContext", "releasePrTitle", "releaseId", "parseBranchName", "selectedVersion", "confirmOrExit", "structuredContent", "textContent", "ghReleaseDeliverMcpTool", "defineMcpTool", "z", "confirm", "select", "z", "$", "confirmDeploy", "args", "confirmedCommand", "branch", "env", "commandEcho", "answer", "confirm", "ghReleaseDeployAll", "version", "skipTerraform", "selectedReleaseBranch", "resolveReleaseBranch", "releasePRsInfo", "getReleasePRsWithInfo", "branches", "pr", "releaseTypes", "detectReleaseType", "descriptions", "getJiraDescriptions", "pickReleaseBranch", "formatBranchPickerItems", "selectedVersion", "releaseLabelFromBranch", "environments", "getInfraKitConfig", "selectedEnv", "select", "OperationError", "shouldSkipTerraform", "logger", "structuredContent", "textContent", "$", "error", "ghReleaseDeployAllMcpTool", "defineMcpTool", "z", "checkbox", "confirm", "select", "fs", "resolve", "yaml", "z", "$", "confirmDeploy", "args", "confirmedCommand", "branch", "env", "commandEcho", "answer", "confirm", "ghReleaseDeploySelected", "version", "services", "skipTerraform", "selectedReleaseBranch", "resolveReleaseBranch", "releasePRsInfo", "getReleasePRsWithInfo", "branches", "pr", "releaseTypes", "detectReleaseType", "descriptions", "getJiraDescriptions", "pickReleaseBranch", "formatBranchPickerItems", "selectedVersion", "releaseLabelFromBranch", "environments", "getInfraKitConfig", "selectedEnv", "select", "OperationError", "availableServices", "parseServicesFromWorkflow", "selectedServices", "checkbox", "svc", "invalidServices", "shouldSkipTerraform", "buildResult", "success", "structuredContent", "textContent", "logger", "$", "serviceFlags", "error", "projectRoot", "getProjectRoot", "workflowPath", "resolve", "content", "fs", "inputs", "yaml", "key", "value", "ghReleaseDeploySelectedMcpTool", "defineMcpTool", "z", "z", "ghReleaseList", "releases", "getReleasePRsWithInfo", "pr", "id", "parseBranchName", "displayLabel", "formatJiraName", "detectReleaseType", "jiraDescriptions", "getJiraDescriptions", "maxVersionLength", "r", "formattedLines", "release", "label", "formatVersionLabel", "description", "logger", "structuredContent", "textContent", "ghReleaseListMcpTool", "defineMcpTool", "z", "confirm", "select", "process", "z", "question", "$", "parseVersion", "versionStr", "sortVersions", "versions", "a", "b", "majA", "minA", "patchA", "majB", "minB", "patchB", "NEXT_TOKEN", "VERSION_RE", "stripBranchPrefix", "raw", "tryParse", "cleaned", "match", "semverKey", "v", "collectKnownVersions", "sources", "all", "parsed", "seen", "key", "sortVersions", "s", "parseVersion", "NoPriorVersionsError", "computeNextVersion", "known", "type", "max", "major", "minor", "highestPatchOnMinor", "acc", "isNextToken", "token", "isReleaseType", "value", "isNamedReleaseInput", "input", "parseReleaseSpec", "spec", "firstColon", "description", "rest", "secondColon", "typeRaw", "typeLower", "entry", "formatReleaseSpec", "withDescription", "base", "resolveNamedInput", "name", "validateName", "resolveReleaseEntries", "entries", "running", "trimmed", "next", "parseReleaseRef", "explicit", "hasNextToken", "e", "extractVersionBranches", "lsRemoteStdout", "line", "tab", "parseBranchName", "id", "parseRemoteRefs", "previousQuiet", "$", "result", "fetchJiraVersionNames", "config", "loadJiraConfigOptional", "getProjectVersions", "v", "loadExistingVersions", "branchesResult", "jiraResult", "logger", "collectKnownVersions", "VERSION_PROMPT_HINT", "trySuggestNext", "known", "type", "computeNextVersion", "err", "NoPriorVersionsError", "resolveOrExit", "entries", "resolveReleaseEntries", "OperationError", "InvalidReleaseNameError", "promptForVersionInput", "running", "suggestion", "defaultHint", "versionAnswer", "question", "versionInput", "logger", "process", "promptForNameInput", "name", "validateName", "reason", "promptForReleasesInteractive", "ensureKnown", "commandEcho", "baseKnown", "ensureRunning", "addAnother", "ordinal", "kind", "select", "resolved", "parseVersion", "description", "confirm", "formatReleaseSummary", "entry", "parts", "echoReleases", "formatReleaseSpec", "collectEntries", "inputReleases", "hasNextToken", "interactive", "assertHomogeneousReleaseType", "confirmReleases", "confirmedCommand", "summary", "confirmOrExit", "executeOne", "args", "jiraConfig", "label", "displayLabel", "prTitleLabel", "prepareGitForRelease", "result", "createSingleRelease", "error", "logFinalSummary", "total", "successCount", "failureCount", "releaseCreate", "loadJiraConfig", "loadExistingVersions", "assertManagementContext", "getBaseBranch", "created", "failed", "failure", "structuredContent", "r", "textContent", "releaseCreateMcpTool", "defineMcpTool", "z", "z", "question", "buildJiraVersionUrl", "jiraConfig", "version", "buildPRBody", "jiraVersionUrl", "description", "pickReleaseBranch", "releasePRsInfo", "getReleasePRsWithInfo", "branches", "pr", "types", "detectReleaseType", "descriptions", "getJiraDescriptions", "branch", "formatBranchPickerItems", "verifyReleasePRExists", "selectedBranch", "prInfo", "OperationError", "promptDescription", "current", "hint", "trimmed", "question", "releaseDescEdit", "args", "versionArg", "descriptionArg", "confirmedCommand", "loadJiraConfig", "resolveReleaseBranch", "commandEcho", "releaseId", "parseBranchName", "selectedVersion", "displayLabel", "versionName", "formatJiraName", "jiraVersion", "findVersionByName", "previousDescription", "newDescription", "buildResult", "changed", "structuredContent", "textContent", "logger", "confirmOrExit", "updateJiraVersion", "body", "updateReleasePRBody", "releaseDescEditMcpTool", "defineMcpTool", "z", "existsSync", "path", "process", "z", "createHash", "readFileSync", "sha256", "filePath", "readFileSync", "writeFileSync", "path", "z", "readdirSync", "path", "VENDOR_DIR", "MANIFEST_SKIP_DIRS", "MANIFEST_SKIP_FILES", "MANIFEST_SKIP_SUFFIXES", "isSkippedDir", "name", "isSkippedFile", "suffix", "toPosix", "relativePath", "path", "walkVendorTree", "vendorRoot", "acc", "walk", "dir", "entry", "readdirSync", "full", "isSkippedDir", "isSkippedFile", "MANIFEST_FILE", "CURRENT_SCHEMA_VERSION", "manifestSchema", "z", "isUnknownSchema", "manifest", "buildFilesMap", "vendorRoot", "files", "rel", "walkVendorTree", "sha256", "path", "cause", "reason", "readManifest", "manifestPath", "raw", "readFileSync", "parsed", "writeManifest", "meta", "writeFileSync", "compareToManifest", "expected", "actualFiles", "modified", "added", "actualSet", "removed", "MAX_REPORTED", "report", "label", "list", "logger", "rel", "vendorCheck", "options", "root", "getProjectRoot", "vendorRoot", "path", "VENDOR_DIR", "base", "existsSync", "structuredContent", "textContent", "MANIFEST_FILE", "manifest", "readManifest", "isUnknownSchema", "modified", "added", "removed", "compareToManifest", "drifted", "vendorCheckOutputSchema", "z", "vendorCheckMcpTool", "defineMcpTool", "process", "z", "fs", "path", "pathToFileURL", "z", "loadVendorConfig", "rootDir", "configPath", "path", "VENDOR_CONFIG_FILE", "stat", "fs", "rawExport", "pathToFileURL", "resolvedExport", "parsed", "vendorConfigSchema", "z", "existsSync", "mkdirSync", "writeFileSync", "path", "$", "EXCLUDE_PATTERNS", "VENDOR_README", "excludeFlags", "pattern", "getSourceCommit", "sourceRoot", "$", "copyDirectory", "source", "target", "existsSync", "sourceSlash", "targetSlash", "copyFile", "targetDir", "path", "mkdirSync", "diffDirectory", "line", "writeVendorMeta", "targetRoot", "meta", "vendorRoot", "VENDOR_DIR", "writeFileSync", "writeManifest", "resolveTargetRoot", "workspaceDir", "repo", "expandTilde", "selectTargets", "allTargets", "repos", "assertContained", "root", "child", "label", "rel", "itemName", "resolvedRoot", "resolved", "OperationError", "runSync", "config", "targets", "copied", "item", "runManifest", "skipped", "runDiff", "entries", "vendoredItems", "changes", "MAX_REPORTED", "vendorDiff", "options", "sourceRoot", "getProjectRoot", "config", "loadVendorConfig", "factory", "loadFactoryConfig", "targets", "selectTargets", "entries", "runDiff", "logger", "entry", "change", "structuredContent", "textContent", "vendorDiffOutputSchema", "z", "vendorDiffMcpTool", "defineMcpTool", "params", "path", "z", "vendorManifest", "options", "sourceRoot", "getProjectRoot", "factory", "loadFactoryConfig", "targets", "selectTargets", "commit", "getSourceCommit", "source", "path", "repos", "runManifest", "repo", "logger", "structuredContent", "textContent", "vendorManifestOutputSchema", "z", "vendorManifestMcpTool", "defineMcpTool", "params", "confirm", "path", "process", "z", "vendorSync", "options", "sourceRoot", "getProjectRoot", "config", "loadVendorConfig", "factory", "loadFactoryConfig", "targets", "selectTargets", "confirm", "path", "process", "commit", "getSourceCommit", "source", "structuredContent", "runSync", "repo", "logger", "textContent", "vendorSyncOutputSchema", "z", "vendorSyncMcpTool", "defineMcpTool", "params", "z", "version", "cliVersion", "package_default", "logger", "structuredContent", "textContent", "versionMcpTool", "defineMcpTool", "z", "confirm", "z", "$", "fs", "path", "addFoldersToCursorWorkspace", "args", "workspacePath", "folderPaths", "workspaceDir", "raw", "error", "parsed", "existingFolders", "existingAbsolutePaths", "entry", "added", "skipped", "folderPath", "absolutePath", "relativePath", "$", "launchCursor", "workspacePath", "fs", "path", "fs", "path", "removeFoldersFromCursorWorkspace", "args", "workspacePath", "folderPaths", "workspaceDir", "raw", "error", "parsed", "existingFolders", "targetAbsolutePaths", "folderPath", "removedAbsolutePaths", "filteredFolders", "entry", "entryAbsolutePath", "removed", "notFound", "absolutePath", "reconcileCursorWorkspaceFolders", "args", "workspacePath", "worktreeDir", "currentBranches", "workspaceDir", "path", "releaseRoot", "raw", "fs", "existingFolders", "desiredAbsolutePaths", "branch", "danglingFolderPaths", "entry", "entryAbsolutePath", "removed", "removeFoldersFromCursorWorkspace", "desiredFolderPaths", "added", "addFoldersToCursorWorkspace", "path", "resolveCursorWorkspacePath", "configValue", "projectRoot", "openCursorWorkspace", "args", "projectRoot", "worktreeDir", "currentBranches", "cursorConfig", "logger", "workspacePath", "resolveCursorWorkspacePath", "added", "removed", "reconcileCursorWorkspaceFolders", "launchCursor", "error", "$", "addFoldersToZedWorkspace", "args", "folderPaths", "folderPath", "$", "openZedWorkspace", "args", "projectRoot", "worktreeDir", "currentBranches", "paths", "branch", "$", "error", "logger", "$", "reuseZedWorkspace", "args", "projectRoot", "worktreeDir", "remainingBranches", "paths", "branch", "$", "logger", "error", "assertNever", "value", "addIdeWorktreeFolders", "args", "projectRoot", "worktreeDir", "branches", "config", "getInfraKitConfig", "ides", "resolveConfiguredIdes", "folderPaths", "branch", "outcomes", "ide", "logger", "workspacePath", "resolveCursorWorkspacePath", "added", "skipped", "addFoldersToCursorWorkspace", "skippedSuffix", "launchCursor", "addFoldersToZedWorkspace", "assertNever", "openIdeWorkspace", "args", "config", "getInfraKitConfig", "ides", "resolveConfiguredIdes", "outcomes", "ide", "outcome", "openCursorWorkspace", "openZedWorkspace", "assertNever", "ideProviderLabel", "provider", "assertNever", "removeIdeWorktreeFolders", "args", "projectRoot", "worktreeDir", "currentWorktrees", "removedWorktrees", "allowEditorRelaunch", "config", "getInfraKitConfig", "ides", "resolveConfiguredIdes", "folderPaths", "branch", "remainingBranches", "outcomes", "ide", "removeFromCursor", "removeFromZed", "assertNever", "workspacePath", "resolveCursorWorkspacePath", "removed", "removeFoldersFromCursorWorkspace", "logger", "error", "reuseZedWorkspace", "IDE_MODES", "FEATURE_DIR", "RELEASE_DIR", "worktreesAdd", "options", "confirmedCommand", "all", "versions", "githubDesktop", "cmux", "ide", "assertManagementContext", "currentWorktrees", "getCurrentWorktrees", "projectRoot", "getProjectRoot", "worktreeDir", "WORKTREES_DIR_SUFFIX", "ensureWorktreeDirectory", "selectedReleaseBranches", "v", "formatBranchName", "parseReleaseRef", "releasePRsInfo", "getReleasePRsWithInfo", "releasePRsList", "pr", "logger", "commandEcho", "textContent", "releaseTypes", "detectReleaseType", "descriptions", "getJiraDescriptions", "pickReleaseBranches", "formatBranchPickerItems", "releaseBranchLabels", "confirmOrExit", "config", "getInfraKitConfig", "ideMode", "resolveConfiguredIdes", "openInGithubDesktop", "confirm", "openInCmux", "branchesToCreate", "categorizeWorktrees", "createdWorktrees", "createWorktrees", "logResults", "addIdeWorktreeFolders", "branch", "$", "repoName", "getRepoName", "existingTitles", "listCmuxWorkspaceTitles", "title", "buildCmuxWorkspaceTitle", "canonicalizeCmuxTitle", "openCmuxWorkspaceWithLayout", "structuredContent", "error", "isPromptCancellation", "OperationError", "args", "currentBranchNames", "isReleaseBranch", "branches", "results", "worktreePath", "created", "index", "result", "err", "worktreesAddMcpTool", "defineMcpTool", "z", "IDE_MODES", "z", "worktreesList", "currentWorktrees", "getCurrentWorktrees", "logger", "textContent", "releasePRsInfo", "jiraDescriptions", "getReleasePRsWithInfo", "getJiraDescriptions", "releaseTypes", "pr", "detectReleaseType", "worktrees", "branch", "id", "parseBranchName", "version", "displayLabel", "type", "description", "formatJiraName", "maxVersionLength", "w", "formattedLines", "worktree", "label", "formatVersionLabel", "structuredContent", "worktreesListMcpTool", "defineMcpTool", "z", "z", "worktreesReload", "projectRoot", "getProjectRoot", "worktreeDir", "WORKTREES_DIR_SUFFIX", "currentBranches", "getCurrentWorktrees", "repoName", "getRepoName", "closedCmux", "closeCmux", "ideOutcomes", "openedCmux", "openIdeWorkspace", "reopenCmux", "result", "outcome", "sum", "logResults", "commandEcho", "textContent", "error", "logger", "OperationError", "args", "openBefore", "listCmuxWorkspaceTitles", "closed", "branch", "title", "buildCmuxWorkspaceTitle", "canonicalizeCmuxTitle", "closeCmuxWorkspaceByTitle", "opened", "openCmuxWorkspaceWithLayout", "context", "ideLabels", "provider", "ideProviderLabel", "worktreesReloadMcpTool", "defineMcpTool", "z", "z", "worktreesRemove", "options", "confirmedCommand", "all", "versions", "assertManagementContext", "currentWorktrees", "getCurrentWorktrees", "logger", "commandEcho", "textContent", "projectRoot", "getProjectRoot", "worktreeDir", "WORKTREES_DIR_SUFFIX", "selectedReleaseBranches", "v", "formatBranchName", "parseReleaseRef", "descriptions", "prInfo", "getJiraDescriptions", "getReleasePRsWithInfo", "releaseTypes", "pr", "detectReleaseType", "pickReleaseBranches", "formatBranchPickerItems", "allSelected", "releaseBranchLabels", "confirmOrExit", "repoName", "getRepoName", "removedWorktrees", "removeWorktrees", "removeIdeWorktreeFolders", "logResults", "structuredContent", "error", "isPromptCancellation", "OperationError", "removed", "branch", "worktreesRemoveMcpTool", "defineMcpTool", "z", "z", "$", "worktreesSync", "options", "confirmedCommand", "assertManagementContext", "currentWorktrees", "getCurrentWorktrees", "projectRoot", "getProjectRoot", "worktreeDir", "WORKTREES_DIR_SUFFIX", "releasePRsList", "getReleasePRs", "confirmOrExit", "commandEcho", "branchesToRemove", "categorizeWorktrees", "repoName", "getRepoName", "removedWorktrees", "removeWorktrees", "removeIdeWorktreeFolders", "logResults", "structuredContent", "textContent", "error", "isPromptCancellation", "logger", "OperationError", "args", "branch", "isReleaseBranch", "branches", "removed", "worktreePath", "title", "buildCmuxWorkspaceTitle", "closeCmuxWorkspaceByTitle", "$", "err", "worktreesSyncMcpTool", "defineMcpTool", "z", "MENU_GROUPS", "commandCatalog", "ghMergeDevMcpTool", "ghReleaseListMcpTool", "releaseCreateMcpTool", "releaseDescEditMcpTool", "ghReleaseDeployAllMcpTool", "ghReleaseDeploySelectedMcpTool", "ghReleaseDeliverMcpTool", "worktreesAddMcpTool", "worktreesListMcpTool", "worktreesReloadMcpTool", "worktreesRemoveMcpTool", "worktreesSyncMcpTool", "envStatusMcpTool", "envListMcpTool", "envLoadMcpTool", "envClearMcpTool", "vendorCheckMcpTool", "vendorDiffMcpTool", "doctorMcpTool", "auditMcpTool", "versionMcpTool", "vendorManifestMcpTool", "vendorSyncMcpTool", "getExposedMcpTools", "entry", "getMenuGroupEntries", "group", "isLongRunningCommand", "commandPath"]
|
|
7
|
-
}
|