codealmanac 0.2.3 → 0.2.4
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/README.md +19 -6
- package/dist/agents-4Y7X24WW.js +25 -0
- package/dist/chunk-BF2J4XTC.js +766 -0
- package/dist/chunk-BF2J4XTC.js.map +1 -0
- package/dist/{chunk-HNVOYWC2.js → chunk-CW4HRLMS.js} +88 -7
- package/dist/chunk-CW4HRLMS.js.map +1 -0
- package/dist/{chunk-P3LDTCLB.js → chunk-H37GKBWI.js} +13 -1
- package/dist/chunk-H37GKBWI.js.map +1 -0
- package/dist/{chunk-NBVIEZZQ.js → chunk-H6QKCB7M.js} +2 -2
- package/dist/{chunk-QQHIVTXT.js → chunk-MRRX4UQB.js} +4 -4
- package/dist/{chunk-QQHIVTXT.js.map → chunk-MRRX4UQB.js.map} +1 -1
- package/dist/chunk-P5WGG4FJ.js +359 -0
- package/dist/chunk-P5WGG4FJ.js.map +1 -0
- package/dist/{chunk-XNTNXEWY.js → chunk-QRK3JLFX.js} +131 -49
- package/dist/chunk-QRK3JLFX.js.map +1 -0
- package/dist/{chunk-V3QOQSXI.js → chunk-TILAKDN6.js} +14 -8
- package/dist/chunk-TILAKDN6.js.map +1 -0
- package/dist/chunk-TT6ZP4GS.js +282 -0
- package/dist/chunk-TT6ZP4GS.js.map +1 -0
- package/dist/chunk-UU6FBRQO.js +187 -0
- package/dist/chunk-UU6FBRQO.js.map +1 -0
- package/dist/{cli-6BOB6KAN.js → cli-MYMZ66EN.js} +123 -33
- package/dist/cli-MYMZ66EN.js.map +1 -0
- package/dist/codealmanac.js +1 -1
- package/dist/config-ML2RCR7J.js +16 -0
- package/dist/doctor-W5KQQLAX.js +17 -0
- package/dist/{register-commands-IXYE5CNZ.js → register-commands-XTK2G2FB.js} +293 -395
- package/dist/register-commands-XTK2G2FB.js.map +1 -0
- package/dist/uninstall-N7JY7ZV2.js +15 -0
- package/dist/{update-RAF7QRYF.js → update-P2IPG7RO.js} +3 -3
- package/guides/mini.md +1 -1
- package/guides/reference.md +68 -9
- package/package.json +1 -1
- package/dist/agents-RVYQ44DB.js +0 -16
- package/dist/auth-S5DVUIUJ.js +0 -18
- package/dist/chunk-HNVOYWC2.js.map +0 -1
- package/dist/chunk-P3LDTCLB.js.map +0 -1
- package/dist/chunk-PIYJQE4Z.js +0 -102
- package/dist/chunk-PIYJQE4Z.js.map +0 -1
- package/dist/chunk-SSYMRT4I.js +0 -126
- package/dist/chunk-SSYMRT4I.js.map +0 -1
- package/dist/chunk-TWM7I2LU.js +0 -116
- package/dist/chunk-TWM7I2LU.js.map +0 -1
- package/dist/chunk-V3QOQSXI.js.map +0 -1
- package/dist/chunk-WRUSDYYE.js +0 -97
- package/dist/chunk-WRUSDYYE.js.map +0 -1
- package/dist/chunk-XNTNXEWY.js.map +0 -1
- package/dist/cli-6BOB6KAN.js.map +0 -1
- package/dist/doctor-DD7EQGCA.js +0 -18
- package/dist/register-commands-IXYE5CNZ.js.map +0 -1
- package/dist/uninstall-OBV4Z3JE.js +0 -16
- /package/dist/{agents-RVYQ44DB.js.map → agents-4Y7X24WW.js.map} +0 -0
- /package/dist/{chunk-NBVIEZZQ.js.map → chunk-H6QKCB7M.js.map} +0 -0
- /package/dist/{auth-S5DVUIUJ.js.map → config-ML2RCR7J.js.map} +0 -0
- /package/dist/{doctor-DD7EQGCA.js.map → doctor-W5KQQLAX.js.map} +0 -0
- /package/dist/{uninstall-OBV4Z3JE.js.map → uninstall-N7JY7ZV2.js.map} +0 -0
- /package/dist/{update-RAF7QRYF.js.map → update-P2IPG7RO.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/setup.ts","../src/commands/setup/install-path.ts","../src/commands/setup/next-steps.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport {\n copyFile,\n mkdir,\n readFile,\n writeFile,\n} from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport {\n checkClaudeAuth,\n type ClaudeAuthStatus,\n type SpawnCliFn,\n UNAUTHENTICATED_MESSAGE,\n} from \"../agent/providers/claude/index.js\";\nimport {\n buildProviderModelChoices,\n buildProviderSetupView,\n parseAgentSelection,\n} from \"../agent/provider-view.js\";\nimport type {\n ProviderModelChoice,\n ProviderSetupView,\n} from \"../agent/provider-view.js\";\nimport {\n isAgentProviderId,\n readConfig,\n writeConfig,\n type AgentProviderId,\n} from \"../update/config.js\";\nimport { runHookInstall } from \"./hook.js\";\nimport {\n detectCurrentInstallPath,\n detectEphemeral,\n spawnGlobalInstall,\n} from \"./setup/install-path.js\";\nimport {\n countExistingPages,\n printNextSteps,\n} from \"./setup/next-steps.js\";\n\n/**\n * `codealmanac setup` — the MCP-style branded TUI that runs when a user\n * invokes the bare `codealmanac` binary (or `almanac setup` / `codealmanac\n * setup` explicitly).\n *\n * Model: `mcp-ts/src/setup.ts` from openalmanac. Same ASCII banner + badge\n * + step-indicator style, same interactive + `--yes` + non-interactive\n * modes.\n *\n * Three things get installed:\n *\n * 1. The `SessionEnd` hook in `~/.claude/settings.json` (delegated to\n * `runHookInstall` from `./hook.ts`).\n * 2. The short \"how to use codealmanac\" guide at\n * `~/.claude/codealmanac.md`, sourced from `guides/mini.md` in the\n * package.\n * 3. The full reference at `~/.claude/codealmanac-reference.md`,\n * sourced from `guides/reference.md`.\n * 4. An `@~/.claude/codealmanac.md` import line in `~/.claude/CLAUDE.md`\n * so Claude Code picks up the short guide globally.\n *\n * Everything is idempotent — running setup again is safe. `--skip-hook`\n * and `--skip-guides` opt out of the individual installs. `--yes` or a\n * non-TTY stdin skips all prompts and installs everything.\n */\n\nexport interface SetupOptions {\n /** Install everything without prompting. */\n yes?: boolean;\n /** Don't install the SessionEnd hook. */\n skipHook?: boolean;\n /** Don't install the CLAUDE.md guides. */\n skipGuides?: boolean;\n /** Set the default agent provider during setup. */\n agent?: string;\n /** Set the default model for the selected provider during setup. */\n model?: string;\n\n // ─── Injection points (tests only) ────────────────────────────────\n /** Override the subprocess spawner for `claude auth status`. */\n spawnCli?: SpawnCliFn;\n /** Override `~/.claude/settings.json` path. */\n settingsPath?: string;\n /** Override the bundled hook script path. */\n hookScriptPath?: string;\n /** Override the stable hooks directory for the hook script copy. */\n stableHooksDir?: string;\n /** Override `~/.claude/` dir for guide install. */\n claudeDir?: string;\n /** Override the directory containing `mini.md` / `reference.md`. */\n guidesDir?: string;\n /** Override interactivity; defaults to `process.stdin.isTTY`. */\n isTTY?: boolean;\n /** Stdout sink; defaults to `process.stdout`. */\n stdout?: NodeJS.WritableStream;\n /**\n * Override the install-path probe result. When `null` the probe is\n * bypassed (tests that don't care about the ephemeral-path step).\n * When a string it's treated as the detected install path.\n */\n installPath?: string | null;\n /**\n * Override the npm global install spawner (tests inject a no-op to\n * avoid actually spawning npm during CI).\n */\n spawnGlobalInstall?: () => Promise<void>;\n}\n\nexport interface SetupResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n// ─── ANSI helpers ────────────────────────────────────────────────────\n\nconst RST = \"\\x1b[0m\";\nconst DIM = \"\\x1b[2m\";\nconst WHITE_BOLD = \"\\x1b[1;37m\";\nconst BLUE = \"\\x1b[38;5;75m\";\nconst ACCENT_BG = \"\\x1b[48;5;252m\\x1b[38;5;16m\";\n\nconst GRADIENT = [\n \"\\x1b[38;5;255m\",\n \"\\x1b[38;5;253m\",\n \"\\x1b[38;5;251m\",\n \"\\x1b[38;5;249m\",\n \"\\x1b[38;5;246m\",\n \"\\x1b[38;5;243m\",\n];\n\n// `codealmanac` 11-letter ASCII banner. Chosen for tasteful rendering —\n// same banner used in the MCP setup wizard design, retooled letters for\n// the word \"codealmanac\". Each glyph is 6 lines tall.\n//\n// If you tweak this, keep it to ≤80 visual columns wide so it fits in\n// narrow terminals (80 cols is the classic default).\nconst LOGO_LINES = [\n \" ___ ___ ___ ___ _ _ __ __ _ _ _ _ ___ \",\n \" / __/ _ \\\\| \\\\| __| /_\\\\ | | | \\\\/ | /_\\\\ | \\\\| | /_\\\\ / __|\",\n \"| (_| (_) | |) | _| / _ \\\\| |__| |\\\\/| |/ _ \\\\| .` |/ _ \\\\ (__ \",\n \" \\\\___\\\\___/|___/|___/_/ \\\\_\\\\____|_| |_/_/ \\\\_\\\\_|\\\\_/_/ \\\\_\\\\___|\",\n \" \",\n \" a living wiki for codebases, for your agent \",\n];\n\nconst BAR = ` ${DIM}\\u2502${RST}`;\n\nfunction printBanner(out: NodeJS.WritableStream): void {\n out.write(\"\\n\");\n for (let i = 0; i < LOGO_LINES.length; i++) {\n const color = GRADIENT[Math.min(i, GRADIENT.length - 1)] ?? \"\";\n out.write(`${color}${LOGO_LINES[i]}${RST}\\n`);\n }\n out.write(`\\n${WHITE_BOLD} Install the hook + agent guides${RST}\\n`);\n}\n\nfunction printBadge(out: NodeJS.WritableStream): void {\n out.write(`\\n ${ACCENT_BG} codealmanac ${RST}\\n\\n`);\n}\n\nfunction stepDone(out: NodeJS.WritableStream, msg: string): void {\n out.write(` ${BLUE}\\u25c7${RST} ${msg}\\n`);\n}\n\nfunction stepActive(out: NodeJS.WritableStream, msg: string): void {\n out.write(` ${BLUE}\\u25c6${RST} ${msg}\\n`);\n}\n\nfunction stepSkipped(out: NodeJS.WritableStream, msg: string): void {\n out.write(` ${DIM}\\u25cb ${msg}${RST}\\n`);\n}\n\n// ─── Entry point ─────────────────────────────────────────────────────\n\nexport async function runSetup(\n options: SetupOptions = {},\n): Promise<SetupResult> {\n const out = options.stdout ?? process.stdout;\n const isTTY =\n options.isTTY ?? (process.stdin.isTTY === true);\n const interactive = isTTY && options.yes !== true;\n\n // No-op fast path. When the caller explicitly skipped every install\n // step, rendering the full banner + step markers + \"Setup complete\"\n // box is actively misleading — nothing was actually set up. Emit a\n // single terse line and exit so the user gets honest feedback and\n // piped callers (CI, scripts) don't parse through nine lines of ANSI\n // to conclude nothing happened.\n if (options.skipHook === true && options.skipGuides === true) {\n out.write(\n \"codealmanac: nothing to install — use --help to see what setup does\\n\",\n );\n return { stdout: \"\", stderr: \"\", exitCode: 0 };\n }\n\n printBanner(out);\n printBadge(out);\n\n // Step 1: auth status. We report what we find; we don't block. The user\n // can still install the hook + guides without being logged in — they'll\n // hit the auth wall on first `capture`, not on setup.\n const auth = await safeCheckAuth(options.spawnCli);\n reportAuth(out, auth);\n out.write(BAR + \"\\n\");\n\n const agentChoice = await chooseDefaultAgent({\n out,\n interactive,\n requested: options.agent,\n requestedModel: options.model,\n spawnCli: options.spawnCli,\n });\n if (!agentChoice.ok) {\n return {\n stdout: \"\",\n stderr: `almanac: ${agentChoice.error}\\n`,\n exitCode: 1,\n };\n }\n stepDone(\n out,\n `Default agent: ${WHITE_BOLD}${agentChoice.provider}${RST}` +\n ` (${agentChoice.model ?? \"provider default\"})`,\n );\n out.write(BAR + \"\\n\");\n\n // Step 1b: ephemeral install detection. When codealmanac was invoked via\n // `npx codealmanac` (no prior `npm i -g`), the binary lives inside an\n // npx cache directory or pnpm store that can be evicted at any time.\n // `almanac` is also not on PATH, so the user can't use it after setup.\n //\n // When we detect an ephemeral location, we offer (or, on --yes, perform)\n // a `npm install -g codealmanac` to make the install permanent.\n //\n // This is Bug #2 from codealmanac-known-bugs.md.\n const ephem = options.installPath !== undefined\n ? (options.installPath !== null\n ? detectEphemeral(options.installPath)\n : false)\n : detectEphemeral(detectCurrentInstallPath());\n if (ephem) {\n let globalAction: InstallDecision = \"install\";\n if (interactive) {\n globalAction = await confirm(\n out,\n `Running from an ephemeral npx location. Install globally so 'almanac' stays on PATH?`,\n true,\n );\n }\n if (globalAction === \"install\") {\n stepActive(out, \"Installing codealmanac globally…\");\n try {\n await (options.spawnGlobalInstall ?? spawnGlobalInstall)();\n stepDone(out, \"codealmanac installed globally (almanac now on PATH)\");\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n stepActive(out, `Global install failed: ${msg}`);\n out.write(\n ` ${DIM}You can retry manually: npm install -g codealmanac${RST}\\n`,\n );\n }\n } else {\n stepSkipped(\n out,\n `Global install ${DIM}skipped — almanac will not be on PATH after this session${RST}`,\n );\n }\n out.write(BAR + \"\\n\");\n }\n\n // Step 2: install the hook (default yes).\n let hookAction: InstallDecision = \"install\";\n if (options.skipHook === true) {\n hookAction = \"skip\";\n } else if (interactive) {\n hookAction = await confirm(\n out,\n \"Install auto-capture hooks for Claude, Codex, and Cursor?\",\n true,\n );\n }\n\n let hookResultLine = \"\";\n if (hookAction === \"install\") {\n const res = await runHookInstall({\n source: \"all\",\n settingsPath: options.settingsPath,\n hookScriptPath: options.hookScriptPath,\n stableHooksDir: options.stableHooksDir,\n });\n if (res.exitCode !== 0) {\n stepActive(out, `SessionEnd hook: ${res.stderr.trim()}`);\n return {\n stdout: \"\",\n stderr: res.stderr,\n exitCode: res.exitCode,\n };\n }\n hookResultLine = res.stdout.includes(\"already installed\")\n ? `Auto-capture hooks ${DIM}already installed${RST}`\n : `Auto-capture hooks installed`;\n stepDone(out, hookResultLine);\n } else {\n stepSkipped(out, `SessionEnd hook ${DIM}skipped${RST}`);\n }\n out.write(BAR + \"\\n\");\n\n // Step 3: install the guides.\n let guidesAction: InstallDecision = \"install\";\n if (options.skipGuides === true) {\n guidesAction = \"skip\";\n } else if (interactive) {\n guidesAction = await confirm(\n out,\n \"Install the codealmanac usage guides into ~/.claude/ and import them from CLAUDE.md?\",\n true,\n );\n }\n\n let guidesSummary: string;\n if (guidesAction === \"install\") {\n try {\n const summary = await installGuides({\n claudeDir: options.claudeDir ?? path.join(homedir(), \".claude\"),\n guidesDir: options.guidesDir ?? resolveGuidesDir(),\n });\n guidesSummary = summary.anyChanges\n ? `Guides installed (${summary.filesWritten.join(\", \")})`\n : `Guides ${DIM}already installed${RST}`;\n stepDone(out, guidesSummary);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n stdout: \"\",\n stderr: `almanac: guide install failed: ${msg}\\n`,\n exitCode: 1,\n };\n }\n } else {\n stepSkipped(out, `Guides ${DIM}skipped${RST}`);\n }\n out.write(BAR + \"\\n\");\n\n stepDone(out, `${BLUE}Setup complete${RST}`);\n out.write(\"\\n\");\n\n // Detect whether the current working directory is inside a repo that\n // already has a wiki with pages. This fixes Bug #6 from\n // codealmanac-known-bugs.md: Engineer B clones a repo that already has\n // `.almanac/pages/` (committed by Engineer A) and gets told to run\n // `almanac bootstrap`, which is wrong — the wiki already exists.\n const existingPageCount = countExistingPages(process.cwd());\n printNextSteps(out, existingPageCount);\n\n return { stdout: \"\", stderr: \"\", exitCode: 0 };\n}\n\n// ─── Auth reporting ──────────────────────────────────────────────────\n\nasync function safeCheckAuth(\n spawnCli?: SpawnCliFn,\n): Promise<ClaudeAuthStatus> {\n try {\n return await checkClaudeAuth(spawnCli);\n } catch {\n return { loggedIn: false };\n }\n}\n\ntype AgentChoice =\n | { ok: true; provider: AgentProviderId; model: string | null }\n | { ok: false; error: string };\n\nasync function chooseDefaultAgent(args: {\n out: NodeJS.WritableStream;\n interactive: boolean;\n requested?: string;\n requestedModel?: string;\n spawnCli?: SpawnCliFn;\n}): Promise<AgentChoice> {\n const config = await readConfig();\n let view: ProviderSetupView | null = null;\n let selected = args.requested ?? config.agent.default;\n if (args.interactive || args.requested !== undefined) {\n view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });\n }\n if (args.interactive && args.requested === undefined && view !== null) {\n args.out.write(\" Choose default agent:\\n\");\n view.choices.forEach((choice, index) => {\n const tag = choice.recommended ? \" recommended\" : \"\";\n const status = choice.ready ? \"ready\" : \"not ready\";\n const detail = choice.account ?? choice.fixCommand ?? choice.detail;\n args.out.write(\n ` ${index + 1}. ${choice.label.padEnd(6)} ${status.padEnd(9)}${tag} ${detail}\\n`,\n );\n });\n selected = (await promptText(\n args.out,\n \"Default agent\",\n view.recommendedProvider,\n )).toLowerCase();\n const number = Number.parseInt(selected, 10);\n if (\n Number.isInteger(number) &&\n number >= 1 &&\n number <= view.choices.length\n ) {\n selected = view.choices[number - 1]?.id ?? selected;\n }\n }\n const parsed = parseAgentSelection(selected);\n if (parsed.provider === null || !isAgentProviderId(parsed.provider)) {\n return {\n ok: false,\n error:\n `unknown agent '${selected}'. Expected one of: claude, codex, cursor.`,\n };\n }\n const provider = parsed.provider;\n let selectedChoice = view?.choices.find((choice) => choice.id === provider);\n if (\n args.interactive &&\n selectedChoice !== undefined &&\n !selectedChoice.ready &&\n selectedChoice.fixCommand?.startsWith(\"run: \") === true\n ) {\n const command = selectedChoice.fixCommand.slice(\"run: \".length);\n const runLogin = await confirm(\n args.out,\n `${selectedChoice.label} is not ready. Run '${command}' now?`,\n true,\n );\n if (runLogin === \"install\") {\n const login = await runLoginCommand(command);\n if (login.ok) {\n view = await buildProviderSetupView({ config, spawnCli: args.spawnCli });\n selectedChoice = view.choices.find((choice) => choice.id === provider);\n } else {\n stepActive(args.out, `${selectedChoice.label} login failed: ${login.error}`);\n }\n }\n }\n const requestedModel = args.requestedModel ?? parsed.model;\n const model = requestedModel ?? await chooseProviderModel({\n out: args.out,\n interactive: args.interactive,\n provider,\n choice: selectedChoice,\n configuredModel: config.agent.models[provider] ?? null,\n });\n await writeConfig({\n ...config,\n agent: {\n ...config.agent,\n default: provider,\n models: {\n ...config.agent.models,\n [provider]: model,\n },\n },\n });\n if (!args.interactive || args.requested !== undefined) {\n const detail = selectedChoice?.ready === true\n ? \"ready\"\n : selectedChoice?.fixCommand ?? selectedChoice?.detail ?? \"status unknown\";\n stepDone(args.out, `Agent readiness: ${detail}`);\n }\n return { ok: true, provider, model };\n}\n\nasync function chooseProviderModel(args: {\n out: NodeJS.WritableStream;\n interactive: boolean;\n provider: AgentProviderId;\n choice?: ProviderSetupView[\"choices\"][number];\n configuredModel: string | null;\n}): Promise<string | null> {\n const choices =\n args.choice?.modelChoices ??\n buildProviderModelChoices(args.provider, args.configuredModel);\n const recommended =\n choices.find((choice) => choice.recommended) ??\n choices.find((choice) => choice.source === \"provider-default\");\n if (!args.interactive) {\n return args.configuredModel ?? recommended?.value ?? null;\n }\n\n args.out.write(` Choose ${args.provider} model:\\n`);\n choices.forEach((choice, index) => {\n const marker = choice.recommended ? \" recommended\" : \"\";\n const current = choice.value === args.configuredModel ? \" current\" : \"\";\n args.out.write(\n ` ${index + 1}. ${choice.label}${marker}${current}\\n`,\n );\n });\n const currentIndex = choices.findIndex((choice) =>\n choice.value === args.configuredModel\n );\n const recommendedIndex = choices.findIndex((choice) => choice.recommended);\n const defaultIndex =\n currentIndex >= 0\n ? currentIndex + 1\n : recommendedIndex >= 0\n ? recommendedIndex + 1\n : 1;\n const selected = await promptText(args.out, \"Model\", String(defaultIndex));\n const number = Number.parseInt(selected, 10);\n let modelChoice: ProviderModelChoice | undefined;\n if (\n Number.isInteger(number) &&\n number >= 1 &&\n number <= choices.length\n ) {\n modelChoice = choices[number - 1];\n } else {\n modelChoice = choices.find((choice) => choice.value === selected);\n }\n if (modelChoice?.source === \"custom\") {\n const custom = await promptText(args.out, \"Custom model id\", \"\");\n return custom.length > 0 ? custom : recommended?.value ?? null;\n }\n return modelChoice?.value ?? recommended?.value ?? null;\n}\n\nasync function runLoginCommand(command: string): Promise<\n | { ok: true }\n | { ok: false; error: string }\n> {\n return new Promise((resolve) => {\n const child = spawn(command, {\n shell: true,\n stdio: \"inherit\",\n });\n child.on(\"error\", (err) => {\n resolve({ ok: false, error: err.message });\n });\n child.on(\"close\", (code) => {\n if (code === 0) {\n resolve({ ok: true });\n return;\n }\n resolve({ ok: false, error: `exited ${code ?? 1}` });\n });\n });\n}\n\nfunction reportAuth(\n out: NodeJS.WritableStream,\n auth: ClaudeAuthStatus,\n): void {\n if (auth.loggedIn) {\n const who = auth.email ?? \"Claude account\";\n const plan =\n auth.subscriptionType !== undefined\n ? ` ${DIM}(${auth.subscriptionType})${RST}`\n : \"\";\n stepDone(out, `Claude auth: ${WHITE_BOLD}${who}${RST}${plan}`);\n return;\n }\n if (\n process.env.ANTHROPIC_API_KEY !== undefined &&\n process.env.ANTHROPIC_API_KEY.length > 0\n ) {\n stepDone(out, `Claude auth: ${WHITE_BOLD}ANTHROPIC_API_KEY${RST} set`);\n return;\n }\n // Not blocking — just report and show the two paths.\n stepActive(out, `Claude auth: ${DIM}not signed in${RST}`);\n for (const line of UNAUTHENTICATED_MESSAGE.split(\"\\n\")) {\n out.write(` ${DIM}\\u2502 ${line}${RST}\\n`);\n }\n}\n\n// ─── Guide installation ──────────────────────────────────────────────\n\ninterface InstallGuidesOptions {\n claudeDir: string;\n guidesDir: string;\n}\n\ninterface InstallGuidesResult {\n anyChanges: boolean;\n filesWritten: string[];\n}\n\n/**\n * Copy the two guide files into `~/.claude/` and append an `@import`\n * line to `~/.claude/CLAUDE.md`. Every step is idempotent:\n *\n * - Guide files are compared by bytes before we write. If the content\n * matches the bundled version, we skip (so `setup` doesn't cause a\n * spurious mtime bump on every invocation).\n * - The import line is appended only if `CLAUDE.md` doesn't already\n * contain the exact `@~/.claude/codealmanac.md` token on a line by\n * itself. We don't try to parse the file — any mention of the token\n * on a non-comment line is treated as \"already present\".\n *\n * Returns a summary the caller uses to decide whether to say \"installed\"\n * or \"already installed\" in the TUI.\n */\nasync function installGuides(\n options: InstallGuidesOptions,\n): Promise<InstallGuidesResult> {\n await mkdir(options.claudeDir, { recursive: true });\n\n const srcMini = path.join(options.guidesDir, \"mini.md\");\n const srcRef = path.join(options.guidesDir, \"reference.md\");\n if (!existsSync(srcMini)) {\n throw new Error(`missing bundled guide: ${srcMini}`);\n }\n if (!existsSync(srcRef)) {\n throw new Error(`missing bundled guide: ${srcRef}`);\n }\n\n const destMini = path.join(options.claudeDir, \"codealmanac.md\");\n const destRef = path.join(options.claudeDir, \"codealmanac-reference.md\");\n\n const miniChanged = await copyIfChanged(srcMini, destMini);\n const refChanged = await copyIfChanged(srcRef, destRef);\n\n const claudeMd = path.join(options.claudeDir, \"CLAUDE.md\");\n const importChanged = await ensureImport(claudeMd);\n\n const filesWritten: string[] = [];\n if (miniChanged) filesWritten.push(\"codealmanac.md\");\n if (refChanged) filesWritten.push(\"codealmanac-reference.md\");\n if (importChanged) filesWritten.push(\"CLAUDE.md\");\n\n return { anyChanges: filesWritten.length > 0, filesWritten };\n}\n\nasync function copyIfChanged(src: string, dest: string): Promise<boolean> {\n const srcBytes = await readFile(src);\n if (existsSync(dest)) {\n try {\n const destBytes = await readFile(dest);\n if (srcBytes.equals(destBytes)) return false;\n } catch {\n // Fall through to write.\n }\n }\n await copyFile(src, dest);\n return true;\n}\n\n/** The exact import line we manage. Changing this requires updating\n * uninstall too. */\nexport const IMPORT_LINE = \"@~/.claude/codealmanac.md\";\n\n/**\n * Append the import line to `~/.claude/CLAUDE.md` if it isn't already\n * present. Creates the file if absent. Returns true when we wrote, false\n * when the line was already there.\n *\n * We match on `@~/.claude/codealmanac.md` appearing on any non-empty\n * line (trimmed). This catches both the bare line we write and any\n * user-edited variant (comments, trailing whitespace). We deliberately\n * do NOT try to repair a user who deleted the newline — that's their\n * file to shape.\n */\nasync function ensureImport(claudeMdPath: string): Promise<boolean> {\n let existing = \"\";\n if (existsSync(claudeMdPath)) {\n existing = await readFile(claudeMdPath, \"utf8\");\n }\n if (hasImportLine(existing)) return false;\n\n const sep =\n existing.length === 0 ? \"\" : existing.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n const body = `${existing}${sep}${IMPORT_LINE}\\n`;\n await writeFile(claudeMdPath, body, \"utf8\");\n return true;\n}\n\nexport function hasImportLine(contents: string): boolean {\n // Match line-starts-with-token rather than exact-line equality so a\n // user who annotated the import line (`@~/.claude/codealmanac.md #\n // codealmanac`) doesn't cause us to re-append a duplicate below.\n // The trailing-character check rules out accidental matches on a\n // longer line like `@~/.claude/codealmanac.md-extra`.\n const lines = contents.split(/\\r?\\n/).map((l) => l.trim());\n return lines.some((line) => {\n if (line === IMPORT_LINE) return true;\n if (!line.startsWith(IMPORT_LINE)) return false;\n const next = line[IMPORT_LINE.length];\n return next === \" \" || next === \"\\t\";\n });\n}\n\n// ─── Interactive prompt ──────────────────────────────────────────────\n\ntype InstallDecision = \"install\" | \"skip\";\n\n/**\n * Minimal `[Y/n]` prompt. No raw mode, no cursor — just readline. The\n * MCP setup uses a fancy arrow-key TUI for multi-choice; we only have\n * binary decisions here, so a line-reader prompt is clearer and doesn't\n * fight with the step-indicator rendering above it.\n */\nfunction confirm(\n out: NodeJS.WritableStream,\n question: string,\n defaultYes: boolean,\n): Promise<InstallDecision> {\n return new Promise((resolve) => {\n const hint = defaultYes ? \"[Y/n]\" : \"[y/N]\";\n out.write(` ${BLUE}\\u25c6${RST} ${question} ${DIM}${hint}${RST} `);\n\n let buf = \"\";\n const onData = (chunk: Buffer): void => {\n buf += chunk.toString(\"utf8\");\n const nl = buf.indexOf(\"\\n\");\n if (nl === -1) return;\n process.stdin.removeListener(\"data\", onData);\n process.stdin.pause();\n\n const answer = buf.slice(0, nl).trim().toLowerCase();\n const accepted =\n answer.length === 0\n ? defaultYes\n : answer === \"y\" || answer === \"yes\";\n resolve(accepted ? \"install\" : \"skip\");\n };\n\n process.stdin.resume();\n process.stdin.on(\"data\", onData);\n });\n}\n\nfunction promptText(\n out: NodeJS.WritableStream,\n question: string,\n defaultValue: string,\n): Promise<string> {\n return new Promise((resolve) => {\n out.write(\n ` ${BLUE}\\u25c6${RST} ${question} ${DIM}[${defaultValue}]${RST} `,\n );\n\n let buf = \"\";\n const onData = (chunk: Buffer): void => {\n buf += chunk.toString(\"utf8\");\n const nl = buf.indexOf(\"\\n\");\n if (nl === -1) return;\n process.stdin.removeListener(\"data\", onData);\n process.stdin.pause();\n\n const answer = buf.slice(0, nl).trim();\n resolve(answer.length === 0 ? defaultValue : answer);\n };\n\n process.stdin.resume();\n process.stdin.on(\"data\", onData);\n });\n}\n\n// ─── Guides path resolution ──────────────────────────────────────────\n\n/**\n * Locate `guides/` relative to the installed package. Mirrors\n * `resolvePromptsDir` from `src/agent/prompts.ts`.\n *\n * Two runtime layouts to handle:\n *\n * 1. **Bundled dist.** `dist/codealmanac.js` → walk one level up →\n * `guides/`.\n * 2. **Source (tests / tsx).** `src/commands/setup.ts` → walk two\n * levels up → `guides/`.\n *\n * We also try `createRequire` to resolve the package root from the\n * `codealmanac/package.json` manifest, as a belt-and-suspenders fallback\n * for unusual install layouts (monorepo hoisting, etc.). That path is\n * only exercised when the direct walk-up fails.\n */\nexport function resolveGuidesDir(): string {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, \"..\", \"guides\"), // dist layout\n path.resolve(here, \"..\", \"..\", \"guides\"), // src layout\n path.resolve(here, \"..\", \"..\", \"..\", \"guides\"),\n ];\n for (const dir of candidates) {\n if (looksLikeGuidesDir(dir)) return dir;\n }\n // Fallback: resolve via the package.json of the currently-running\n // codealmanac. createRequire lets us ask Node's resolver rather than\n // guessing at directory layouts.\n try {\n const require = createRequire(import.meta.url);\n const pkgJson = require.resolve(\"codealmanac/package.json\");\n const guides = path.join(path.dirname(pkgJson), \"guides\");\n if (looksLikeGuidesDir(guides)) return guides;\n } catch {\n // Ignore — we'll throw with the candidate list below.\n }\n throw new Error(\n \"could not locate bundled guides/ directory. Tried:\\n\" +\n candidates.map((c) => ` - ${c}`).join(\"\\n\"),\n );\n}\n\nfunction looksLikeGuidesDir(dir: string): boolean {\n return existsSync(path.join(dir, \"mini.md\"));\n}\n","import { execFile } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Return the directory of the currently-running codealmanac install by\n * walking up from this module's file to the nearest `package.json` whose\n * `name` is `codealmanac`. Returns the empty string when the walk fails.\n */\nexport function detectCurrentInstallPath(): string {\n try {\n const req = createRequire(import.meta.url);\n const here = fileURLToPath(import.meta.url);\n let dir = path.dirname(here);\n for (let i = 0; i < 6; i++) {\n const pkgPath = path.join(dir, \"package.json\");\n try {\n const raw = req(\"fs\").readFileSync(pkgPath, \"utf-8\") as string;\n const pkg = JSON.parse(raw) as { name?: unknown };\n if (pkg.name === \"codealmanac\") return dir;\n } catch {\n // keep walking\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch {\n // import.meta.url unavailable — not ephemeral for our purposes.\n }\n return \"\";\n}\n\n/**\n * Return true when the given install path looks ephemeral.\n *\n * Ephemeral locations we recognize:\n * - `~/.npm/_npx/` — npm's npx cache (GC'd on version bumps or\n * `npm cache clean`)\n * - `~/.local/share/pnpm/dlx/` — pnpm's dlx (like npx) cache\n * - `/tmp/` or `/var/folders/` — common CI / temp paths\n *\n * A global install (`~/.nvm/.../lib/node_modules/`, `/usr/local/lib/...`,\n * `~/.local/lib/node_modules/`) is NOT ephemeral.\n */\nexport function detectEphemeral(installPath: string): boolean {\n if (installPath.length === 0) return false;\n const home = homedir();\n if (installPath.startsWith(path.join(home, \".npm\", \"_npx\"))) return true;\n if (\n installPath.startsWith(path.join(home, \".local\", \"share\", \"pnpm\", \"dlx\"))\n ) return true;\n if (installPath.startsWith(\"/tmp/\")) return true;\n if (installPath.startsWith(\"/var/folders/\")) return true;\n return false;\n}\n\n/**\n * Spawn `npm install -g codealmanac@latest` in a child process and wait\n * for it to finish. Rejects on non-zero exit or spawn error.\n */\nexport function spawnGlobalInstall(): Promise<void> {\n return new Promise((resolve, reject) => {\n execFile(\n \"npm\",\n [\"install\", \"-g\", \"codealmanac@latest\"],\n { shell: false },\n (err, _stdout, stderr) => {\n if (err !== null) {\n reject(\n new Error(\n stderr.length > 0\n ? stderr.trim().split(\"\\n\")[0] ?? err.message\n : err.message,\n ),\n );\n } else {\n resolve();\n }\n },\n );\n });\n}\n","import { existsSync, readdirSync } from \"node:fs\";\nimport path from \"node:path\";\n\nconst RST = \"\\x1b[0m\";\nconst BOLD = \"\\x1b[1m\";\nconst DIM = \"\\x1b[2m\";\nconst WHITE_BOLD = \"\\x1b[1;37m\";\nconst BLUE = \"\\x1b[38;5;75m\";\nconst BLUE_DIM = \"\\x1b[38;5;69m\";\n\n/**\n * Print the \"Next steps\" box. When `existingPageCount` is greater than 0,\n * the current working directory already has a wiki with committed pages.\n * In that case we skip the `almanac bootstrap` step and tell the user to\n * start querying.\n */\nexport function printNextSteps(\n out: NodeJS.WritableStream,\n existingPageCount: number,\n): void {\n const innerW = 62;\n const vis = (s: string): number =>\n s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n const row = (content: string): string => {\n const padding = Math.max(0, innerW - vis(content));\n return ` ${BLUE_DIM}\\u2502${RST}${content}${\" \".repeat(padding)}${BLUE_DIM}\\u2502${RST}\\n`;\n };\n const empty = row(\"\");\n\n out.write(` ${BLUE_DIM}\\u256d${\"─\".repeat(innerW)}\\u256e${RST}\\n`);\n out.write(empty);\n out.write(row(` ${WHITE_BOLD}Next steps${RST}`));\n out.write(empty);\n\n if (existingPageCount > 0) {\n out.write(\n row(\n ` ${BLUE}\\u25c7${RST} This repo already has a wiki ${DIM}(${existingPageCount} page${existingPageCount === 1 ? \"\" : \"s\"})${RST}`,\n ),\n );\n out.write(empty);\n out.write(row(` ${BLUE}1.${RST} Start querying your wiki:`));\n out.write(row(` ${BOLD}almanac search --mentions <file>${RST}`));\n out.write(\n row(` ${BLUE}2.${RST} Work normally — capture runs on session end`),\n );\n } else {\n out.write(\n row(` ${BLUE}1.${RST} ${BOLD}cd${RST} into a repo you want to document`),\n );\n out.write(\n row(\n ` ${BLUE}2.${RST} ${BOLD}almanac bootstrap${RST} ${DIM}# scaffold the wiki${RST}`,\n ),\n );\n out.write(\n row(` ${BLUE}3.${RST} Work normally — capture runs on session end`),\n );\n }\n\n out.write(empty);\n out.write(` ${BLUE_DIM}\\u2570${\"─\".repeat(innerW)}\\u256f${RST}\\n\\n`);\n}\n\n/**\n * Count `.md` files in `.almanac/pages/` under the current working\n * directory or any parent. Returns 0 when no wiki is found or the pages\n * directory is empty.\n */\nexport function countExistingPages(cwd: string): number {\n try {\n let dir = cwd;\n for (let i = 0; i < 10; i++) {\n const pagesDir = path.join(dir, \".almanac\", \"pages\");\n if (existsSync(pagesDir)) {\n try {\n const entries = readdirSync(pagesDir);\n return entries.filter((e) => e.endsWith(\".md\")).length;\n } catch {\n return 0;\n }\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch {\n // Swallow — never crash setup because of this.\n }\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA,SAAS,cAAAA,mBAAkB;AAC3B,SAAS,aAAa;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,WAAAC,gBAAe;AACxB,OAAOC,WAAU;AACjB,SAAS,iBAAAC,sBAAqB;;;ACX9B,SAAS,gBAAgB;AACzB,SAAS,qBAAqB;AAC9B,SAAS,eAAe;AACxB,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAOvB,SAAS,2BAAmC;AACjD,MAAI;AACF,UAAM,MAAM,cAAc,YAAY,GAAG;AACzC,UAAM,OAAO,cAAc,YAAY,GAAG;AAC1C,QAAI,MAAM,KAAK,QAAQ,IAAI;AAC3B,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,UAAU,KAAK,KAAK,KAAK,cAAc;AAC7C,UAAI;AACF,cAAM,MAAM,IAAI,IAAI,EAAE,aAAa,SAAS,OAAO;AACnD,cAAM,MAAM,KAAK,MAAM,GAAG;AAC1B,YAAI,IAAI,SAAS,cAAe,QAAO;AAAA,MACzC,QAAQ;AAAA,MAER;AACA,YAAM,SAAS,KAAK,QAAQ,GAAG;AAC/B,UAAI,WAAW,IAAK;AACpB,YAAM;AAAA,IACR;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAcO,SAAS,gBAAgB,aAA8B;AAC5D,MAAI,YAAY,WAAW,EAAG,QAAO;AACrC,QAAM,OAAO,QAAQ;AACrB,MAAI,YAAY,WAAW,KAAK,KAAK,MAAM,QAAQ,MAAM,CAAC,EAAG,QAAO;AACpE,MACE,YAAY,WAAW,KAAK,KAAK,MAAM,UAAU,SAAS,QAAQ,KAAK,CAAC,EACxE,QAAO;AACT,MAAI,YAAY,WAAW,OAAO,EAAG,QAAO;AAC5C,MAAI,YAAY,WAAW,eAAe,EAAG,QAAO;AACpD,SAAO;AACT;AAMO,SAAS,qBAAoC;AAClD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC;AAAA,MACE;AAAA,MACA,CAAC,WAAW,MAAM,oBAAoB;AAAA,MACtC,EAAE,OAAO,MAAM;AAAA,MACf,CAAC,KAAK,SAAS,WAAW;AACxB,YAAI,QAAQ,MAAM;AAChB;AAAA,YACE,IAAI;AAAA,cACF,OAAO,SAAS,IACZ,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,KAAK,IAAI,UACpC,IAAI;AAAA,YACV;AAAA,UACF;AAAA,QACF,OAAO;AACL,kBAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACpFA,SAAS,YAAY,mBAAmB;AACxC,OAAOC,WAAU;AAEjB,IAAM,MAAM;AACZ,IAAM,OAAO;AACb,IAAM,MAAM;AACZ,IAAM,aAAa;AACnB,IAAM,OAAO;AACb,IAAM,WAAW;AAQV,SAAS,eACd,KACA,mBACM;AACN,QAAM,SAAS;AACf,QAAM,MAAM,CAAC,MACX,EAAE,QAAQ,mBAAmB,EAAE,EAAE;AACnC,QAAM,MAAM,CAAC,YAA4B;AACvC,UAAM,UAAU,KAAK,IAAI,GAAG,SAAS,IAAI,OAAO,CAAC;AACjD,WAAO,KAAK,QAAQ,SAAS,GAAG,GAAG,OAAO,GAAG,IAAI,OAAO,OAAO,CAAC,GAAG,QAAQ,SAAS,GAAG;AAAA;AAAA,EACzF;AACA,QAAM,QAAQ,IAAI,EAAE;AAEpB,MAAI,MAAM,KAAK,QAAQ,SAAS,SAAI,OAAO,MAAM,CAAC,SAAS,GAAG;AAAA,CAAI;AAClE,MAAI,MAAM,KAAK;AACf,MAAI,MAAM,IAAI,KAAK,UAAU,aAAa,GAAG,EAAE,CAAC;AAChD,MAAI,MAAM,KAAK;AAEf,MAAI,oBAAoB,GAAG;AACzB,QAAI;AAAA,MACF;AAAA,QACE,KAAK,IAAI,SAAS,GAAG,kCAAkC,GAAG,IAAI,iBAAiB,QAAQ,sBAAsB,IAAI,KAAK,GAAG,IAAI,GAAG;AAAA,MAClI;AAAA,IACF;AACA,QAAI,MAAM,KAAK;AACf,QAAI,MAAM,IAAI,KAAK,IAAI,KAAK,GAAG,6BAA6B,CAAC;AAC7D,QAAI,MAAM,IAAI,UAAU,IAAI,mCAAmC,GAAG,EAAE,CAAC;AACrE,QAAI;AAAA,MACF,IAAI,KAAK,IAAI,KAAK,GAAG,oDAA+C;AAAA,IACtE;AAAA,EACF,OAAO;AACL,QAAI;AAAA,MACF,IAAI,KAAK,IAAI,KAAK,GAAG,KAAK,IAAI,KAAK,GAAG,mCAAmC;AAAA,IAC3E;AACA,QAAI;AAAA,MACF;AAAA,QACE,KAAK,IAAI,KAAK,GAAG,KAAK,IAAI,oBAAoB,GAAG,KAAK,GAAG,sBAAsB,GAAG;AAAA,MACpF;AAAA,IACF;AACA,QAAI;AAAA,MACF,IAAI,KAAK,IAAI,KAAK,GAAG,oDAA+C;AAAA,IACtE;AAAA,EACF;AAEA,MAAI,MAAM,KAAK;AACf,MAAI,MAAM,KAAK,QAAQ,SAAS,SAAI,OAAO,MAAM,CAAC,SAAS,GAAG;AAAA;AAAA,CAAM;AACtE;AAOO,SAAS,mBAAmB,KAAqB;AACtD,MAAI;AACF,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,YAAM,WAAWA,MAAK,KAAK,KAAK,YAAY,OAAO;AACnD,UAAI,WAAW,QAAQ,GAAG;AACxB,YAAI;AACF,gBAAM,UAAU,YAAY,QAAQ;AACpC,iBAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,EAAE;AAAA,QAClD,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AACA,YAAM,SAASA,MAAK,QAAQ,GAAG;AAC/B,UAAI,WAAW,IAAK;AACpB,YAAM;AAAA,IACR;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;;;AF+BA,IAAMC,OAAM;AACZ,IAAMC,OAAM;AACZ,IAAMC,cAAa;AACnB,IAAMC,QAAO;AACb,IAAM,YAAY;AAElB,IAAM,WAAW;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQA,IAAM,aAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,MAAM,KAAKF,IAAG,SAASD,IAAG;AAEhC,SAAS,YAAY,KAAkC;AACrD,MAAI,MAAM,IAAI;AACd,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,QAAQ,SAAS,KAAK,IAAI,GAAG,SAAS,SAAS,CAAC,CAAC,KAAK;AAC5D,QAAI,MAAM,GAAG,KAAK,GAAG,WAAW,CAAC,CAAC,GAAGA,IAAG;AAAA,CAAI;AAAA,EAC9C;AACA,MAAI,MAAM;AAAA,EAAKE,WAAU,oCAAoCF,IAAG;AAAA,CAAI;AACtE;AAEA,SAAS,WAAW,KAAkC;AACpD,MAAI,MAAM;AAAA,KAAQ,SAAS,gBAAgBA,IAAG;AAAA;AAAA,CAAM;AACtD;AAEA,SAAS,SAAS,KAA4B,KAAmB;AAC/D,MAAI,MAAM,KAAKG,KAAI,SAASH,IAAG,KAAK,GAAG;AAAA,CAAI;AAC7C;AAEA,SAAS,WAAW,KAA4B,KAAmB;AACjE,MAAI,MAAM,KAAKG,KAAI,SAASH,IAAG,KAAK,GAAG;AAAA,CAAI;AAC7C;AAEA,SAAS,YAAY,KAA4B,KAAmB;AAClE,MAAI,MAAM,KAAKC,IAAG,WAAW,GAAG,GAAGD,IAAG;AAAA,CAAI;AAC5C;AAIA,eAAsB,SACpB,UAAwB,CAAC,GACH;AACtB,QAAM,MAAM,QAAQ,UAAU,QAAQ;AACtC,QAAM,QACJ,QAAQ,SAAU,QAAQ,MAAM,UAAU;AAC5C,QAAM,cAAc,SAAS,QAAQ,QAAQ;AAQ7C,MAAI,QAAQ,aAAa,QAAQ,QAAQ,eAAe,MAAM;AAC5D,QAAI;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,QAAQ,IAAI,QAAQ,IAAI,UAAU,EAAE;AAAA,EAC/C;AAEA,cAAY,GAAG;AACf,aAAW,GAAG;AAKd,QAAM,OAAO,MAAM,cAAc,QAAQ,QAAQ;AACjD,aAAW,KAAK,IAAI;AACpB,MAAI,MAAM,MAAM,IAAI;AAEpB,QAAM,cAAc,MAAM,mBAAmB;AAAA,IAC3C;AAAA,IACA;AAAA,IACA,WAAW,QAAQ;AAAA,IACnB,gBAAgB,QAAQ;AAAA,IACxB,UAAU,QAAQ;AAAA,EACpB,CAAC;AACD,MAAI,CAAC,YAAY,IAAI;AACnB,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ,YAAY,YAAY,KAAK;AAAA;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA,EACF;AACA;AAAA,IACE;AAAA,IACA,kBAAkBE,WAAU,GAAG,YAAY,QAAQ,GAAGF,IAAG,KAClD,YAAY,SAAS,kBAAkB;AAAA,EAChD;AACA,MAAI,MAAM,MAAM,IAAI;AAWpB,QAAM,QAAQ,QAAQ,gBAAgB,SACjC,QAAQ,gBAAgB,OACrB,gBAAgB,QAAQ,WAAW,IACnC,QACJ,gBAAgB,yBAAyB,CAAC;AAC9C,MAAI,OAAO;AACT,QAAI,eAAgC;AACpC,QAAI,aAAa;AACf,qBAAe,MAAM;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,iBAAiB,WAAW;AAC9B,iBAAW,KAAK,uCAAkC;AAClD,UAAI;AACF,eAAO,QAAQ,sBAAsB,oBAAoB;AACzD,iBAAS,KAAK,sDAAsD;AAAA,MACtE,SAAS,KAAc;AACrB,cAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,mBAAW,KAAK,0BAA0B,GAAG,EAAE;AAC/C,YAAI;AAAA,UACF,KAAKC,IAAG,qDAAqDD,IAAG;AAAA;AAAA,QAClE;AAAA,MACF;AAAA,IACF,OAAO;AACL;AAAA,QACE;AAAA,QACA,kBAAkBC,IAAG,gEAA2DD,IAAG;AAAA,MACrF;AAAA,IACF;AACA,QAAI,MAAM,MAAM,IAAI;AAAA,EACtB;AAGA,MAAI,aAA8B;AAClC,MAAI,QAAQ,aAAa,MAAM;AAC7B,iBAAa;AAAA,EACf,WAAW,aAAa;AACtB,iBAAa,MAAM;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,iBAAiB;AACrB,MAAI,eAAe,WAAW;AAC5B,UAAM,MAAM,MAAM,eAAe;AAAA,MAC/B,QAAQ;AAAA,MACR,cAAc,QAAQ;AAAA,MACtB,gBAAgB,QAAQ;AAAA,MACxB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AACD,QAAI,IAAI,aAAa,GAAG;AACtB,iBAAW,KAAK,oBAAoB,IAAI,OAAO,KAAK,CAAC,EAAE;AACvD,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ,IAAI;AAAA,QACZ,UAAU,IAAI;AAAA,MAChB;AAAA,IACF;AACA,qBAAiB,IAAI,OAAO,SAAS,mBAAmB,IACpD,sBAAsBC,IAAG,oBAAoBD,IAAG,KAChD;AACJ,aAAS,KAAK,cAAc;AAAA,EAC9B,OAAO;AACL,gBAAY,KAAK,mBAAmBC,IAAG,UAAUD,IAAG,EAAE;AAAA,EACxD;AACA,MAAI,MAAM,MAAM,IAAI;AAGpB,MAAI,eAAgC;AACpC,MAAI,QAAQ,eAAe,MAAM;AAC/B,mBAAe;AAAA,EACjB,WAAW,aAAa;AACtB,mBAAe,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACJ,MAAI,iBAAiB,WAAW;AAC9B,QAAI;AACF,YAAM,UAAU,MAAM,cAAc;AAAA,QAClC,WAAW,QAAQ,aAAaI,MAAK,KAAKC,SAAQ,GAAG,SAAS;AAAA,QAC9D,WAAW,QAAQ,aAAa,iBAAiB;AAAA,MACnD,CAAC;AACD,sBAAgB,QAAQ,aACpB,qBAAqB,QAAQ,aAAa,KAAK,IAAI,CAAC,MACpD,UAAUJ,IAAG,oBAAoBD,IAAG;AACxC,eAAS,KAAK,aAAa;AAAA,IAC7B,SAAS,KAAc;AACrB,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ,kCAAkC,GAAG;AAAA;AAAA,QAC7C,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF,OAAO;AACL,gBAAY,KAAK,UAAUC,IAAG,UAAUD,IAAG,EAAE;AAAA,EAC/C;AACA,MAAI,MAAM,MAAM,IAAI;AAEpB,WAAS,KAAK,GAAGG,KAAI,iBAAiBH,IAAG,EAAE;AAC3C,MAAI,MAAM,IAAI;AAOd,QAAM,oBAAoB,mBAAmB,QAAQ,IAAI,CAAC;AAC1D,iBAAe,KAAK,iBAAiB;AAErC,SAAO,EAAE,QAAQ,IAAI,QAAQ,IAAI,UAAU,EAAE;AAC/C;AAIA,eAAe,cACb,UAC2B;AAC3B,MAAI;AACF,WAAO,MAAM,gBAAgB,QAAQ;AAAA,EACvC,QAAQ;AACN,WAAO,EAAE,UAAU,MAAM;AAAA,EAC3B;AACF;AAMA,eAAe,mBAAmB,MAMT;AACvB,QAAM,SAAS,MAAM,WAAW;AAChC,MAAI,OAAiC;AACrC,MAAI,WAAW,KAAK,aAAa,OAAO,MAAM;AAC9C,MAAI,KAAK,eAAe,KAAK,cAAc,QAAW;AACpD,WAAO,MAAM,uBAAuB,EAAE,QAAQ,UAAU,KAAK,SAAS,CAAC;AAAA,EACzE;AACA,MAAI,KAAK,eAAe,KAAK,cAAc,UAAa,SAAS,MAAM;AACrE,SAAK,IAAI,MAAM,2BAA2B;AAC1C,SAAK,QAAQ,QAAQ,CAAC,QAAQ,UAAU;AACtC,YAAM,MAAM,OAAO,cAAc,iBAAiB;AAClD,YAAM,SAAS,OAAO,QAAQ,UAAU;AACxC,YAAM,SAAS,OAAO,WAAW,OAAO,cAAc,OAAO;AAC7D,WAAK,IAAI;AAAA,QACP,OAAO,QAAQ,CAAC,KAAK,OAAO,MAAM,OAAO,CAAC,CAAC,IAAI,OAAO,OAAO,CAAC,CAAC,GAAG,GAAG,KAAK,MAAM;AAAA;AAAA,MAClF;AAAA,IACF,CAAC;AACD,gBAAY,MAAM;AAAA,MAChB,KAAK;AAAA,MACL;AAAA,MACA,KAAK;AAAA,IACP,GAAG,YAAY;AACf,UAAM,SAAS,OAAO,SAAS,UAAU,EAAE;AAC3C,QACE,OAAO,UAAU,MAAM,KACvB,UAAU,KACV,UAAU,KAAK,QAAQ,QACvB;AACA,iBAAW,KAAK,QAAQ,SAAS,CAAC,GAAG,MAAM;AAAA,IAC7C;AAAA,EACF;AACA,QAAM,SAAS,oBAAoB,QAAQ;AAC3C,MAAI,OAAO,aAAa,QAAQ,CAAC,kBAAkB,OAAO,QAAQ,GAAG;AACnE,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,OACE,kBAAkB,QAAQ;AAAA,IAC9B;AAAA,EACF;AACA,QAAM,WAAW,OAAO;AACxB,MAAI,iBAAiB,MAAM,QAAQ,KAAK,CAAC,WAAW,OAAO,OAAO,QAAQ;AAC1E,MACE,KAAK,eACL,mBAAmB,UACnB,CAAC,eAAe,SAChB,eAAe,YAAY,WAAW,OAAO,MAAM,MACnD;AACA,UAAM,UAAU,eAAe,WAAW,MAAM,QAAQ,MAAM;AAC9D,UAAM,WAAW,MAAM;AAAA,MACrB,KAAK;AAAA,MACL,GAAG,eAAe,KAAK,uBAAuB,OAAO;AAAA,MACrD;AAAA,IACF;AACA,QAAI,aAAa,WAAW;AAC1B,YAAM,QAAQ,MAAM,gBAAgB,OAAO;AAC3C,UAAI,MAAM,IAAI;AACZ,eAAO,MAAM,uBAAuB,EAAE,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvE,yBAAiB,KAAK,QAAQ,KAAK,CAAC,WAAW,OAAO,OAAO,QAAQ;AAAA,MACvE,OAAO;AACL,mBAAW,KAAK,KAAK,GAAG,eAAe,KAAK,kBAAkB,MAAM,KAAK,EAAE;AAAA,MAC7E;AAAA,IACF;AAAA,EACF;AACA,QAAM,iBAAiB,KAAK,kBAAkB,OAAO;AACrD,QAAM,QAAQ,kBAAkB,MAAM,oBAAoB;AAAA,IACxD,KAAK,KAAK;AAAA,IACV,aAAa,KAAK;AAAA,IAClB;AAAA,IACA,QAAQ;AAAA,IACR,iBAAiB,OAAO,MAAM,OAAO,QAAQ,KAAK;AAAA,EACpD,CAAC;AACD,QAAM,YAAY;AAAA,IAChB,GAAG;AAAA,IACH,OAAO;AAAA,MACL,GAAG,OAAO;AAAA,MACV,SAAS;AAAA,MACT,QAAQ;AAAA,QACN,GAAG,OAAO,MAAM;AAAA,QAChB,CAAC,QAAQ,GAAG;AAAA,MACd;AAAA,IACF;AAAA,EACF,CAAC;AACD,MAAI,CAAC,KAAK,eAAe,KAAK,cAAc,QAAW;AACrD,UAAM,SAAS,gBAAgB,UAAU,OACrC,UACA,gBAAgB,cAAc,gBAAgB,UAAU;AAC5D,aAAS,KAAK,KAAK,oBAAoB,MAAM,EAAE;AAAA,EACjD;AACA,SAAO,EAAE,IAAI,MAAM,UAAU,MAAM;AACrC;AAEA,eAAe,oBAAoB,MAMR;AACzB,QAAM,UACJ,KAAK,QAAQ,gBACb,0BAA0B,KAAK,UAAU,KAAK,eAAe;AAC/D,QAAM,cACJ,QAAQ,KAAK,CAAC,WAAW,OAAO,WAAW,KAC3C,QAAQ,KAAK,CAAC,WAAW,OAAO,WAAW,kBAAkB;AAC/D,MAAI,CAAC,KAAK,aAAa;AACrB,WAAO,KAAK,mBAAmB,aAAa,SAAS;AAAA,EACvD;AAEA,OAAK,IAAI,MAAM,YAAY,KAAK,QAAQ;AAAA,CAAW;AACnD,UAAQ,QAAQ,CAAC,QAAQ,UAAU;AACjC,UAAM,SAAS,OAAO,cAAc,iBAAiB;AACrD,UAAM,UAAU,OAAO,UAAU,KAAK,kBAAkB,aAAa;AACrE,SAAK,IAAI;AAAA,MACP,OAAO,QAAQ,CAAC,KAAK,OAAO,KAAK,GAAG,MAAM,GAAG,OAAO;AAAA;AAAA,IACtD;AAAA,EACF,CAAC;AACD,QAAM,eAAe,QAAQ;AAAA,IAAU,CAAC,WACtC,OAAO,UAAU,KAAK;AAAA,EACxB;AACA,QAAM,mBAAmB,QAAQ,UAAU,CAAC,WAAW,OAAO,WAAW;AACzE,QAAM,eACJ,gBAAgB,IACZ,eAAe,IACf,oBAAoB,IAClB,mBAAmB,IACnB;AACR,QAAM,WAAW,MAAM,WAAW,KAAK,KAAK,SAAS,OAAO,YAAY,CAAC;AACzE,QAAM,SAAS,OAAO,SAAS,UAAU,EAAE;AAC3C,MAAI;AACJ,MACE,OAAO,UAAU,MAAM,KACvB,UAAU,KACV,UAAU,QAAQ,QAClB;AACA,kBAAc,QAAQ,SAAS,CAAC;AAAA,EAClC,OAAO;AACL,kBAAc,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,QAAQ;AAAA,EAClE;AACA,MAAI,aAAa,WAAW,UAAU;AACpC,UAAM,SAAS,MAAM,WAAW,KAAK,KAAK,mBAAmB,EAAE;AAC/D,WAAO,OAAO,SAAS,IAAI,SAAS,aAAa,SAAS;AAAA,EAC5D;AACA,SAAO,aAAa,SAAS,aAAa,SAAS;AACrD;AAEA,eAAe,gBAAgB,SAG7B;AACA,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,MAAM,SAAS;AAAA,MAC3B,OAAO;AAAA,MACP,OAAO;AAAA,IACT,CAAC;AACD,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,cAAQ,EAAE,IAAI,OAAO,OAAO,IAAI,QAAQ,CAAC;AAAA,IAC3C,CAAC;AACD,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,UAAI,SAAS,GAAG;AACd,gBAAQ,EAAE,IAAI,KAAK,CAAC;AACpB;AAAA,MACF;AACA,cAAQ,EAAE,IAAI,OAAO,OAAO,UAAU,QAAQ,CAAC,GAAG,CAAC;AAAA,IACrD,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,WACP,KACA,MACM;AACN,MAAI,KAAK,UAAU;AACjB,UAAM,MAAM,KAAK,SAAS;AAC1B,UAAM,OACJ,KAAK,qBAAqB,SACtB,IAAIC,IAAG,IAAI,KAAK,gBAAgB,IAAID,IAAG,KACvC;AACN,aAAS,KAAK,gBAAgBE,WAAU,GAAG,GAAG,GAAGF,IAAG,GAAG,IAAI,EAAE;AAC7D;AAAA,EACF;AACA,MACE,QAAQ,IAAI,sBAAsB,UAClC,QAAQ,IAAI,kBAAkB,SAAS,GACvC;AACA,aAAS,KAAK,gBAAgBE,WAAU,oBAAoBF,IAAG,MAAM;AACrE;AAAA,EACF;AAEA,aAAW,KAAK,gBAAgBC,IAAG,gBAAgBD,IAAG,EAAE;AACxD,aAAW,QAAQ,wBAAwB,MAAM,IAAI,GAAG;AACtD,QAAI,MAAM,KAAKC,IAAG,YAAY,IAAI,GAAGD,IAAG;AAAA,CAAI;AAAA,EAC9C;AACF;AA6BA,eAAe,cACb,SAC8B;AAC9B,QAAM,MAAM,QAAQ,WAAW,EAAE,WAAW,KAAK,CAAC;AAElD,QAAM,UAAUI,MAAK,KAAK,QAAQ,WAAW,SAAS;AACtD,QAAM,SAASA,MAAK,KAAK,QAAQ,WAAW,cAAc;AAC1D,MAAI,CAACE,YAAW,OAAO,GAAG;AACxB,UAAM,IAAI,MAAM,0BAA0B,OAAO,EAAE;AAAA,EACrD;AACA,MAAI,CAACA,YAAW,MAAM,GAAG;AACvB,UAAM,IAAI,MAAM,0BAA0B,MAAM,EAAE;AAAA,EACpD;AAEA,QAAM,WAAWF,MAAK,KAAK,QAAQ,WAAW,gBAAgB;AAC9D,QAAM,UAAUA,MAAK,KAAK,QAAQ,WAAW,0BAA0B;AAEvE,QAAM,cAAc,MAAM,cAAc,SAAS,QAAQ;AACzD,QAAM,aAAa,MAAM,cAAc,QAAQ,OAAO;AAEtD,QAAM,WAAWA,MAAK,KAAK,QAAQ,WAAW,WAAW;AACzD,QAAM,gBAAgB,MAAM,aAAa,QAAQ;AAEjD,QAAM,eAAyB,CAAC;AAChC,MAAI,YAAa,cAAa,KAAK,gBAAgB;AACnD,MAAI,WAAY,cAAa,KAAK,0BAA0B;AAC5D,MAAI,cAAe,cAAa,KAAK,WAAW;AAEhD,SAAO,EAAE,YAAY,aAAa,SAAS,GAAG,aAAa;AAC7D;AAEA,eAAe,cAAc,KAAa,MAAgC;AACxE,QAAM,WAAW,MAAM,SAAS,GAAG;AACnC,MAAIE,YAAW,IAAI,GAAG;AACpB,QAAI;AACF,YAAM,YAAY,MAAM,SAAS,IAAI;AACrC,UAAI,SAAS,OAAO,SAAS,EAAG,QAAO;AAAA,IACzC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,SAAS,KAAK,IAAI;AACxB,SAAO;AACT;AAIO,IAAM,cAAc;AAa3B,eAAe,aAAa,cAAwC;AAClE,MAAI,WAAW;AACf,MAAIA,YAAW,YAAY,GAAG;AAC5B,eAAW,MAAM,SAAS,cAAc,MAAM;AAAA,EAChD;AACA,MAAI,cAAc,QAAQ,EAAG,QAAO;AAEpC,QAAM,MACJ,SAAS,WAAW,IAAI,KAAK,SAAS,SAAS,IAAI,IAAI,OAAO;AAChE,QAAM,OAAO,GAAG,QAAQ,GAAG,GAAG,GAAG,WAAW;AAAA;AAC5C,QAAM,UAAU,cAAc,MAAM,MAAM;AAC1C,SAAO;AACT;AAEO,SAAS,cAAc,UAA2B;AAMvD,QAAM,QAAQ,SAAS,MAAM,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACzD,SAAO,MAAM,KAAK,CAAC,SAAS;AAC1B,QAAI,SAAS,YAAa,QAAO;AACjC,QAAI,CAAC,KAAK,WAAW,WAAW,EAAG,QAAO;AAC1C,UAAM,OAAO,KAAK,YAAY,MAAM;AACpC,WAAO,SAAS,OAAO,SAAS;AAAA,EAClC,CAAC;AACH;AAYA,SAAS,QACP,KACA,UACA,YAC0B;AAC1B,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,OAAO,aAAa,UAAU;AACpC,QAAI,MAAM,KAAKH,KAAI,SAASH,IAAG,KAAK,QAAQ,IAAIC,IAAG,GAAG,IAAI,GAAGD,IAAG,GAAG;AAEnE,QAAI,MAAM;AACV,UAAM,SAAS,CAAC,UAAwB;AACtC,aAAO,MAAM,SAAS,MAAM;AAC5B,YAAM,KAAK,IAAI,QAAQ,IAAI;AAC3B,UAAI,OAAO,GAAI;AACf,cAAQ,MAAM,eAAe,QAAQ,MAAM;AAC3C,cAAQ,MAAM,MAAM;AAEpB,YAAM,SAAS,IAAI,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,YAAY;AACnD,YAAM,WACJ,OAAO,WAAW,IACd,aACA,WAAW,OAAO,WAAW;AACnC,cAAQ,WAAW,YAAY,MAAM;AAAA,IACvC;AAEA,YAAQ,MAAM,OAAO;AACrB,YAAQ,MAAM,GAAG,QAAQ,MAAM;AAAA,EACjC,CAAC;AACH;AAEA,SAAS,WACP,KACA,UACA,cACiB;AACjB,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI;AAAA,MACF,KAAKG,KAAI,SAASH,IAAG,KAAK,QAAQ,IAAIC,IAAG,IAAI,YAAY,IAAID,IAAG;AAAA,IAClE;AAEA,QAAI,MAAM;AACV,UAAM,SAAS,CAAC,UAAwB;AACtC,aAAO,MAAM,SAAS,MAAM;AAC5B,YAAM,KAAK,IAAI,QAAQ,IAAI;AAC3B,UAAI,OAAO,GAAI;AACf,cAAQ,MAAM,eAAe,QAAQ,MAAM;AAC3C,cAAQ,MAAM,MAAM;AAEpB,YAAM,SAAS,IAAI,MAAM,GAAG,EAAE,EAAE,KAAK;AACrC,cAAQ,OAAO,WAAW,IAAI,eAAe,MAAM;AAAA,IACrD;AAEA,YAAQ,MAAM,OAAO;AACrB,YAAQ,MAAM,GAAG,QAAQ,MAAM;AAAA,EACjC,CAAC;AACH;AAoBO,SAAS,mBAA2B;AACzC,QAAM,OAAOI,MAAK,QAAQG,eAAc,YAAY,GAAG,CAAC;AACxD,QAAM,aAAa;AAAA,IACjBH,MAAK,QAAQ,MAAM,MAAM,QAAQ;AAAA;AAAA,IACjCA,MAAK,QAAQ,MAAM,MAAM,MAAM,QAAQ;AAAA;AAAA,IACvCA,MAAK,QAAQ,MAAM,MAAM,MAAM,MAAM,QAAQ;AAAA,EAC/C;AACA,aAAW,OAAO,YAAY;AAC5B,QAAI,mBAAmB,GAAG,EAAG,QAAO;AAAA,EACtC;AAIA,MAAI;AACF,UAAMI,WAAUC,eAAc,YAAY,GAAG;AAC7C,UAAM,UAAUD,SAAQ,QAAQ,0BAA0B;AAC1D,UAAM,SAASJ,MAAK,KAAKA,MAAK,QAAQ,OAAO,GAAG,QAAQ;AACxD,QAAI,mBAAmB,MAAM,EAAG,QAAO;AAAA,EACzC,QAAQ;AAAA,EAER;AACA,QAAM,IAAI;AAAA,IACR,yDACE,WAAW,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI;AAAA,EAC/C;AACF;AAEA,SAAS,mBAAmB,KAAsB;AAChD,SAAOE,YAAWF,MAAK,KAAK,KAAK,SAAS,CAAC;AAC7C;","names":["existsSync","createRequire","homedir","path","fileURLToPath","path","RST","DIM","WHITE_BOLD","BLUE","path","homedir","existsSync","fileURLToPath","require","createRequire"]}
|
|
@@ -4,8 +4,10 @@ import {
|
|
|
4
4
|
getStatePath
|
|
5
5
|
} from "./chunk-F53U6JQG.js";
|
|
6
6
|
import {
|
|
7
|
-
getConfigPath
|
|
8
|
-
|
|
7
|
+
getConfigPath,
|
|
8
|
+
getLegacyConfigPath,
|
|
9
|
+
parseConfigText
|
|
10
|
+
} from "./chunk-P5WGG4FJ.js";
|
|
9
11
|
|
|
10
12
|
// src/update/schedule.ts
|
|
11
13
|
import { spawn } from "child_process";
|
|
@@ -41,13 +43,17 @@ function shouldSchedule(argv) {
|
|
|
41
43
|
return true;
|
|
42
44
|
}
|
|
43
45
|
function notifierEnabled() {
|
|
46
|
+
const parsed = readConfigSync(getConfigPath());
|
|
47
|
+
if (parsed !== null) return parsed.update_notifier !== false;
|
|
48
|
+
const legacy = readConfigSync(getLegacyConfigPath());
|
|
49
|
+
return legacy?.update_notifier !== false;
|
|
50
|
+
}
|
|
51
|
+
function readConfigSync(path) {
|
|
44
52
|
try {
|
|
45
|
-
const raw = readFileSync(
|
|
46
|
-
|
|
47
|
-
if (parsed.update_notifier === false) return false;
|
|
48
|
-
return true;
|
|
53
|
+
const raw = readFileSync(path, "utf8");
|
|
54
|
+
return parseConfigText(raw, path);
|
|
49
55
|
} catch {
|
|
50
|
-
return
|
|
56
|
+
return null;
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
async function runInternalUpdateCheck() {
|
|
@@ -80,4 +86,4 @@ export {
|
|
|
80
86
|
runInternalUpdateCheck,
|
|
81
87
|
readStateForDoctor
|
|
82
88
|
};
|
|
83
|
-
//# sourceMappingURL=chunk-
|
|
89
|
+
//# sourceMappingURL=chunk-TILAKDN6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/update/schedule.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\n\nimport { checkForUpdate } from \"./check.js\";\nimport {\n getConfigPath,\n getLegacyConfigPath,\n parseConfigText,\n} from \"./config.js\";\nimport { getStatePath, type UpdateState } from \"./state.js\";\n\n/**\n * Post-command scheduler for the background update check.\n *\n * After any normal `almanac <command>` exits, we want a fresh check to\n * have happened by the next invocation. We achieve that by spawning a\n * detached copy of ourselves with the hidden `--internal-check-updates`\n * flag; that child does nothing but hit the registry and write\n * `~/.almanac/update-state.json`, then exits.\n *\n * Why detach rather than check inline:\n * - 3s network timeout in the foreground would feel sluggish on every\n * command.\n * - `npm test` and CI scripts shouldn't pay for a registry round-trip\n * (gated below via env).\n * - A detached child with `stdio: \"ignore\"` cannot leak output into\n * the parent's stdout/stderr — critical for pipelines.\n *\n * Hazards we accept:\n * - A Claude Code subprocess whose parent shell exits right after the\n * `almanac` call may kill the child before it finishes. That's\n * fine: a failed check just means we try again next invocation.\n * - Detached child survival on Windows isn't as robust as on Unix.\n * Same fallback: next invocation retries.\n */\n\nexport function scheduleBackgroundUpdateCheck(argv: string[]): void {\n if (!shouldSchedule(argv)) return;\n\n const scriptPath = argv[1];\n const nodeBin = process.execPath;\n if (scriptPath === undefined || scriptPath.length === 0) return;\n\n // Spawn with the current Node and the same script path. `detached:\n // true` + `stdio: \"ignore\"` + `unref()` detaches the child from our\n // event loop so the parent can exit independently.\n try {\n const child = spawn(\n nodeBin,\n [scriptPath, \"--internal-check-updates\"],\n {\n detached: true,\n stdio: \"ignore\",\n // Windows: with `detached: true` and no `stdio`, Node opens a\n // console window — `\"ignore\"` prevents that.\n },\n );\n child.unref();\n // Swallow any synchronous spawn errors (e.g. ENOENT in strange\n // installs) — never propagate to the foreground command.\n child.on(\"error\", () => {});\n } catch {\n // Last-resort swallow: background checks are best-effort.\n }\n}\n\n/**\n * Should we spawn the worker at all?\n *\n * - Respect the `update_notifier` config — no banner means no need\n * for the data that feeds it.\n * - Skip in test environments so `npm test` doesn't fork 300 copies\n * of itself into the background and hammer the registry.\n * - Skip on the worker invocation itself (prevents a fork bomb).\n * - Skip when the user doesn't own the install path (permission\n * weirdness) — detected by `~/.almanac` mkdir failing; simplest\n * to just rely on the worker's own error handling, so we don't\n * gate here.\n * - Skip when the argv contains `--help`/`--version`/nothing — these\n * commands are often run from scripts that care about clean exit;\n * though the inline banner still shows, we don't kick off a check.\n */\nfunction shouldSchedule(argv: string[]): boolean {\n if (process.env.CODEALMANAC_SKIP_UPDATE_CHECK === \"1\") return false;\n if (process.env.NODE_ENV === \"test\") return false;\n if (process.env.VITEST !== undefined) return false;\n\n // Already the worker. argv[2..] contains the internal flag.\n if (argv.slice(2).includes(\"--internal-check-updates\")) return false;\n\n if (!notifierEnabled()) return false;\n\n return true;\n}\n\nfunction notifierEnabled(): boolean {\n const parsed = readConfigSync(getConfigPath());\n if (parsed !== null) return parsed.update_notifier !== false;\n const legacy = readConfigSync(getLegacyConfigPath());\n return legacy?.update_notifier !== false;\n}\n\nfunction readConfigSync(path: string): { update_notifier?: unknown } | null {\n try {\n const raw = readFileSync(path, \"utf8\");\n return parseConfigText(raw, path) as { update_notifier?: unknown };\n } catch {\n return null;\n }\n}\n\n/**\n * The worker body. Invoked when `--internal-check-updates` appears on\n * the argv. Must be fast and must never print: the parent spawned us\n * with `stdio: \"ignore\"` but a stray write could still surprise a\n * downstream debugger.\n *\n * We take a simple file lock at `~/.almanac/.update-check.lock` to\n * prevent two workers running at the same time (which could happen if\n * the user fires several commands in parallel). The lock is just the\n * existence of the file with our PID inside; if an existing lock is\n * stale (older than the 3s + cache-write budget), we steal it.\n */\nexport async function runInternalUpdateCheck(): Promise<void> {\n // The worker is intentionally minimal. Any error (network, fs,\n // JSON) is handled inside `checkForUpdate` and surfaces as a\n // swallowed return; we just need to await it and exit.\n try {\n await checkForUpdate({});\n } catch {\n // Defense-in-depth: nothing must escape the worker.\n }\n}\n\n/**\n * Read the current state snapshot for diagnostic surfaces (doctor, the\n * `update --check` command). Wraps the sync read so callers can grab\n * state without the `async readState` ceremony.\n */\nexport function readStateForDoctor(path?: string): UpdateState | null {\n const file = path ?? getStatePath();\n try {\n const raw = readFileSync(file, \"utf8\");\n const trimmed = raw.trim();\n if (trimmed.length === 0) return null;\n const parsed = JSON.parse(trimmed) as Partial<UpdateState>;\n return {\n last_check_at:\n typeof parsed.last_check_at === \"number\" ? parsed.last_check_at : 0,\n installed_version:\n typeof parsed.installed_version === \"string\"\n ? parsed.installed_version\n : \"\",\n latest_version:\n typeof parsed.latest_version === \"string\" ? parsed.latest_version : \"\",\n dismissed_versions: Array.isArray(parsed.dismissed_versions)\n ? parsed.dismissed_versions.filter((v): v is string => typeof v === \"string\")\n : [],\n last_fetch_failed_at:\n typeof parsed.last_fetch_failed_at === \"number\"\n ? parsed.last_fetch_failed_at\n : undefined,\n };\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,aAAa;AACtB,SAAS,oBAAoB;AAmCtB,SAAS,8BAA8B,MAAsB;AAClE,MAAI,CAAC,eAAe,IAAI,EAAG;AAE3B,QAAM,aAAa,KAAK,CAAC;AACzB,QAAM,UAAU,QAAQ;AACxB,MAAI,eAAe,UAAa,WAAW,WAAW,EAAG;AAKzD,MAAI;AACF,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,CAAC,YAAY,0BAA0B;AAAA,MACvC;AAAA,QACE,UAAU;AAAA,QACV,OAAO;AAAA;AAAA;AAAA,MAGT;AAAA,IACF;AACA,UAAM,MAAM;AAGZ,UAAM,GAAG,SAAS,MAAM;AAAA,IAAC,CAAC;AAAA,EAC5B,QAAQ;AAAA,EAER;AACF;AAkBA,SAAS,eAAe,MAAyB;AAC/C,MAAI,QAAQ,IAAI,kCAAkC,IAAK,QAAO;AAC9D,MAAI,QAAQ,IAAI,aAAa,OAAQ,QAAO;AAC5C,MAAI,QAAQ,IAAI,WAAW,OAAW,QAAO;AAG7C,MAAI,KAAK,MAAM,CAAC,EAAE,SAAS,0BAA0B,EAAG,QAAO;AAE/D,MAAI,CAAC,gBAAgB,EAAG,QAAO;AAE/B,SAAO;AACT;AAEA,SAAS,kBAA2B;AAClC,QAAM,SAAS,eAAe,cAAc,CAAC;AAC7C,MAAI,WAAW,KAAM,QAAO,OAAO,oBAAoB;AACvD,QAAM,SAAS,eAAe,oBAAoB,CAAC;AACnD,SAAO,QAAQ,oBAAoB;AACrC;AAEA,SAAS,eAAe,MAAoD;AAC1E,MAAI;AACF,UAAM,MAAM,aAAa,MAAM,MAAM;AACrC,WAAO,gBAAgB,KAAK,IAAI;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAcA,eAAsB,yBAAwC;AAI5D,MAAI;AACF,UAAM,eAAe,CAAC,CAAC;AAAA,EACzB,QAAQ;AAAA,EAER;AACF;AAOO,SAAS,mBAAmB,MAAmC;AACpE,QAAM,OAAO,QAAQ,aAAa;AAClC,MAAI;AACF,UAAM,MAAM,aAAa,MAAM,MAAM;AACrC,UAAM,UAAU,IAAI,KAAK;AACzB,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,WAAO;AAAA,MACL,eACE,OAAO,OAAO,kBAAkB,WAAW,OAAO,gBAAgB;AAAA,MACpE,mBACE,OAAO,OAAO,sBAAsB,WAChC,OAAO,oBACP;AAAA,MACN,gBACE,OAAO,OAAO,mBAAmB,WAAW,OAAO,iBAAiB;AAAA,MACtE,oBAAoB,MAAM,QAAQ,OAAO,kBAAkB,IACvD,OAAO,mBAAmB,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,IAC1E,CAAC;AAAA,MACL,sBACE,OAAO,OAAO,yBAAyB,WACnC,OAAO,uBACP;AAAA,IACR;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
AGENT_PROVIDER_IDS,
|
|
4
|
+
getConfigPath,
|
|
5
|
+
getProjectConfigPath,
|
|
6
|
+
isAgentProviderId,
|
|
7
|
+
parseConfigText,
|
|
8
|
+
readConfig,
|
|
9
|
+
readConfigWithOrigins,
|
|
10
|
+
serializeConfig
|
|
11
|
+
} from "./chunk-P5WGG4FJ.js";
|
|
12
|
+
|
|
13
|
+
// src/commands/config.ts
|
|
14
|
+
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
15
|
+
import { dirname } from "path";
|
|
16
|
+
|
|
17
|
+
// src/commands/config-keys.ts
|
|
18
|
+
var CONFIG_KEYS = [
|
|
19
|
+
"update_notifier",
|
|
20
|
+
"agent.default",
|
|
21
|
+
...AGENT_PROVIDER_IDS.map((id) => `agent.models.${id}`)
|
|
22
|
+
];
|
|
23
|
+
function parseConfigKey(raw) {
|
|
24
|
+
if (raw === "update_notifier" || raw === "agent.default") return raw;
|
|
25
|
+
const prefix = "agent.models.";
|
|
26
|
+
if (!raw.startsWith(prefix)) return null;
|
|
27
|
+
const provider = raw.slice(prefix.length);
|
|
28
|
+
if (!isAgentProviderId(provider)) return null;
|
|
29
|
+
return `agent.models.${provider}`;
|
|
30
|
+
}
|
|
31
|
+
function getConfigValue(config, key) {
|
|
32
|
+
if (key === "update_notifier") return config.update_notifier;
|
|
33
|
+
if (key === "agent.default") return config.agent.default;
|
|
34
|
+
const provider = providerFromModelKey(key);
|
|
35
|
+
return config.agent.models[provider] ?? null;
|
|
36
|
+
}
|
|
37
|
+
function setConfigValue(config, key, rawValue) {
|
|
38
|
+
if (key === "update_notifier") {
|
|
39
|
+
return {
|
|
40
|
+
...config,
|
|
41
|
+
update_notifier: parseBoolean(rawValue)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (key === "agent.default") {
|
|
45
|
+
if (rawValue === null || !isAgentProviderId(rawValue)) {
|
|
46
|
+
throw new Error("agent.default must be one of: claude, codex, cursor");
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
...config,
|
|
50
|
+
agent: {
|
|
51
|
+
...config.agent,
|
|
52
|
+
default: rawValue
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const provider = providerFromModelKey(key);
|
|
57
|
+
const model = normalizeModel(rawValue);
|
|
58
|
+
return {
|
|
59
|
+
...config,
|
|
60
|
+
agent: {
|
|
61
|
+
...config.agent,
|
|
62
|
+
models: {
|
|
63
|
+
...config.agent.models,
|
|
64
|
+
[provider]: model
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function configEntries(config) {
|
|
70
|
+
return CONFIG_KEYS.map((key) => ({
|
|
71
|
+
key,
|
|
72
|
+
value: getConfigValue(config, key)
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
function formatConfigValue(value) {
|
|
76
|
+
if (value === null) return "default";
|
|
77
|
+
return String(value);
|
|
78
|
+
}
|
|
79
|
+
function providerFromModelKey(key) {
|
|
80
|
+
const provider = key.slice("agent.models.".length);
|
|
81
|
+
if (!isAgentProviderId(provider)) {
|
|
82
|
+
throw new Error(`not a model key: ${key}`);
|
|
83
|
+
}
|
|
84
|
+
return provider;
|
|
85
|
+
}
|
|
86
|
+
function parseBoolean(value) {
|
|
87
|
+
if (value === "true") return true;
|
|
88
|
+
if (value === "false") return false;
|
|
89
|
+
throw new Error("update_notifier must be true or false");
|
|
90
|
+
}
|
|
91
|
+
function normalizeModel(value) {
|
|
92
|
+
if (value === null) return null;
|
|
93
|
+
if (value === "default" || value === "null") return null;
|
|
94
|
+
if (value.length === 0) {
|
|
95
|
+
throw new Error("model must be non-empty, default, or null");
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/commands/config.ts
|
|
101
|
+
async function runConfigList(opts = {}) {
|
|
102
|
+
const { config, origins } = await readConfigWithOrigins({ cwd: process.cwd() });
|
|
103
|
+
const rows = configEntries(config).map((entry) => ({
|
|
104
|
+
...entry,
|
|
105
|
+
origin: origins[entry.key] ?? "default"
|
|
106
|
+
}));
|
|
107
|
+
if (opts.json === true) {
|
|
108
|
+
return ok(`${JSON.stringify(rows, null, 2)}
|
|
109
|
+
`);
|
|
110
|
+
}
|
|
111
|
+
const lines = rows.map((row) => {
|
|
112
|
+
const value = formatConfigValue(row.value);
|
|
113
|
+
return opts.showOrigin === true ? `${row.key.padEnd(20)} ${value.padEnd(24)} ${row.origin}` : `${row.key.padEnd(20)} ${value}`;
|
|
114
|
+
});
|
|
115
|
+
return ok(`${lines.join("\n")}
|
|
116
|
+
`);
|
|
117
|
+
}
|
|
118
|
+
async function runConfigGet(opts) {
|
|
119
|
+
const key = parseConfigKey(opts.key);
|
|
120
|
+
if (key === null) return unknownKey(opts.key);
|
|
121
|
+
const { config, origins } = await readConfigWithOrigins({ cwd: process.cwd() });
|
|
122
|
+
const value = getConfigValue(config, key);
|
|
123
|
+
const origin = origins[key] ?? "default";
|
|
124
|
+
if (opts.json === true) {
|
|
125
|
+
return ok(`${JSON.stringify({ key, value, origin }, null, 2)}
|
|
126
|
+
`);
|
|
127
|
+
}
|
|
128
|
+
const rendered = formatConfigValue(value);
|
|
129
|
+
return ok(
|
|
130
|
+
opts.showOrigin === true ? `${key}=${rendered} (${origin})
|
|
131
|
+
` : `${rendered}
|
|
132
|
+
`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
async function runConfigSet(opts) {
|
|
136
|
+
const key = parseConfigKey(opts.key);
|
|
137
|
+
if (key === null) return unknownKey(opts.key);
|
|
138
|
+
if (opts.project === true && key === "update_notifier") {
|
|
139
|
+
return {
|
|
140
|
+
stdout: "",
|
|
141
|
+
stderr: "almanac: update_notifier is user-level only.\n",
|
|
142
|
+
exitCode: 1
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (opts.value === void 0) {
|
|
146
|
+
return {
|
|
147
|
+
stdout: "",
|
|
148
|
+
stderr: `almanac: missing value for ${key}.
|
|
149
|
+
`,
|
|
150
|
+
exitCode: 1
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const file = targetConfigPath(opts.project === true);
|
|
155
|
+
if (file === null) {
|
|
156
|
+
return {
|
|
157
|
+
stdout: "",
|
|
158
|
+
stderr: "almanac: no .almanac/ found for project config.\n",
|
|
159
|
+
exitCode: 1
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const next = setConfigValue(await readConfig({ cwd: process.cwd() }), key, opts.value);
|
|
163
|
+
const raw = ensureRawObject(await readRawConfig(file));
|
|
164
|
+
setRawConfigValue(raw, key, getConfigValue(next, key));
|
|
165
|
+
await writeRawConfig(raw, file);
|
|
166
|
+
return ok(
|
|
167
|
+
`codealmanac: set ${key}=${formatConfigValue(getConfigValue(next, key))}${opts.project === true ? " in project config" : ""}.
|
|
168
|
+
`
|
|
169
|
+
);
|
|
170
|
+
} catch (err) {
|
|
171
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
172
|
+
return { stdout: "", stderr: `almanac: ${msg}
|
|
173
|
+
`, exitCode: 1 };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function runConfigUnset(opts) {
|
|
177
|
+
const key = parseConfigKey(opts.key);
|
|
178
|
+
if (key === null) return unknownKey(opts.key);
|
|
179
|
+
if (opts.project === true && key === "update_notifier") {
|
|
180
|
+
return {
|
|
181
|
+
stdout: "",
|
|
182
|
+
stderr: "almanac: update_notifier is user-level only.\n",
|
|
183
|
+
exitCode: 1
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const file = targetConfigPath(opts.project === true);
|
|
187
|
+
if (file === null) {
|
|
188
|
+
return {
|
|
189
|
+
stdout: "",
|
|
190
|
+
stderr: "almanac: no .almanac/ found for project config.\n",
|
|
191
|
+
exitCode: 1
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const raw = ensureRawObject(await readRawConfig(file));
|
|
195
|
+
deleteRawConfigValue(raw, key);
|
|
196
|
+
await writeRawConfig(raw, file);
|
|
197
|
+
return ok(
|
|
198
|
+
`codealmanac: unset ${key}${opts.project === true ? " in project config" : ""}.
|
|
199
|
+
`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
function unknownKey(key) {
|
|
203
|
+
return {
|
|
204
|
+
stdout: "",
|
|
205
|
+
stderr: `almanac: unknown config key '${key}'. Expected one of: ${CONFIG_KEYS.join(", ")}.
|
|
206
|
+
`,
|
|
207
|
+
exitCode: 1
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function ok(stdout) {
|
|
211
|
+
return { stdout, stderr: "", exitCode: 0 };
|
|
212
|
+
}
|
|
213
|
+
function targetConfigPath(project) {
|
|
214
|
+
return project ? getProjectConfigPath(process.cwd()) : getConfigPath();
|
|
215
|
+
}
|
|
216
|
+
async function readRawConfig(file = getConfigPath()) {
|
|
217
|
+
try {
|
|
218
|
+
return parseConfigText(await readFile(file, "utf8"), file);
|
|
219
|
+
} catch {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function writeRawConfig(raw, file = getConfigPath()) {
|
|
224
|
+
await mkdir(dirname(file), { recursive: true });
|
|
225
|
+
const tmp = `${file}.tmp`;
|
|
226
|
+
await writeFile(tmp, serializeConfig(raw, file), "utf8");
|
|
227
|
+
await rename(tmp, file);
|
|
228
|
+
}
|
|
229
|
+
function ensureRawObject(raw) {
|
|
230
|
+
if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
|
|
231
|
+
return raw;
|
|
232
|
+
}
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
function setRawConfigValue(raw, key, value) {
|
|
236
|
+
const parts = key.split(".");
|
|
237
|
+
let cursor = raw;
|
|
238
|
+
for (const part of parts.slice(0, -1)) {
|
|
239
|
+
const next = cursor[part];
|
|
240
|
+
if (next === null || typeof next !== "object" || Array.isArray(next)) {
|
|
241
|
+
cursor[part] = {};
|
|
242
|
+
}
|
|
243
|
+
cursor = cursor[part];
|
|
244
|
+
}
|
|
245
|
+
const leaf = parts[parts.length - 1];
|
|
246
|
+
if (leaf !== void 0) cursor[leaf] = value;
|
|
247
|
+
}
|
|
248
|
+
function deleteRawConfigValue(raw, key) {
|
|
249
|
+
const parts = key.split(".");
|
|
250
|
+
const parents = [];
|
|
251
|
+
let cursor = raw;
|
|
252
|
+
for (const part of parts.slice(0, -1)) {
|
|
253
|
+
if (cursor === null || typeof cursor !== "object" || Array.isArray(cursor)) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const object = cursor;
|
|
257
|
+
parents.push({ object, key: part });
|
|
258
|
+
cursor = object[part];
|
|
259
|
+
}
|
|
260
|
+
if (cursor === null || typeof cursor !== "object" || Array.isArray(cursor)) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const leaf = parts[parts.length - 1];
|
|
264
|
+
if (leaf === void 0) return;
|
|
265
|
+
delete cursor[leaf];
|
|
266
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
267
|
+
const parent = parents[i];
|
|
268
|
+
if (parent === void 0) continue;
|
|
269
|
+
const value = parent.object[parent.key];
|
|
270
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0) {
|
|
271
|
+
delete parent.object[parent.key];
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export {
|
|
277
|
+
runConfigList,
|
|
278
|
+
runConfigGet,
|
|
279
|
+
runConfigSet,
|
|
280
|
+
runConfigUnset
|
|
281
|
+
};
|
|
282
|
+
//# sourceMappingURL=chunk-TT6ZP4GS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/config.ts","../src/commands/config-keys.ts"],"sourcesContent":["import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport {\n CONFIG_KEYS,\n configEntries,\n formatConfigValue,\n getConfigValue,\n parseConfigKey,\n setConfigValue,\n type ConfigKey,\n} from \"./config-keys.js\";\nimport {\n getConfigPath,\n getProjectConfigPath,\n parseConfigText,\n readConfig,\n readConfigWithOrigins,\n serializeConfig,\n} from \"../update/config.js\";\n\nexport interface ConfigResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\nexport async function runConfigList(opts: {\n json?: boolean;\n showOrigin?: boolean;\n} = {}): Promise<ConfigResult> {\n const { config, origins } = await readConfigWithOrigins({ cwd: process.cwd() });\n const rows = configEntries(config).map((entry) => ({\n ...entry,\n origin: origins[entry.key] ?? \"default\",\n }));\n if (opts.json === true) {\n return ok(`${JSON.stringify(rows, null, 2)}\\n`);\n }\n const lines = rows.map((row) => {\n const value = formatConfigValue(row.value);\n return opts.showOrigin === true\n ? `${row.key.padEnd(20)} ${value.padEnd(24)} ${row.origin}`\n : `${row.key.padEnd(20)} ${value}`;\n });\n return ok(`${lines.join(\"\\n\")}\\n`);\n}\n\nexport async function runConfigGet(opts: {\n key: string;\n json?: boolean;\n showOrigin?: boolean;\n}): Promise<ConfigResult> {\n const key = parseConfigKey(opts.key);\n if (key === null) return unknownKey(opts.key);\n const { config, origins } = await readConfigWithOrigins({ cwd: process.cwd() });\n const value = getConfigValue(config, key);\n const origin = origins[key] ?? \"default\";\n if (opts.json === true) {\n return ok(`${JSON.stringify({ key, value, origin }, null, 2)}\\n`);\n }\n const rendered = formatConfigValue(value);\n return ok(\n opts.showOrigin === true\n ? `${key}=${rendered} (${origin})\\n`\n : `${rendered}\\n`,\n );\n}\n\nexport async function runConfigSet(opts: {\n key: string;\n value?: string;\n project?: boolean;\n}): Promise<ConfigResult> {\n const key = parseConfigKey(opts.key);\n if (key === null) return unknownKey(opts.key);\n if (opts.project === true && key === \"update_notifier\") {\n return {\n stdout: \"\",\n stderr: \"almanac: update_notifier is user-level only.\\n\",\n exitCode: 1,\n };\n }\n if (opts.value === undefined) {\n return {\n stdout: \"\",\n stderr: `almanac: missing value for ${key}.\\n`,\n exitCode: 1,\n };\n }\n try {\n const file = targetConfigPath(opts.project === true);\n if (file === null) {\n return {\n stdout: \"\",\n stderr: \"almanac: no .almanac/ found for project config.\\n\",\n exitCode: 1,\n };\n }\n const next = setConfigValue(await readConfig({ cwd: process.cwd() }), key, opts.value);\n const raw = ensureRawObject(await readRawConfig(file));\n setRawConfigValue(raw, key, getConfigValue(next, key));\n await writeRawConfig(raw, file);\n return ok(\n `codealmanac: set ${key}=${formatConfigValue(getConfigValue(next, key))}` +\n `${opts.project === true ? \" in project config\" : \"\"}.\\n`,\n );\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return { stdout: \"\", stderr: `almanac: ${msg}\\n`, exitCode: 1 };\n }\n}\n\nexport async function runConfigUnset(opts: {\n key: string;\n project?: boolean;\n}): Promise<ConfigResult> {\n const key = parseConfigKey(opts.key);\n if (key === null) return unknownKey(opts.key);\n if (opts.project === true && key === \"update_notifier\") {\n return {\n stdout: \"\",\n stderr: \"almanac: update_notifier is user-level only.\\n\",\n exitCode: 1,\n };\n }\n const file = targetConfigPath(opts.project === true);\n if (file === null) {\n return {\n stdout: \"\",\n stderr: \"almanac: no .almanac/ found for project config.\\n\",\n exitCode: 1,\n };\n }\n const raw = ensureRawObject(await readRawConfig(file));\n deleteRawConfigValue(raw, key);\n await writeRawConfig(raw, file);\n return ok(\n `codealmanac: unset ${key}${opts.project === true ? \" in project config\" : \"\"}.\\n`,\n );\n}\n\nfunction unknownKey(key: string): ConfigResult {\n return {\n stdout: \"\",\n stderr:\n `almanac: unknown config key '${key}'. ` +\n `Expected one of: ${CONFIG_KEYS.join(\", \")}.\\n`,\n exitCode: 1,\n };\n}\n\nfunction ok(stdout: string): ConfigResult {\n return { stdout, stderr: \"\", exitCode: 0 };\n}\n\nfunction targetConfigPath(project: boolean): string | null {\n return project ? getProjectConfigPath(process.cwd()) : getConfigPath();\n}\n\nasync function readRawConfig(file = getConfigPath()): Promise<unknown> {\n try {\n return parseConfigText(await readFile(file, \"utf8\"), file);\n } catch {\n return null;\n }\n}\n\nasync function writeRawConfig(\n raw: Record<string, unknown>,\n file = getConfigPath(),\n): Promise<void> {\n await mkdir(dirname(file), { recursive: true });\n const tmp = `${file}.tmp`;\n await writeFile(tmp, serializeConfig(raw, file), \"utf8\");\n await rename(tmp, file);\n}\n\nfunction ensureRawObject(raw: unknown): Record<string, unknown> {\n if (raw !== null && typeof raw === \"object\" && !Array.isArray(raw)) {\n return raw as Record<string, unknown>;\n }\n return {};\n}\n\nfunction setRawConfigValue(\n raw: Record<string, unknown>,\n key: ConfigKey,\n value: string | boolean | null,\n): void {\n const parts = key.split(\".\");\n let cursor = raw;\n for (const part of parts.slice(0, -1)) {\n const next = cursor[part];\n if (next === null || typeof next !== \"object\" || Array.isArray(next)) {\n cursor[part] = {};\n }\n cursor = cursor[part] as Record<string, unknown>;\n }\n const leaf = parts[parts.length - 1];\n if (leaf !== undefined) cursor[leaf] = value;\n}\n\nfunction deleteRawConfigValue(raw: Record<string, unknown>, key: ConfigKey): void {\n const parts = key.split(\".\");\n const parents: Array<{ object: Record<string, unknown>; key: string }> = [];\n let cursor: unknown = raw;\n for (const part of parts.slice(0, -1)) {\n if (cursor === null || typeof cursor !== \"object\" || Array.isArray(cursor)) {\n return;\n }\n const object = cursor as Record<string, unknown>;\n parents.push({ object, key: part });\n cursor = object[part];\n }\n if (cursor === null || typeof cursor !== \"object\" || Array.isArray(cursor)) {\n return;\n }\n const leaf = parts[parts.length - 1];\n if (leaf === undefined) return;\n delete (cursor as Record<string, unknown>)[leaf];\n\n for (let i = parents.length - 1; i >= 0; i--) {\n const parent = parents[i];\n if (parent === undefined) continue;\n const value = parent.object[parent.key];\n if (\n value !== null &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n Object.keys(value).length === 0\n ) {\n delete parent.object[parent.key];\n }\n }\n}\n","import {\n AGENT_PROVIDER_IDS,\n isAgentProviderId,\n type AgentProviderId,\n type GlobalConfig,\n} from \"../update/config.js\";\n\nexport type ConfigKey =\n | \"update_notifier\"\n | \"agent.default\"\n | `agent.models.${AgentProviderId}`;\n\nexport interface ConfigEntry {\n key: ConfigKey;\n value: string | boolean | null;\n}\n\nexport const CONFIG_KEYS: ConfigKey[] = [\n \"update_notifier\",\n \"agent.default\",\n ...AGENT_PROVIDER_IDS.map((id) => `agent.models.${id}` as const),\n];\n\nexport function parseConfigKey(raw: string): ConfigKey | null {\n if (raw === \"update_notifier\" || raw === \"agent.default\") return raw;\n const prefix = \"agent.models.\";\n if (!raw.startsWith(prefix)) return null;\n const provider = raw.slice(prefix.length);\n if (!isAgentProviderId(provider)) return null;\n return `agent.models.${provider}`;\n}\n\nexport function getConfigValue(\n config: GlobalConfig,\n key: ConfigKey,\n): string | boolean | null {\n if (key === \"update_notifier\") return config.update_notifier;\n if (key === \"agent.default\") return config.agent.default;\n const provider = providerFromModelKey(key);\n return config.agent.models[provider] ?? null;\n}\n\nexport function setConfigValue(\n config: GlobalConfig,\n key: ConfigKey,\n rawValue: string | null,\n): GlobalConfig {\n if (key === \"update_notifier\") {\n return {\n ...config,\n update_notifier: parseBoolean(rawValue),\n };\n }\n if (key === \"agent.default\") {\n if (rawValue === null || !isAgentProviderId(rawValue)) {\n throw new Error(\"agent.default must be one of: claude, codex, cursor\");\n }\n return {\n ...config,\n agent: {\n ...config.agent,\n default: rawValue,\n },\n };\n }\n const provider = providerFromModelKey(key);\n const model = normalizeModel(rawValue);\n return {\n ...config,\n agent: {\n ...config.agent,\n models: {\n ...config.agent.models,\n [provider]: model,\n },\n },\n };\n}\n\nexport function configEntries(config: GlobalConfig): ConfigEntry[] {\n return CONFIG_KEYS.map((key) => ({\n key,\n value: getConfigValue(config, key),\n }));\n}\n\nexport function formatConfigValue(value: string | boolean | null): string {\n if (value === null) return \"default\";\n return String(value);\n}\n\nfunction providerFromModelKey(key: ConfigKey): AgentProviderId {\n const provider = key.slice(\"agent.models.\".length);\n if (!isAgentProviderId(provider)) {\n throw new Error(`not a model key: ${key}`);\n }\n return provider;\n}\n\nfunction parseBoolean(value: string | null): boolean {\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n throw new Error(\"update_notifier must be true or false\");\n}\n\nfunction normalizeModel(value: string | null): string | null {\n if (value === null) return null;\n if (value === \"default\" || value === \"null\") return null;\n if (value.length === 0) {\n throw new Error(\"model must be non-empty, default, or null\");\n }\n return value;\n}\n"],"mappings":";;;;;;;;;;;;;AAAA,SAAS,OAAO,UAAU,QAAQ,iBAAiB;AACnD,SAAS,eAAe;;;ACgBjB,IAAM,cAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA,GAAG,mBAAmB,IAAI,CAAC,OAAO,gBAAgB,EAAE,EAAW;AACjE;AAEO,SAAS,eAAe,KAA+B;AAC5D,MAAI,QAAQ,qBAAqB,QAAQ,gBAAiB,QAAO;AACjE,QAAM,SAAS;AACf,MAAI,CAAC,IAAI,WAAW,MAAM,EAAG,QAAO;AACpC,QAAM,WAAW,IAAI,MAAM,OAAO,MAAM;AACxC,MAAI,CAAC,kBAAkB,QAAQ,EAAG,QAAO;AACzC,SAAO,gBAAgB,QAAQ;AACjC;AAEO,SAAS,eACd,QACA,KACyB;AACzB,MAAI,QAAQ,kBAAmB,QAAO,OAAO;AAC7C,MAAI,QAAQ,gBAAiB,QAAO,OAAO,MAAM;AACjD,QAAM,WAAW,qBAAqB,GAAG;AACzC,SAAO,OAAO,MAAM,OAAO,QAAQ,KAAK;AAC1C;AAEO,SAAS,eACd,QACA,KACA,UACc;AACd,MAAI,QAAQ,mBAAmB;AAC7B,WAAO;AAAA,MACL,GAAG;AAAA,MACH,iBAAiB,aAAa,QAAQ;AAAA,IACxC;AAAA,EACF;AACA,MAAI,QAAQ,iBAAiB;AAC3B,QAAI,aAAa,QAAQ,CAAC,kBAAkB,QAAQ,GAAG;AACrD,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACvE;AACA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OAAO;AAAA,QACL,GAAG,OAAO;AAAA,QACV,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACA,QAAM,WAAW,qBAAqB,GAAG;AACzC,QAAM,QAAQ,eAAe,QAAQ;AACrC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO;AAAA,MACL,GAAG,OAAO;AAAA,MACV,QAAQ;AAAA,QACN,GAAG,OAAO,MAAM;AAAA,QAChB,CAAC,QAAQ,GAAG;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,cAAc,QAAqC;AACjE,SAAO,YAAY,IAAI,CAAC,SAAS;AAAA,IAC/B;AAAA,IACA,OAAO,eAAe,QAAQ,GAAG;AAAA,EACnC,EAAE;AACJ;AAEO,SAAS,kBAAkB,OAAwC;AACxE,MAAI,UAAU,KAAM,QAAO;AAC3B,SAAO,OAAO,KAAK;AACrB;AAEA,SAAS,qBAAqB,KAAiC;AAC7D,QAAM,WAAW,IAAI,MAAM,gBAAgB,MAAM;AACjD,MAAI,CAAC,kBAAkB,QAAQ,GAAG;AAChC,UAAM,IAAI,MAAM,oBAAoB,GAAG,EAAE;AAAA,EAC3C;AACA,SAAO;AACT;AAEA,SAAS,aAAa,OAA+B;AACnD,MAAI,UAAU,OAAQ,QAAO;AAC7B,MAAI,UAAU,QAAS,QAAO;AAC9B,QAAM,IAAI,MAAM,uCAAuC;AACzD;AAEA,SAAS,eAAe,OAAqC;AAC3D,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,UAAU,aAAa,UAAU,OAAQ,QAAO;AACpD,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACA,SAAO;AACT;;;ADrFA,eAAsB,cAAc,OAGhC,CAAC,GAA0B;AAC7B,QAAM,EAAE,QAAQ,QAAQ,IAAI,MAAM,sBAAsB,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC;AAC9E,QAAM,OAAO,cAAc,MAAM,EAAE,IAAI,CAAC,WAAW;AAAA,IACjD,GAAG;AAAA,IACH,QAAQ,QAAQ,MAAM,GAAG,KAAK;AAAA,EAChC,EAAE;AACF,MAAI,KAAK,SAAS,MAAM;AACtB,WAAO,GAAG,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,CAAI;AAAA,EAChD;AACA,QAAM,QAAQ,KAAK,IAAI,CAAC,QAAQ;AAC9B,UAAM,QAAQ,kBAAkB,IAAI,KAAK;AACzC,WAAO,KAAK,eAAe,OACvB,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC,IAAI,MAAM,OAAO,EAAE,CAAC,IAAI,IAAI,MAAM,KACvD,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC,IAAI,KAAK;AAAA,EACpC,CAAC;AACD,SAAO,GAAG,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA,CAAI;AACnC;AAEA,eAAsB,aAAa,MAIT;AACxB,QAAM,MAAM,eAAe,KAAK,GAAG;AACnC,MAAI,QAAQ,KAAM,QAAO,WAAW,KAAK,GAAG;AAC5C,QAAM,EAAE,QAAQ,QAAQ,IAAI,MAAM,sBAAsB,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC;AAC9E,QAAM,QAAQ,eAAe,QAAQ,GAAG;AACxC,QAAM,SAAS,QAAQ,GAAG,KAAK;AAC/B,MAAI,KAAK,SAAS,MAAM;AACtB,WAAO,GAAG,GAAG,KAAK,UAAU,EAAE,KAAK,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC;AAAA,CAAI;AAAA,EAClE;AACA,QAAM,WAAW,kBAAkB,KAAK;AACxC,SAAO;AAAA,IACL,KAAK,eAAe,OAChB,GAAG,GAAG,IAAI,QAAQ,KAAK,MAAM;AAAA,IAC7B,GAAG,QAAQ;AAAA;AAAA,EACjB;AACF;AAEA,eAAsB,aAAa,MAIT;AACxB,QAAM,MAAM,eAAe,KAAK,GAAG;AACnC,MAAI,QAAQ,KAAM,QAAO,WAAW,KAAK,GAAG;AAC5C,MAAI,KAAK,YAAY,QAAQ,QAAQ,mBAAmB;AACtD,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ;AAAA,EACF;AACA,MAAI,KAAK,UAAU,QAAW;AAC5B,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ,8BAA8B,GAAG;AAAA;AAAA,MACzC,UAAU;AAAA,IACZ;AAAA,EACF;AACA,MAAI;AACF,UAAM,OAAO,iBAAiB,KAAK,YAAY,IAAI;AACnD,QAAI,SAAS,MAAM;AACjB,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,UAAU;AAAA,MACZ;AAAA,IACF;AACA,UAAM,OAAO,eAAe,MAAM,WAAW,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,GAAG,KAAK,KAAK,KAAK;AACrF,UAAM,MAAM,gBAAgB,MAAM,cAAc,IAAI,CAAC;AACrD,sBAAkB,KAAK,KAAK,eAAe,MAAM,GAAG,CAAC;AACrD,UAAM,eAAe,KAAK,IAAI;AAC9B,WAAO;AAAA,MACL,oBAAoB,GAAG,IAAI,kBAAkB,eAAe,MAAM,GAAG,CAAC,CAAC,GAClE,KAAK,YAAY,OAAO,uBAAuB,EAAE;AAAA;AAAA,IACxD;AAAA,EACF,SAAS,KAAc;AACrB,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,WAAO,EAAE,QAAQ,IAAI,QAAQ,YAAY,GAAG;AAAA,GAAM,UAAU,EAAE;AAAA,EAChE;AACF;AAEA,eAAsB,eAAe,MAGX;AACxB,QAAM,MAAM,eAAe,KAAK,GAAG;AACnC,MAAI,QAAQ,KAAM,QAAO,WAAW,KAAK,GAAG;AAC5C,MAAI,KAAK,YAAY,QAAQ,QAAQ,mBAAmB;AACtD,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ;AAAA,EACF;AACA,QAAM,OAAO,iBAAiB,KAAK,YAAY,IAAI;AACnD,MAAI,SAAS,MAAM;AACjB,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ;AAAA,EACF;AACA,QAAM,MAAM,gBAAgB,MAAM,cAAc,IAAI,CAAC;AACrD,uBAAqB,KAAK,GAAG;AAC7B,QAAM,eAAe,KAAK,IAAI;AAC9B,SAAO;AAAA,IACL,sBAAsB,GAAG,GAAG,KAAK,YAAY,OAAO,uBAAuB,EAAE;AAAA;AAAA,EAC/E;AACF;AAEA,SAAS,WAAW,KAA2B;AAC7C,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,QACE,gCAAgC,GAAG,uBACf,YAAY,KAAK,IAAI,CAAC;AAAA;AAAA,IAC5C,UAAU;AAAA,EACZ;AACF;AAEA,SAAS,GAAG,QAA8B;AACxC,SAAO,EAAE,QAAQ,QAAQ,IAAI,UAAU,EAAE;AAC3C;AAEA,SAAS,iBAAiB,SAAiC;AACzD,SAAO,UAAU,qBAAqB,QAAQ,IAAI,CAAC,IAAI,cAAc;AACvE;AAEA,eAAe,cAAc,OAAO,cAAc,GAAqB;AACrE,MAAI;AACF,WAAO,gBAAgB,MAAM,SAAS,MAAM,MAAM,GAAG,IAAI;AAAA,EAC3D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,eACb,KACA,OAAO,cAAc,GACN;AACf,QAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAM,MAAM,GAAG,IAAI;AACnB,QAAM,UAAU,KAAK,gBAAgB,KAAK,IAAI,GAAG,MAAM;AACvD,QAAM,OAAO,KAAK,IAAI;AACxB;AAEA,SAAS,gBAAgB,KAAuC;AAC9D,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,GAAG;AAClE,WAAO;AAAA,EACT;AACA,SAAO,CAAC;AACV;AAEA,SAAS,kBACP,KACA,KACA,OACM;AACN,QAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,MAAI,SAAS;AACb,aAAW,QAAQ,MAAM,MAAM,GAAG,EAAE,GAAG;AACrC,UAAM,OAAO,OAAO,IAAI;AACxB,QAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AACpE,aAAO,IAAI,IAAI,CAAC;AAAA,IAClB;AACA,aAAS,OAAO,IAAI;AAAA,EACtB;AACA,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,MAAI,SAAS,OAAW,QAAO,IAAI,IAAI;AACzC;AAEA,SAAS,qBAAqB,KAA8B,KAAsB;AAChF,QAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,QAAM,UAAmE,CAAC;AAC1E,MAAI,SAAkB;AACtB,aAAW,QAAQ,MAAM,MAAM,GAAG,EAAE,GAAG;AACrC,QAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E;AAAA,IACF;AACA,UAAM,SAAS;AACf,YAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAClC,aAAS,OAAO,IAAI;AAAA,EACtB;AACA,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E;AAAA,EACF;AACA,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,MAAI,SAAS,OAAW;AACxB,SAAQ,OAAmC,IAAI;AAE/C,WAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,UAAM,SAAS,QAAQ,CAAC;AACxB,QAAI,WAAW,OAAW;AAC1B,UAAM,QAAQ,OAAO,OAAO,OAAO,GAAG;AACtC,QACE,UAAU,QACV,OAAO,UAAU,YACjB,CAAC,MAAM,QAAQ,KAAK,KACpB,OAAO,KAAK,KAAK,EAAE,WAAW,GAC9B;AACA,aAAO,OAAO,OAAO,OAAO,GAAG;AAAA,IACjC;AAAA,EACF;AACF;","names":[]}
|