@cryer/star-cli 0.3.7 → 0.3.8

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 CHANGED
@@ -17,7 +17,7 @@ Multi-model LLM access · streaming terminal UI · tool calling · permission co
17
17
 
18
18
  </div>
19
19
 
20
- Features: streaming REPL with slash commands (+ autocomplete) · OpenAI / Anthropic / OpenAI-compatible providers with an interactive `/connect` onboarding wizard · built-in fs / bash / web tools with a permission gate · git integration (`/commit` drafts Conventional Commits messages, `/diff` shows a colored working-tree diff, repo status injected into the system prompt) · plan mode with read-only research and plan approval · thinking spinner with dim reasoning preview · diff preview on write/edit approval · `@file` mentions · `!cmd` shell passthrough · custom slash commands from Markdown files · conversation compaction (`/compact`) · session persistence and resume with auto-generated titles (`/resume`, `star -r`) · subagent delegation for focused subtasks · lifecycle hooks (`PreToolUse`/`PostToolUse`/`Stop` shell commands from config) · file-write snapshots with `/undo` and checkpoint rollback with `/rewind` · TODO task tracking · background shell tasks with status-bar visibility (`/tasks`) · terminal bell on long turns and background-task completion · Markdown session export (`/export`) · `/init` + `/doctor` project scaffolding and environment checks · cost estimation · update notifier · `--json` NDJSON output for scripting.
20
+ Features: streaming REPL with slash commands (+ autocomplete) · OpenAI / Anthropic / OpenAI-compatible providers with an interactive `/connect` onboarding wizard · built-in fs / bash / web tools with a permission gate · git integration (`/commit` drafts Conventional Commits messages, `/diff` shows a colored working-tree diff, repo status sent as a per-turn request-scoped reminder so it never breaks the prompt cache) · prompt caching on Anthropic providers (cache breakpoints on the stable prefix and the history tail; OpenAI-style protocols cache server-side automatically) with a status-bar hit rate · token thrift: re-reading an unchanged file answers with a one-line note, and auxiliary calls (summaries, titles) can run on a cheaper `smallModel` · plan mode with read-only research and plan approval · thinking spinner with dim reasoning preview · diff preview on write/edit approval · `@file` mentions · `!cmd` shell passthrough · custom slash commands from Markdown files · conversation compaction (`/compact`) · session persistence and resume with auto-generated titles (`/resume`, `star -r`) · subagent delegation for focused subtasks · lifecycle hooks (`PreToolUse`/`PostToolUse`/`Stop` shell commands from config) · file-write snapshots with `/undo` and checkpoint rollback with `/rewind` · TODO task tracking · background shell tasks with status-bar visibility (`/tasks`) · terminal bell on long turns and background-task completion · Markdown session export (`/export`) · `/init` + `/doctor` project scaffolding and environment checks · cost estimation · update notifier · `--json` NDJSON output for scripting.
21
21
 
22
22
  ## Requirements
23
23
 
@@ -50,6 +50,9 @@ Config file: `~/.star-cli/config.toml` (set the `STAR_HOME` environment variable
50
50
 
51
51
  ```toml
52
52
  defaultModel = "gpt" # name of the [[models]] block used by default
53
+ # smallModel = "haiku" # optional [[models]] block for cheap auxiliary calls
54
+ # (compaction summaries, session titles, completion checks);
55
+ # unset = the active main model does them
53
56
  permissionMode = "ask" # ask | auto | readonly | yolo | plan
54
57
  contextMaxTokens = 100000 # context window in tokens (per-model override below)
55
58
  # compactThresholdTokens = 80000 # optional auto-compaction trigger in tokens; defaults to the
@@ -348,7 +351,7 @@ A `.starignore` file in the working directory hides paths from the agent: the `g
348
351
 
349
352
  ## Built-in tools
350
353
 
351
- `read_file`, `read_image` (attach an image file to the conversation mid-session — screenshots, mockups; png/jpg/gif/webp, auto-downsampled past 2000px), `write_file`, `edit_file`, `glob`, `grep`, `bash`, `web_fetch`, `web_search` (DuckDuckGo, no API key needed), `todo_read`, `todo_write`, `task_list`, `task_output`, `task_kill`, `subagent`, `skill`, `remember` — each declares a permission level (`read` / `write` / `exec`) enforced by the permission gate. Hard safety rules (dangerous shell commands, paths outside the working directory, secret files like `.env` / private keys) are denied in `ask` / `auto` / `readonly` and cannot be overridden by allow-rules.
354
+ `read_file`, `read_image` (attach an image file to the conversation mid-session — screenshots, mockups; png/jpg/gif/webp, auto-downsampled past 2000px), `screenshot` (capture the primary display and see it — visual verification of a running program, e.g. a dev server's rendered page; platform-native capture with no dependencies, DPI-aware on Windows so scaled displays come out pixel-exact, headless sessions get a clear error), `write_file`, `edit_file`, `glob`, `grep`, `bash`, `web_fetch`, `web_search` (DuckDuckGo, no API key needed), `todo_read`, `todo_write`, `task_list`, `task_output`, `task_kill`, `subagent`, `skill`, `remember` — each declares a permission level (`read` / `write` / `exec`) enforced by the permission gate. Hard safety rules (dangerous shell commands, paths outside the working directory, secret files like `.env` / private keys) are denied in `ask` / `auto` / `readonly` and cannot be overridden by allow-rules.
352
355
 
353
356
  The `subagent` tool (`exec` level, so it is hidden in plan mode and denied in readonly) spawns a child agent loop with the same built-in tools to handle a focused, self-contained subtask — research, exploration, or an isolated change — and returns the child's final report as the tool result. Subagents run one level deep (a subagent cannot spawn further subagents), share the parent's permission mode and confirmation prompt, and their conversation is not persisted to the session.
354
357
 
package/README.zh-CN.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  </div>
19
19
 
20
- 功能特性:带斜杠命令(+ 自动补全)的流式 REPL · OpenAI / Anthropic / OpenAI 兼容服务商,配交互式 `/connect` 接入向导 · 内置 fs / bash / web 工具,带权限门禁 · git 集成(`/commit` 起草 Conventional Commits 提交信息,`/diff` 显示彩色工作区 diff,仓库状态注入系统提示词)· 计划模式:只读调研 + 计划批准 · 带暗色推理预览的思考动画 · 写入/编辑批准时的 diff 预览 · `@file` 引用 · `!cmd` shell 直通 · 用 Markdown 文件自定义斜杠命令 · 对话压缩(`/compact`)· 会话持久化与恢复,自动生成标题(`/resume`、`star -r`)· 子代理委派,处理专注的子任务 · 生命周期钩子(来自配置的 `PreToolUse`/`PostToolUse`/`Stop` shell 命令)· 文件写入快照 `/undo` 与检查点回滚 `/rewind` · TODO 任务跟踪 · 状态栏可见的后台 shell 任务(`/tasks`)· 长回合与后台任务完成时的终端响铃 · Markdown 会话导出(`/export`)· `/init` + `/doctor` 项目脚手架与环境检查 · 成本估算 · 更新提醒 · 便于脚本化的 `--json` NDJSON 输出。
20
+ 功能特性:带斜杠命令(+ 自动补全)的流式 REPL · OpenAI / Anthropic / OpenAI 兼容服务商,配交互式 `/connect` 接入向导 · 内置 fs / bash / web 工具,带权限门禁 · git 集成(`/commit` 起草 Conventional Commits 提交信息,`/diff` 显示彩色工作区 diff,仓库状态以每回合请求级 reminder 发送,不会破坏提示词缓存)· Anthropic 服务商的提示词缓存(在稳定前缀与历史尾部打缓存断点;OpenAI 系协议由服务端自动缓存),状态栏显示命中率 · token 节俭:重复读取未变化的文件只回一行说明,压缩摘要、标题等辅助调用可配置更便宜的 `smallModel` · 计划模式:只读调研 + 计划批准 · 带暗色推理预览的思考动画 · 写入/编辑批准时的 diff 预览 · `@file` 引用 · `!cmd` shell 直通 · 用 Markdown 文件自定义斜杠命令 · 对话压缩(`/compact`)· 会话持久化与恢复,自动生成标题(`/resume`、`star -r`)· 子代理委派,处理专注的子任务 · 生命周期钩子(来自配置的 `PreToolUse`/`PostToolUse`/`Stop` shell 命令)· 文件写入快照 `/undo` 与检查点回滚 `/rewind` · TODO 任务跟踪 · 状态栏可见的后台 shell 任务(`/tasks`)· 长回合与后台任务完成时的终端响铃 · Markdown 会话导出(`/export`)· `/init` + `/doctor` 项目脚手架与环境检查 · 成本估算 · 更新提醒 · 便于脚本化的 `--json` NDJSON 输出。
21
21
 
22
22
  ## 环境要求
23
23
 
@@ -50,6 +50,9 @@ star -p "hi" # non-interactive print mode
50
50
 
51
51
  ```toml
52
52
  defaultModel = "gpt" # 默认使用的 [[models]] 块名称
53
+ # smallModel = "haiku" # 可选的 [[models]] 块,用于低价的辅助调用
54
+ #(压缩摘要、会话标题、完成度检查);
55
+ # 不设置 = 由当前主模型执行
53
56
  permissionMode = "ask" # ask | auto | readonly | yolo | plan
54
57
  contextMaxTokens = 100000 # 上下文窗口(token 数,下方可按模型覆盖)
55
58
  # compactThresholdTokens = 80000 # 可选的自动压缩触发阈值(token 数);默认为上下文窗口本身,
@@ -344,7 +347,7 @@ star -p "what's wrong in this UI?" --image screenshot.png --image mockup.jpg
344
347
 
345
348
  ## 内置工具
346
349
 
347
- `read_file`、`read_image`(会话中把图片文件带给模型——截图、设计稿等;支持 png/jpg/gif/webp,超过 2000px 自动降采样)、`write_file`、`edit_file`、`glob`、`grep`、`bash`、`web_fetch`、`web_search`(DuckDuckGo,无需 API 密钥)、`todo_read`、`todo_write`、`task_list`、`task_output`、`task_kill`、`subagent`、`skill`、`remember`——每个都声明一个权限级别(`read` / `write` / `exec`),由权限门禁强制执行。硬性安全规则(危险 shell 命令、工作目录之外的路径、`.env` / 私钥等机密文件)在 `ask` / `auto` / `readonly` 下被拒绝,且无法被 allow 规则覆盖。
350
+ `read_file`、`read_image`(会话中把图片文件带给模型——截图、设计稿等;支持 png/jpg/gif/webp,超过 2000px 自动降采样)、`screenshot`(截取主显示器画面给模型看——用于核对运行中程序的真实视觉效果,比如 dev server 渲染的页面;平台原生截图、零依赖,Windows 上 DPI 感知保证缩放下像素精确,无显示器的 headless 会话返回明确错误)、`write_file`、`edit_file`、`glob`、`grep`、`bash`、`web_fetch`、`web_search`(DuckDuckGo,无需 API 密钥)、`todo_read`、`todo_write`、`task_list`、`task_output`、`task_kill`、`subagent`、`skill`、`remember`——每个都声明一个权限级别(`read` / `write` / `exec`),由权限门禁强制执行。硬性安全规则(危险 shell 命令、工作目录之外的路径、`.env` / 私钥等机密文件)在 `ask` / `auto` / `readonly` 下被拒绝,且无法被 allow 规则覆盖。
348
351
 
349
352
  `subagent` 工具(`exec` 级别,因此在计划模式下隐藏,在 readonly 下被拒绝)派生一个带相同内置工具的子代理循环,处理专注、自包含的子任务——调研、探索或一处孤立的改动——并把子代理的最终报告作为工具结果返回。子代理只运行一层(子代理不能再派生子代理),共享父代理的权限模式与确认提示,其对话不持久化到会话。
350
353
 
@@ -7,9 +7,8 @@ import {
7
7
  isSensitivePath,
8
8
  pruneTreeReposExcept,
9
9
  removeTreeRepo,
10
- sessionsDir,
11
- wrapFetchWithActivity
12
- } from "./chunk-OOOW7FC4.js";
10
+ sessionsDir
11
+ } from "./chunk-OHLA2DP7.js";
13
12
 
14
13
  // src/cli/cost.ts
15
14
  function formatDollars(cost) {
@@ -541,202 +540,6 @@ Never consider the task finished until you have verified the result yourself: ru
541
540
  Do not end your turn while the task is still incomplete; keep going until it is done or you are genuinely blocked, and if you are blocked, state exactly what is missing.
542
541
  Never end a reply by announcing what you will do next \u2014 either do it now with tool calls, or do not mention it. Narrating future actions is not progress.`;
543
542
 
544
- // src/llm/registry.ts
545
- function listModels(config) {
546
- return config.models;
547
- }
548
- function resolveModelConfig(config, modelName) {
549
- const name = modelName ?? config.defaultModel;
550
- const model = config.models.find((m) => m.name === name);
551
- if (!model) {
552
- const available = config.models.map((m) => m.name).join(", ") || "(none)";
553
- throw new Error(`Model "${name}" not found. Available models: ${available}`);
554
- }
555
- return model;
556
- }
557
-
558
- // src/llm/provider.ts
559
- import { createAnthropic } from "@ai-sdk/anthropic";
560
- import { createOpenAI } from "@ai-sdk/openai";
561
- import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
562
-
563
- // src/llm/sse-normalize.ts
564
- function splitJsonPrefix(text) {
565
- if (!text.startsWith("{")) return null;
566
- let depth = 0;
567
- let inString = false;
568
- let escaped = false;
569
- for (let i = 0; i < text.length; i++) {
570
- const ch = text[i];
571
- if (inString) {
572
- if (escaped) escaped = false;
573
- else if (ch === "\\") escaped = true;
574
- else if (ch === '"') inString = false;
575
- continue;
576
- }
577
- if (ch === '"') inString = true;
578
- else if (ch === "{") depth += 1;
579
- else if (ch === "}") {
580
- depth -= 1;
581
- if (depth === 0) return [text.slice(0, i + 1), text.slice(i + 1).trimStart()];
582
- }
583
- }
584
- return null;
585
- }
586
- var SseNormalizeTransform = class extends TransformStream {
587
- constructor() {
588
- let buffer = "";
589
- let pending = null;
590
- const flushPending = (controller) => {
591
- if (pending === null) return;
592
- controller.enqueue(`data: ${pending}
593
-
594
- `);
595
- pending = null;
596
- };
597
- const emitPayload = (payload, controller) => {
598
- let rest = payload;
599
- for (; ; ) {
600
- const split = splitJsonPrefix(rest);
601
- if (!split) {
602
- pending = rest;
603
- return;
604
- }
605
- const [json, tail] = split;
606
- if (tail === "") {
607
- pending = json;
608
- return;
609
- }
610
- controller.enqueue(`data: ${json}
611
-
612
- `);
613
- if (tail.startsWith("data:")) {
614
- rest = tail.slice(5).replace(/^ /, "");
615
- continue;
616
- }
617
- if (tail.startsWith("{")) {
618
- rest = tail;
619
- continue;
620
- }
621
- controller.enqueue(`${tail}
622
- `);
623
- return;
624
- }
625
- };
626
- const handleLine = (raw, controller) => {
627
- const line = raw.endsWith("\r") ? raw.slice(0, -1) : raw;
628
- if (line === "") {
629
- flushPending(controller);
630
- return;
631
- }
632
- if (line.startsWith("data:")) {
633
- flushPending(controller);
634
- emitPayload(line.slice(5).replace(/^ /, ""), controller);
635
- return;
636
- }
637
- if (line.startsWith("{") && pending !== null) {
638
- flushPending(controller);
639
- emitPayload(line, controller);
640
- return;
641
- }
642
- flushPending(controller);
643
- controller.enqueue(`${line}
644
- `);
645
- };
646
- super({
647
- transform(chunk, controller) {
648
- buffer += chunk;
649
- let index = buffer.indexOf("\n");
650
- while (index !== -1) {
651
- handleLine(buffer.slice(0, index), controller);
652
- buffer = buffer.slice(index + 1);
653
- index = buffer.indexOf("\n");
654
- }
655
- },
656
- flush(controller) {
657
- if (buffer.length > 0) handleLine(buffer, controller);
658
- flushPending(controller);
659
- }
660
- });
661
- }
662
- };
663
- function createSseNormalizingFetch(baseFetch = fetch) {
664
- return async (input, init) => {
665
- const response = await baseFetch(input, init);
666
- const contentType = response.headers.get("content-type") ?? "";
667
- if (!response.body || !contentType.includes("text/event-stream")) {
668
- return response;
669
- }
670
- const body = response.body.pipeThrough(new TextDecoderStream()).pipeThrough(new SseNormalizeTransform()).pipeThrough(new TextEncoderStream());
671
- const headers = new Headers(response.headers);
672
- headers.delete("content-length");
673
- headers.delete("content-encoding");
674
- return new Response(body, {
675
- status: response.status,
676
- statusText: response.statusText,
677
- headers
678
- });
679
- };
680
- }
681
-
682
- // src/llm/provider.ts
683
- function resolveApiKey(provider) {
684
- const fromEnv = provider.apiKeyEnv ? process.env[provider.apiKeyEnv] : void 0;
685
- const apiKey = fromEnv ?? provider.apiKey;
686
- if (!apiKey) {
687
- const hint = provider.apiKeyEnv ? `set ${provider.apiKeyEnv} or configure apiKey` : "configure apiKey";
688
- throw new Error(`Missing API key for provider "${provider.name}": ${hint}`);
689
- }
690
- return apiKey;
691
- }
692
- function createModel(config, modelName) {
693
- const modelConfig = resolveModelConfig(config, modelName);
694
- const provider = config.providers.find((p) => p.name === modelConfig.provider);
695
- if (!provider) {
696
- const available = config.providers.map((p) => p.name).join(", ") || "(none)";
697
- throw new Error(
698
- `Provider "${modelConfig.provider}" for model "${modelConfig.name}" not found. Available providers: ${available}`
699
- );
700
- }
701
- const apiKey = resolveApiKey(provider);
702
- const trackedFetch = wrapFetchWithActivity();
703
- switch (provider.protocol) {
704
- case "anthropic":
705
- return createAnthropic({
706
- baseURL: provider.baseURL,
707
- apiKey,
708
- headers: provider.headers,
709
- fetch: trackedFetch
710
- })(modelConfig.model);
711
- case "openai-compatible":
712
- return createOpenAICompatible({
713
- name: provider.name,
714
- baseURL: provider.baseURL,
715
- apiKey,
716
- headers: provider.headers,
717
- fetch: trackedFetch
718
- })(modelConfig.model, {}, { includeUsage: true });
719
- case "openai-responses":
720
- return createOpenAI({
721
- baseURL: provider.baseURL,
722
- apiKey,
723
- headers: provider.headers,
724
- // Relays fronting the Responses API sometimes batch several JSON
725
- // events into one SSE data payload, which the SDK's parser cannot
726
- // read; normalize the wire format back to one event per data line.
727
- fetch: createSseNormalizingFetch(trackedFetch)
728
- }).responses(modelConfig.model);
729
- }
730
- }
731
- function reasoningEffortMetadata(config, modelName) {
732
- const modelConfig = resolveModelConfig(config, modelName);
733
- if (!modelConfig.reasoningEffort) return void 0;
734
- const provider = config.providers.find((p) => p.name === modelConfig.provider);
735
- if (!provider || provider.protocol === "anthropic") return void 0;
736
- const key = provider.protocol === "openai-responses" ? "openai" : provider.name;
737
- return { [key]: { reasoningEffort: modelConfig.reasoningEffort } };
738
- }
739
-
740
543
  // src/session/checkpoints.ts
741
544
  import fs from "fs/promises";
742
545
  import path2 from "path";
@@ -1422,10 +1225,6 @@ export {
1422
1225
  SYSTEM_PROMPT,
1423
1226
  loadEnvFile,
1424
1227
  upsertEnvContent,
1425
- listModels,
1426
- resolveModelConfig,
1427
- createModel,
1428
- reasoningEffortMetadata,
1429
1228
  loadSessionSnapshots,
1430
1229
  SessionStore,
1431
1230
  clearSessions,
@@ -1438,4 +1237,4 @@ export {
1438
1237
  formatSessionEntries,
1439
1238
  VERSION
1440
1239
  };
1441
- //# sourceMappingURL=chunk-4X25DNTF.js.map
1240
+ //# sourceMappingURL=chunk-IYWYSFSF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/cost.ts","../src/cli/terminal-text.ts","../src/cli/markdown.ts","../src/cli/format.ts","../src/cli/mentions.ts","../src/cli/system-prompt.ts","../src/session/checkpoints.ts","../src/session/store.ts","../src/session/clear.ts","../src/session/list.ts","../src/version.ts","../src/config/env.ts"],"sourcesContent":["import type { ModelConfig } from \"../config/schema\";\n\nexport interface TokenUsage {\n promptTokens: number;\n completionTokens: number;\n // Prompt tokens served from the provider cache, a subset of promptTokens\n // (OpenAI / openai-compatible style).\n cachedPromptTokens?: number;\n // Cache-read tokens reported on top of promptTokens (Anthropic style).\n cacheReadInputTokens?: number;\n}\n\nexport function formatDollars(cost: number): string {\n let s = cost.toFixed(4).replace(/0+$/, \"\");\n if (s.endsWith(\".\")) {\n s += \"00\";\n } else if (s.split(\".\")[1]?.length === 1) {\n s += \"0\";\n }\n return s;\n}\n\n// User-facing token counts by magnitude: the raw number below 1K, otherwise\n// K/M with two decimals (842 → \"842\", 532_100 → \"532.10K\", 6_740_000 →\n// \"6.74M\"). Request counts and config values stay raw — this is for tokens.\nexport function formatTokens(n: number): string {\n if (!Number.isFinite(n)) return String(n);\n if (n < 1000) return String(n);\n if (n < 1_000_000) return `${(n / 1000).toFixed(2)}K`;\n return `${(n / 1_000_000).toFixed(2)}M`;\n}\n\n// Numeric session cost in USD; null when the model has no pricing configured.\n// Cache pricing: OpenAI-style cached tokens are a subset of promptTokens, so\n// the uncached rest bills at promptPrice and the cached share at\n// cacheReadPrice (full promptPrice when no cache price is configured);\n// Anthropic-style cache reads come on top of promptTokens and bill at\n// cacheReadPrice only (unconfigured = unbilled, as before).\nexport function computeCostUsd(usage: TokenUsage, model: ModelConfig | undefined): number | null {\n if (model?.promptPrice === undefined || model.completionPrice === undefined) {\n return null;\n }\n const cached = Math.min(Math.max(usage.cachedPromptTokens ?? 0, 0), usage.promptTokens);\n const promptUsd =\n (usage.promptTokens - cached) * model.promptPrice +\n cached * (model.cacheReadPrice ?? model.promptPrice) +\n (usage.cacheReadInputTokens ?? 0) * (model.cacheReadPrice ?? 0);\n return (promptUsd + usage.completionTokens * model.completionPrice) / 1_000_000;\n}\n\nexport function estimateCost(\n usage: TokenUsage,\n modelName: string,\n model: ModelConfig | undefined,\n): string {\n const cost = computeCostUsd(usage, model);\n if (cost === null) {\n if (model !== undefined) {\n const missing = [\n model.promptPrice === undefined ? \"promptPrice\" : null,\n model.completionPrice === undefined ? \"completionPrice\" : null,\n ].filter((f) => f !== null);\n if (missing.length === 1) {\n return `Estimated cost: unknown (${model.name} is missing ${missing[0]} — both prices are needed)`;\n }\n }\n return `Estimated cost: unknown (no price configured for ${modelName})`;\n }\n const cachePrice =\n model?.cacheReadPrice !== undefined ? `, $${model.cacheReadPrice}/M cache read` : \"\";\n return `Estimated cost: $${formatDollars(cost)} (${model?.name} @ $${model?.promptPrice}/M prompt, $${model?.completionPrice}/M completion${cachePrice})`;\n}\n","// Model/tool-controlled text lands in Ink's live region, where each frame is\n// erased and redrawn based on Ink's own width math. string-width reports \\t\n// as width 0 while real terminals expand it to the next tab stop, and raw\n// control bytes (\\r, ESC, …) move the cursor — both make the on-screen frame\n// taller than Ink counted, so the erase misses lines and every spinner tick\n// leaves a stale duplicate behind (the \"scroll spam\" bug). Normalize such\n// text before it reaches the terminal: tabs become two spaces, all other C0\n// controls except \\n are dropped. Display-only — persisted messages and tool\n// results keep the original bytes.\n// biome-ignore lint/suspicious/noControlCharactersInRegex: matching control characters is the whole point\nconst UNSAFE_CHARS = /[\\x00-\\x09\\x0b-\\x1f\\x7f]/;\n// biome-ignore lint/suspicious/noControlCharactersInRegex: matching control characters is the whole point\nconst CONTROL_CHARS = /[\\x00-\\x08\\x0b-\\x1f\\x7f]/g;\nconst TABS = /\\t/g;\n\nexport function toTerminalSafe(text: string): string {\n if (!UNSAFE_CHARS.test(text)) return text;\n return text.replace(TABS, \" \").replace(CONTROL_CHARS, \"\");\n}\n","// Minimal markdown renderer for terminal output: compiles a markdown subset\n// (headings, bold/italic/strikethrough, inline code, fenced code blocks,\n// links, images, blockquotes, horizontal rules, pipe tables) into an\n// ANSI-styled string suitable for a single Ink <Text> node.\n//\n// `base` is the ANSI SGR code of the surrounding text color (e.g. \"32\" for\n// the green assistant text): every styled span ends with a full reset, so\n// the base must be re-opened after each span or the rest of the line would\n// lose its color.\n//\n// Streaming-safe: unclosed inline markers and fences render as literal text\n// until completed. Width accounting uses display cells (CJK/emoji = 2) so\n// tables stay aligned for CJK content.\n\nconst ESC = \"\\u001B[\";\nconst RESET = `${ESC}0m`;\n\nfunction styled(open: string, text: string, base: string): string {\n const reopen = base ? `${ESC}${base}m` : \"\";\n return `${ESC}${open}m${text}${RESET}${reopen}`;\n}\n\nconst bold = (t: string, b: string) => styled(\"1\", t, b);\nconst dim = (t: string, b: string) => styled(\"2\", t, b);\nconst italic = (t: string, b: string) => styled(\"3\", t, b);\nconst underline = (t: string, b: string) => styled(\"4\", t, b);\nconst strike = (t: string, b: string) => styled(\"9\", t, b);\nconst yellow = (t: string, b: string) => styled(\"33\", t, b);\nconst cyanBold = (t: string, b: string) => styled(\"1;36\", t, b);\n\n// Display width in terminal cells: CJK-wide and emoji ranges count 2,\n// combining marks / ZWJ / variation selectors count 0. Good enough for table\n// alignment; Ink does its own precise width math for wrapping.\nexport function cellWidth(text: string): number {\n let width = 0;\n for (const ch of text) {\n const cp = ch.codePointAt(0) ?? 0;\n if ((cp >= 0x0300 && cp <= 0x036f) || cp === 0x200d || (cp >= 0xfe00 && cp <= 0xfe0f)) {\n continue;\n }\n if (\n (cp >= 0x1100 && cp <= 0x115f) ||\n (cp >= 0x2e80 && cp <= 0x9fff) ||\n (cp >= 0xa000 && cp <= 0xa4cf) ||\n (cp >= 0xac00 && cp <= 0xd7a3) ||\n (cp >= 0xf900 && cp <= 0xfaff) ||\n (cp >= 0xfe30 && cp <= 0xfe4f) ||\n (cp >= 0xff00 && cp <= 0xff60) ||\n (cp >= 0xffe0 && cp <= 0xffe6) ||\n (cp >= 0x1f000 && cp <= 0x1faff) ||\n (cp >= 0x20000 && cp <= 0x3fffd)\n ) {\n width += 2;\n } else {\n width += 1;\n }\n }\n return width;\n}\n\nconst IMAGE_RE = /!\\[([^\\]]*)\\]\\(([^)\\s]+)\\)/g;\nconst LINK_RE = /\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/g;\nconst BOLD_RE = /\\*\\*([^*\\n]+)\\*\\*|__([^_\\n]+)__/g;\nconst STRIKE_RE = /~~([^~\\n]+)~~/g;\nconst STAR_ITALIC_RE = /\\*([^*\\n]+)\\*/g;\n// _emphasis_ only at word boundaries so snake_case stays literal.\nconst UNDERSCORE_ITALIC_RE = /(^|[^\\w])_([^_\\n]+)_(?=[^\\w]|$)/g;\n\n// Inline styles. Code spans are split out first so their contents are never\n// re-parsed for emphasis markers.\nexport function renderInline(text: string, base = \"\"): string {\n return text\n .split(/`([^`\\n]+)`/)\n .map((segment, index) => {\n // Odd segments are code-span contents.\n if (index % 2 === 1) return yellow(segment, base);\n return segment\n .replace(IMAGE_RE, (_m, alt: string) => dim(`[image: ${alt}]`, base))\n .replace(\n LINK_RE,\n (_m, label: string, url: string) => `${underline(label, base)}${dim(` (${url})`, base)}`,\n )\n .replace(BOLD_RE, (_m, star: string, under: string) => bold(star ?? under, base))\n .replace(STRIKE_RE, (_m, t: string) => strike(t, base))\n .replace(STAR_ITALIC_RE, (_m, t: string) => italic(t, base))\n .replace(UNDERSCORE_ITALIC_RE, (_m, pre: string, t: string) => pre + italic(t, base));\n })\n .join(\"\");\n}\n\nconst HEADING_RE = /^#{1,6}\\s+(.*)$/;\nconst HR_RE = /^\\s{0,3}(?:-{3,}|\\*{3,}|_{3,})\\s*$/;\nconst QUOTE_RE = /^\\s{0,3}>\\s?(.*)$/;\nconst FENCE_RE = /^\\s{0,3}(```+|~~~+)/;\nconst TABLE_SEPARATOR_CELL_RE = /^:?-{3,}:?$/;\n\nfunction isTableSeparator(line: string): boolean {\n const cells = splitTableRow(line);\n return (\n cells !== null &&\n cells.length > 0 &&\n cells.every((cell) => TABLE_SEPARATOR_CELL_RE.test(cell.trim()))\n );\n}\n\nfunction splitTableRow(line: string): string[] | null {\n const trimmed = line.trim();\n if (!trimmed.includes(\"|\")) return null;\n const stripped = trimmed.replace(/^\\|/, \"\").replace(/\\|$/, \"\");\n return stripped.split(\"|\").map((cell) => cell.trim());\n}\n\nconst MIN_COLUMN_WIDTH = 4;\n\n// Wraps a cell to its column width by display cells. Breaks at the last\n// space when there is one, hard-breaks otherwise (CJK text has no spaces).\nfunction wrapCell(text: string, width: number): string[] {\n const lines: string[] = [];\n let current = \"\";\n let currentWidth = 0;\n for (const ch of text) {\n const w = cellWidth(ch);\n if (currentWidth > 0 && currentWidth + w > width) {\n const lastSpace = current.lastIndexOf(\" \");\n if (lastSpace > 0 && ch !== \" \") {\n lines.push(current.slice(0, lastSpace));\n current = current.slice(lastSpace + 1) + ch;\n } else {\n lines.push(current);\n current = ch === \" \" ? \"\" : ch;\n }\n currentWidth = cellWidth(current);\n } else {\n current += ch;\n currentWidth += w;\n }\n }\n if (current.length > 0 || lines.length === 0) lines.push(current);\n return lines;\n}\n\nfunction renderTable(lines: string[], base: string, maxWidth: number): string[] {\n const rows: string[][] = [];\n for (const line of lines) {\n const cells = splitTableRow(line);\n if (cells) rows.push(cells);\n }\n if (rows.length < 2) return lines;\n const columnCount = Math.max(...rows.map((row) => row.length));\n // Natural widths first: columns only shrink when the whole grid would\n // exceed the terminal — then the widest column gives up one cell at a\n // time. Overlong cell text wraps onto extra lines instead of truncating.\n const widths = Array.from({ length: columnCount }, (_, column) =>\n Math.max(1, ...rows.map((row) => cellWidth(row[column] ?? \"\"))),\n );\n // 2 for the indent, 3 per \" │ \" joiner.\n const totalWidth = () =>\n 2 + widths.reduce((sum, width) => sum + width, 0) + 3 * (columnCount - 1);\n while (totalWidth() > maxWidth) {\n const widest = widths.indexOf(Math.max(...widths));\n if (widths[widest] === undefined || widths[widest] <= MIN_COLUMN_WIDTH) break;\n widths[widest] -= 1;\n }\n // Widths come from the raw cell text; padding is appended after styling so\n // ANSI bytes never skew the alignment.\n const joiner = ` ${dim(\"│\", base)} `;\n const formatRow = (row: string[], style: (cell: string) => string): string[] => {\n const wrapped = widths.map((width, column) => wrapCell(row[column] ?? \"\", width));\n const height = Math.max(...wrapped.map((cell) => cell.length));\n const out: string[] = [];\n for (let line = 0; line < height; line++) {\n out.push(\n ` ${widths\n .map((width, column) => {\n const raw = wrapped[column]?.[line] ?? \"\";\n return style(raw) + \" \".repeat(Math.max(0, width - cellWidth(raw)));\n })\n .join(joiner)\n .trimEnd()}`,\n );\n }\n return out;\n };\n const separator = dim(` ${widths.map((width) => \"─\".repeat(width)).join(\"─┼─\")}`, base);\n const [header, , ...body] = rows as [string[], string[], ...string[][]];\n return [\n ...formatRow(header, (cell) => bold(cell, base)),\n separator,\n ...body.flatMap((row, index) => {\n const lines = formatRow(row, (cell) => renderInline(cell, base));\n return index < body.length - 1 ? [...lines, separator] : lines;\n }),\n ];\n}\n\n// Longest leading run of lines that ends at a markdown block boundary: not\n// inside a fenced code block, and not inside a run of pipe lines (a table, or\n// a would-be table whose separator is still streaming). The REPL commits\n// streamed text to static history in chunks and each chunk is rendered\n// standalone, so a chunk must never end mid-construct — a table split in two\n// would render its tail as raw pipe text.\nexport function committableLineCount(lines: string[]): number {\n let inFence = false;\n let safe = 0;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? \"\";\n if (FENCE_RE.test(line)) inFence = !inFence;\n const isPipe = line.includes(\"|\");\n // For the last line the next line is still streaming, so assume the worst.\n const nextIsPipe = i + 1 >= lines.length || (lines[i + 1] ?? \"\").includes(\"|\");\n if (!inFence && !(isPipe && nextIsPipe)) safe = i + 1;\n }\n return safe;\n}\n\n// Renders a markdown block into an ANSI-styled string. `base` re-opens the\n// surrounding text color after each styled span (see module docstring).\n// `maxWidth` caps rendered table grids so rows never wrap in the terminal.\nexport function renderMarkdown(text: string, base = \"\", maxWidth = 80): string {\n const lines = text.split(\"\\n\");\n const out: string[] = [];\n let inFence = false;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? \"\";\n if (inFence) {\n if (FENCE_RE.test(line)) {\n inFence = false;\n } else {\n out.push(`${dim(\" ▎\", base)}${line}`);\n }\n continue;\n }\n const fence = FENCE_RE.exec(line);\n if (fence) {\n inFence = true;\n const lang = line\n .trim()\n .slice(fence[1]?.length ?? 0)\n .trim();\n if (lang) out.push(dim(` ▎ ${lang}`, base));\n continue;\n }\n const heading = HEADING_RE.exec(line);\n if (heading) {\n out.push(cyanBold(heading[1] ?? \"\", base));\n continue;\n }\n if (HR_RE.test(line)) {\n out.push(dim(\" ────────\", base));\n continue;\n }\n const quote = QUOTE_RE.exec(line);\n if (quote) {\n out.push(`${dim(\" ▎\", base)}${italic(renderInline(quote[1] ?? \"\", base), base)}`);\n continue;\n }\n // Pipe table: a header row followed by a --- separator row.\n const headerCells = splitTableRow(line);\n if (headerCells && i + 1 < lines.length && isTableSeparator(lines[i + 1] ?? \"\")) {\n const block = [line];\n let j = i + 1;\n while (j < lines.length && splitTableRow(lines[j] ?? \"\") !== null) {\n block.push(lines[j] ?? \"\");\n j += 1;\n }\n out.push(...renderTable(block, base, maxWidth));\n i = j - 1;\n continue;\n }\n out.push(renderInline(line, base));\n }\n return out.join(\"\\n\");\n}\n","import type { CoreMessage } from \"../core/messages\";\nimport type { DisplayMessage } from \"./components/MessageList\";\nimport { committableLineCount } from \"./markdown\";\nimport { toTerminalSafe } from \"./terminal-text\";\n\nexport function summarizeArgs(args: unknown, maxLength = 120): string {\n let json: string;\n try {\n json = JSON.stringify(args) ?? String(args);\n } catch {\n json = String(args);\n }\n return json.length > maxLength ? `${json.slice(0, maxLength)}...` : json;\n}\n\nexport function previewLines(text: string, maxLines = 10): { text: string; truncated: boolean } {\n const lines = text.split(\"\\n\");\n if (lines.length <= maxLines) return { text, truncated: false };\n return { text: lines.slice(0, maxLines).join(\"\\n\"), truncated: true };\n}\n\nexport function formatByteCount(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;\n}\n\n// Elapsed-time label for the thinking indicator: seconds while short, then\n// the two largest units (1m5s, 1h2m3s) so long relay waits stay readable.\nexport function formatElapsedSeconds(seconds: number): string {\n const total = Math.max(0, Math.floor(seconds));\n if (total < 60) return `${total}s`;\n const minutes = Math.floor(total / 60);\n const secs = total % 60;\n if (minutes < 60) return `${minutes}m${secs}s`;\n return `${Math.floor(minutes / 60)}h${minutes % 60}m${secs}s`;\n}\n\n// Splits a streaming buffer into a committable head (complete lines) and a\n// remainder, once the buffer holds at least minCompleteLines complete lines.\n// The last line is always kept in the remainder: it may still be growing.\n// The head is further capped at a markdown block boundary (never mid-table\n// or mid-fence) because committed chunks render standalone in static history.\n// Returns null when there is nothing worth committing yet.\nexport function splitCommittableLines(\n text: string,\n minCompleteLines: number,\n): { committed: string; rest: string } | null {\n const lastNewline = text.lastIndexOf(\"\\n\");\n if (lastNewline < 0) return null;\n const complete = text.slice(0, lastNewline);\n const lines = complete.split(\"\\n\");\n const count = committableLineCount(lines);\n if (count < minCompleteLines) return null;\n const committed = lines.slice(0, count).join(\"\\n\");\n return { committed, rest: text.slice(committed.length + 1) };\n}\n\nexport function formatStreamError(error: Error): string {\n if (error.message.includes(\"Unexpected end of JSON input\")) {\n return `${error.message} (response stream was truncated — try again)`;\n }\n return error.message;\n}\n\nexport function coreMessageText(message: CoreMessage): string {\n const content = message.content;\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return content\n .filter((part) => part.type === \"text\")\n .map((part) => (\"text\" in part ? part.text : \"\"))\n .join(\" \");\n }\n return \"\";\n}\n\nexport function buildDisplayMessages(messages: CoreMessage[]): DisplayMessage[] {\n const display: DisplayMessage[] = [];\n let collapsed = 0;\n for (const message of messages) {\n if (message.role === \"user\" || message.role === \"assistant\") {\n const text = coreMessageText(message);\n if (text) {\n // Restored history skips the streaming ingestion path, so escape it\n // here instead (idempotent — live-turn text is already normalized).\n display.push({ id: display.length, role: message.role, text: toTerminalSafe(text) });\n } else {\n collapsed++;\n }\n } else if (message.role === \"tool\") {\n collapsed++;\n }\n }\n if (collapsed > 0) {\n display.push({\n id: display.length,\n role: \"system\",\n text: `Restored ${collapsed} history message(s) not shown here.`,\n });\n }\n return display;\n}\n","import { readFile, readdir, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { downsampleImageIfNeeded } from \"../core/image-resize\";\nimport type { ImageInput } from \"../core/messages\";\nimport {\n type IgnorePredicate,\n SKIP_DIRS,\n createIgnorePredicate,\n isSensitivePath,\n} from \"../tools/fs/util\";\n\nexport const MAX_MENTION_BYTES = 100 * 1024;\nexport const MAX_IMAGE_BYTES = 5 * 1024 * 1024;\nexport const MAX_DIR_MENTION_ENTRIES = 200;\nexport const MAX_DIR_MENTION_DEPTH = 10;\n// Backstop for the total-entry count: past this the listing reports \"many\n// more entries\" instead of counting a huge tree (e.g. @. in a repo) to the\n// end.\nexport const MAX_DIR_MENTION_TOTAL = 5000;\n\nconst IMAGE_MIME_TYPES: Record<string, string> = {\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n};\n\nexport function imageMimeType(filePath: string): string | null {\n return IMAGE_MIME_TYPES[path.extname(filePath).toLowerCase()] ?? null;\n}\n\n// Reads an image file into a base64 ImageInput, downsampling anything past\n// the dimension cap. Returns null when the path is not a supported image or\n// cannot be read.\nexport async function readImageInput(filePath: string, cwd: string): Promise<ImageInput | null> {\n const mimeType = imageMimeType(filePath);\n if (!mimeType) return null;\n const abs = path.resolve(cwd, filePath);\n const st = await stat(abs).catch(() => null);\n if (!st || !st.isFile() || st.size > MAX_IMAGE_BYTES) return null;\n const buf = await readFile(abs).catch(() => null);\n if (!buf) return null;\n const result = await downsampleImageIfNeeded({\n path: filePath,\n mimeType,\n data: buf.toString(\"base64\"),\n });\n return result.image;\n}\n\nexport interface ParsedMentions {\n cleanText: string;\n mentions: string[];\n}\n\n// CJK ranges cover unified ideographs, Hangul syllables, compatibility\n// ideographs and fullwidth forms so @中文文件名.md mentions parse. Keep in\n// sync with the token charset path-suggest's extractAtToken accepts (\\S).\nconst MENTION_RE =\n /(?<=^|\\s)@([A-Za-z]:[\\\\/][A-Za-z0-9._\\-/\\\\\\u2e80-\\u9fff\\uac00-\\ud7af\\uf900-\\ufaff\\uff00-\\uffef]*|[A-Za-z0-9._\\-/\\\\\\u2e80-\\u9fff\\uac00-\\ud7af\\uf900-\\ufaff\\uff00-\\uffef]+)/g;\n\nexport function parseMentions(text: string): ParsedMentions {\n const mentions: string[] = [];\n const seen = new Set<string>();\n const ranges: Array<[number, number]> = [];\n for (const match of text.matchAll(MENTION_RE)) {\n const mention = match[1];\n const full = match[0];\n if (mention === undefined || full === undefined) continue;\n ranges.push([match.index, match.index + full.length]);\n if (!seen.has(mention)) {\n seen.add(mention);\n mentions.push(mention);\n }\n }\n if (ranges.length === 0) {\n return { cleanText: text, mentions };\n }\n let clean = \"\";\n let last = 0;\n for (const [start, end] of ranges) {\n clean += text.slice(last, start);\n last = end;\n }\n clean += text.slice(last);\n const cleanText = clean\n .replace(/[ \\t]+\\n/g, \"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n return { cleanText, mentions };\n}\n\nexport interface MentionSkip {\n path: string;\n reason: string;\n}\n\nexport interface ResolvedMentions {\n input: string;\n attached: string[];\n skipped: MentionSkip[];\n images: ImageInput[];\n}\n\nfunction isBinaryContent(buf: Buffer): boolean {\n const probe = buf.subarray(0, 8000);\n return probe.includes(0);\n}\n\ninterface DirListState {\n lines: string[];\n shown: number;\n total: number;\n capped: boolean;\n}\n\nasync function listDirEntries(\n dir: string,\n prefix: string,\n depth: number,\n state: DirListState,\n ignore: IgnorePredicate | undefined,\n): Promise<void> {\n if (state.capped) return;\n const entries = await readdir(dir, { withFileTypes: true }).catch(() => null);\n if (!entries) return;\n const visible = entries\n .filter((entry) => entry.isDirectory() || entry.isFile())\n .filter((entry) => !(entry.isDirectory() && SKIP_DIRS.has(entry.name)))\n .filter((entry) => !isSensitivePath(entry.name))\n .filter((entry) => !ignore?.(path.join(dir, entry.name), entry.isDirectory()))\n .sort((a, b) => {\n const dirDiff = Number(b.isDirectory()) - Number(a.isDirectory());\n return dirDiff !== 0 ? dirDiff : a.name.localeCompare(b.name);\n });\n for (let i = 0; i < visible.length; i++) {\n if (state.total >= MAX_DIR_MENTION_TOTAL) {\n state.capped = true;\n return;\n }\n const entry = visible[i];\n if (!entry) continue;\n state.total += 1;\n const last = i === visible.length - 1;\n const isDir = entry.isDirectory();\n if (state.shown < MAX_DIR_MENTION_ENTRIES) {\n state.shown += 1;\n state.lines.push(`${prefix}${last ? \"└── \" : \"├── \"}${entry.name}${isDir ? \"/\" : \"\"}`);\n if (isDir && depth < MAX_DIR_MENTION_DEPTH) {\n await listDirEntries(\n path.join(dir, entry.name),\n `${prefix}${last ? \" \" : \"│ \"}`,\n depth + 1,\n state,\n ignore,\n );\n }\n } else if (isDir && depth < MAX_DIR_MENTION_DEPTH) {\n await listDirEntries(path.join(dir, entry.name), prefix, depth + 1, state, ignore);\n }\n }\n}\n\n// Renders a directory mention as an indented tree block. Returns null when the\n// directory cannot be read at all.\nasync function directoryBlock(\n abs: string,\n mention: string,\n ignore: IgnorePredicate | undefined,\n): Promise<string | null> {\n const display = `${mention.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\")}/`;\n const state: DirListState = { lines: [`${display}`], shown: 0, total: 0, capped: false };\n const probe = await readdir(abs).catch(() => null);\n if (!probe) return null;\n await listDirEntries(abs, \"\", 1, state, ignore);\n if (state.capped) {\n state.lines.push(\"... (truncated, many more entries)\");\n } else if (state.total > state.shown) {\n state.lines.push(\n `... (truncated, ${state.total - state.shown} more entries — use glob/grep tools or mention a subdirectory to see more)`,\n );\n }\n return `--- @${display} (directory) ---\\n${state.lines.join(\"\\n\")}\\n--- end ---`;\n}\n\nexport async function resolveMentions(text: string, cwd: string): Promise<ResolvedMentions> {\n const { cleanText, mentions } = parseMentions(text);\n if (mentions.length === 0) {\n return { input: text, attached: [], skipped: [], images: [] };\n }\n const blocks: string[] = [];\n const attached: string[] = [];\n const skipped: MentionSkip[] = [];\n const images: ImageInput[] = [];\n let ignore: IgnorePredicate | null | undefined;\n for (const mention of mentions) {\n const abs = path.resolve(cwd, mention);\n if (isSensitivePath(abs)) {\n skipped.push({ path: mention, reason: \"sensitive file\" });\n continue;\n }\n const mimeType = imageMimeType(mention);\n if (mimeType) {\n const st = await stat(abs).catch(() => null);\n if (!st || !st.isFile()) {\n skipped.push({ path: mention, reason: st ? \"is a directory\" : \"file not found\" });\n continue;\n }\n if (st.size > MAX_IMAGE_BYTES) {\n skipped.push({ path: mention, reason: \"image too large\" });\n continue;\n }\n const buf = await readFile(abs).catch(() => null);\n if (!buf) {\n skipped.push({ path: mention, reason: \"unreadable\" });\n continue;\n }\n const result = await downsampleImageIfNeeded({\n path: mention,\n mimeType,\n data: buf.toString(\"base64\"),\n });\n attached.push(mention);\n images.push(result.image);\n continue;\n }\n const st = await stat(abs).catch(() => null);\n if (!st) {\n skipped.push({ path: mention, reason: \"file not found\" });\n continue;\n }\n if (st.isDirectory()) {\n if (ignore === undefined) {\n ignore = createIgnorePredicate(cwd) ?? null;\n }\n const block = await directoryBlock(abs, mention, ignore ?? undefined);\n if (!block) {\n skipped.push({ path: mention, reason: \"unreadable\" });\n continue;\n }\n attached.push(mention);\n blocks.push(block);\n continue;\n }\n if (st.size > MAX_MENTION_BYTES) {\n skipped.push({ path: mention, reason: `exceeds ${MAX_MENTION_BYTES / 1024}KB limit` });\n continue;\n }\n const buf = await readFile(abs).catch(() => null);\n if (!buf) {\n skipped.push({ path: mention, reason: \"unreadable\" });\n continue;\n }\n if (isBinaryContent(buf)) {\n skipped.push({ path: mention, reason: \"binary file\" });\n continue;\n }\n attached.push(mention);\n blocks.push(`--- @${mention} ---\\n${buf.toString(\"utf8\")}\\n--- end ---`);\n }\n const input = [cleanText, ...blocks].filter((s) => s.length > 0).join(\"\\n\\n\") || text;\n return { input, attached, skipped, images };\n}\n","// Base system prompt shared by main.tsx (initial loop) and the REPL's\n// switchModel (rebuilt loop): syncSystemMessage re-derives the prompt from\n// opts.system every turn, so a rebuilt loop without it would silently drop\n// the core instructions whenever AGENTS.md/git/skills context exists.\nexport const SYSTEM_PROMPT = `You are Star CLI, an AI coding agent running in the user's terminal.\nYou help with software engineering tasks: reading, writing and editing code, running shell commands, and managing todos.\nBe concise and direct. Use tools when they help accomplish the task.\nFor any task with two or more steps, start by creating a todo list with todo_write and keep it updated as you progress; only mark an item completed after you have verified its result.\nThe working directory is the user's project root; never touch files outside it without explicit instruction.\nFor conversion, batch-processing, or other scriptable tasks, write a script file into the working directory and run it instead of pasting long inline code into the shell; once the script's output is verified, delete the script unless the user asked to keep it.\nWhen a tool call or command fails, read the error output, work out the cause, and try again with a corrected or alternative approach — never repeat an identical failing call without changing something.\nNever consider the task finished until you have verified the result yourself: run the tests, the build, or a check command that proves the output is correct, and if verification fails or reveals gaps, keep fixing until it passes.\nDo not end your turn while the task is still incomplete; keep going until it is done or you are genuinely blocked, and if you are blocked, state exactly what is missing.\nNever end a reply by announcing what you will do next — either do it now with tool calls, or do not mention it. Narrating future actions is not progress.`;\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { FileSnapshot } from \"../tools/fs/snapshots\";\n\nexport interface CheckpointRecord {\n id: number;\n timestamp: number;\n path: string;\n existed: boolean;\n toolName: string;\n turn: number;\n messageIndex: number;\n owner?: \"root\" | \"subagent\";\n // Set when the original content exceeded the snapshot size cap: no content\n // file exists and reverts must skip the file.\n contentTooLarge?: boolean;\n}\n\nfunction checkpointsDir(sessionDir: string): string {\n return path.join(sessionDir, \"checkpoints\");\n}\n\nfunction indexPath(sessionDir: string): string {\n return path.join(checkpointsDir(sessionDir), \"index.json\");\n}\n\n// Read-modify-write on index.json is serialized per session directory, the\n// same pattern as SessionStore's metaWriteQueue: the root loop and background\n// subagents append checkpoints concurrently, and unprotected read-then-write\n// loses records.\nconst indexWriteQueues = new Map<string, Promise<void>>();\n\nfunction enqueueIndexWrite<T>(sessionDir: string, op: () => Promise<T>): Promise<T> {\n const queued = indexWriteQueues.get(sessionDir) ?? Promise.resolve();\n const run = queued.then(op);\n indexWriteQueues.set(\n sessionDir,\n run.then(\n () => {},\n () => {},\n ),\n );\n return run;\n}\n\nexport function checkpointContentPath(sessionDir: string, id: number): string {\n return path.join(checkpointsDir(sessionDir), `${id}.snapshot`);\n}\n\nexport async function listCheckpointRecords(sessionDir: string): Promise<CheckpointRecord[]> {\n let raw: string;\n try {\n raw = await fs.readFile(indexPath(sessionDir), \"utf8\");\n } catch {\n return [];\n }\n try {\n const parsed = JSON.parse(raw) as CheckpointRecord[];\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\n// Lazily creates the checkpoints directory on first use, mirroring\n// SessionStore's lazy creation — a session without file writes leaves no\n// checkpoints on disk.\nexport async function appendCheckpointRecord(\n sessionDir: string,\n record: CheckpointRecord,\n content: string | null,\n): Promise<void> {\n await enqueueIndexWrite(sessionDir, async () => {\n await fs.mkdir(checkpointsDir(sessionDir), { recursive: true, mode: 0o700 });\n if (record.existed && content !== null) {\n await fs.writeFile(checkpointContentPath(sessionDir, record.id), content, \"utf8\");\n }\n const records = await listCheckpointRecords(sessionDir);\n records.push(record);\n await fs.writeFile(indexPath(sessionDir), JSON.stringify(records, null, 2));\n });\n}\n\nexport async function removeCheckpointRecords(sessionDir: string, ids: number[]): Promise<void> {\n if (ids.length === 0) return;\n await enqueueIndexWrite(sessionDir, async () => {\n const drop = new Set(ids);\n const records = await listCheckpointRecords(sessionDir);\n const kept = records.filter((record) => !drop.has(record.id));\n if (kept.length === records.length) return;\n await fs.mkdir(checkpointsDir(sessionDir), { recursive: true, mode: 0o700 });\n await fs.writeFile(indexPath(sessionDir), JSON.stringify(kept, null, 2));\n for (const id of ids) {\n await fs.rm(checkpointContentPath(sessionDir, id), { force: true });\n }\n });\n}\n\n// Maps persisted records back into snapshots whose content is read lazily\n// from the per-checkpoint content file, for hydrating the in-memory stack\n// after a session resume.\nexport async function loadSessionSnapshots(sessionDir: string): Promise<FileSnapshot[]> {\n const records = await listCheckpointRecords(sessionDir);\n return records.map((record) => ({\n ...record,\n content: null,\n contentFile:\n record.existed && !record.contentTooLarge\n ? checkpointContentPath(sessionDir, record.id)\n : undefined,\n }));\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport { sessionsDir } from \"../config/paths\";\nimport type { CoreMessage } from \"../core/messages\";\nimport {\n type CheckpointRecord,\n appendCheckpointRecord,\n listCheckpointRecords,\n removeCheckpointRecords,\n} from \"./checkpoints\";\n\nexport interface SessionUsage {\n requests: number;\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n // Prompt-cache totals; present only after a provider reports cache fields.\n cachedPromptTokens?: number;\n cacheReadInputTokens?: number;\n}\n\nexport interface DayUsageBucket {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n}\n\nexport interface SessionMeta {\n id: string;\n title: string;\n model: string;\n cwd: string;\n createdAt: number;\n updatedAt: number;\n usage?: SessionUsage;\n // Per-day token buckets keyed by local date (YYYY-MM-DD), recorded alongside\n // `usage` from the day this field was introduced; older sessions only have\n // the grand totals in `usage`.\n usageByDay?: Record<string, DayUsageBucket>;\n}\n\nfunction generateId(now: Date): string {\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const stamp =\n `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}` +\n `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;\n const suffix = Math.random().toString(16).slice(2, 8).padEnd(6, \"0\");\n return `${stamp}-${suffix}`;\n}\n\nfunction debugWarn(message: string): void {\n if (process.env.STAR_DEBUG === \"1\") process.stderr.write(`[star-cli] ${message}\\n`);\n}\n\n// Persistent message-write failures surface unconditionally (unlike\n// debugWarn): a dropped append means the line is missing after a resume.\nfunction warn(message: string): void {\n process.stderr.write(`[star-cli] ${message}\\n`);\n}\n\n// Windows antivirus and search indexers briefly lock freshly written files,\n// so a write or rename right after the touch can come back EPERM/EACCES/EBUSY;\n// a few short retries ride out the scan. Shared by meta writes, message\n// appends and history rewrites.\nconst FS_WRITE_MAX_ATTEMPTS = 5;\nconst FS_WRITE_RETRY_DELAY_MS = 25;\n\nfunction isTransientFsError(error: unknown): boolean {\n const code = (error as NodeJS.ErrnoException).code;\n return code === \"EPERM\" || code === \"EACCES\" || code === \"EBUSY\";\n}\n\nasync function withTransientFsRetry(operation: () => Promise<unknown>): Promise<void> {\n let lastError: unknown;\n for (let attempt = 0; attempt < FS_WRITE_MAX_ATTEMPTS; attempt++) {\n try {\n await operation();\n return;\n } catch (error) {\n lastError = error;\n if (isTransientFsError(error) && attempt + 1 < FS_WRITE_MAX_ATTEMPTS) {\n await new Promise((resolve) =>\n setTimeout(resolve, FS_WRITE_RETRY_DELAY_MS * (attempt + 1)),\n );\n } else {\n break;\n }\n }\n }\n throw lastError;\n}\n\n// Brands the fallback meta returned for a corrupt/unreadable meta.json so\n// writeMeta can refuse to persist it: writing the empty defaults back would\n// wipe the real model/cwd/usage fields still sitting in the damaged file.\nconst FALLBACK_META: unique symbol = Symbol(\"star.fallbackMeta\");\n\nfunction isFallbackMeta(meta: SessionMeta): boolean {\n return (meta as unknown as Record<symbol, unknown>)[FALLBACK_META] === true;\n}\n\n// append() bumps only meta.updatedAt, so those writes are debounced: at most\n// one meta flush per window during a burst plus a trailing flush after it\n// settles (a 30-tool-call turn appends ~60 messages, and each used to land\n// its own atomic tmp+rename meta write). Substantive changes (title, usage,\n// history rewrites, the initial write) still flush immediately.\nconst META_FLUSH_INTERVAL_MS = 2000;\n\n// messages.jsonl is read back in chunks of this size instead of buffering\n// the whole file (a resume used to peak at 3-4x the file size in memory).\nconst MESSAGES_READ_CHUNK_BYTES = 64 * 1024;\n\nexport function dayKey(date: Date): string {\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;\n}\n\nexport class SessionStore {\n readonly id: string;\n readonly dir: string;\n\n private pendingMeta: { cwd: string; model: string; createdAt: number } | null = null;\n private initialized = false;\n private cachedMeta: SessionMeta | null = null;\n private metaTmpSeq = 0;\n private messagesTmpSeq = 0;\n private metaWriteQueue: Promise<void> = Promise.resolve();\n private metaLastFlushAt = 0;\n private metaFlushTimer: NodeJS.Timeout | null = null;\n private metaDirty = false;\n\n private constructor(id: string) {\n this.id = id;\n this.dir = path.join(sessionsDir(), id);\n }\n\n private metaPath(): string {\n return path.join(this.dir, \"meta.json\");\n }\n\n private messagesPath(): string {\n return path.join(this.dir, \"messages.jsonl\");\n }\n\n static async create(cwd: string, model: string): Promise<SessionStore> {\n const store = new SessionStore(generateId(new Date()));\n store.pendingMeta = { cwd, model, createdAt: Date.now() };\n return store;\n }\n\n static async open(id: string): Promise<SessionStore | null> {\n const store = new SessionStore(id);\n let raw: string;\n try {\n raw = await fs.readFile(store.metaPath(), \"utf8\");\n } catch {\n return null;\n }\n try {\n const meta = JSON.parse(raw) as SessionMeta;\n if (meta.id !== id) return null;\n } catch {\n // meta.json is corrupt (e.g. truncated write); open anyway and let\n // meta() fall back to defaults so messages stay resumable.\n debugWarn(`session ${id}: corrupt meta.json, using defaults`);\n }\n await store.sweepTmpFiles();\n store.initialized = true;\n return store;\n }\n\n static async list(cwd?: string): Promise<SessionMeta[]> {\n let entries: string[];\n try {\n entries = await fs.readdir(sessionsDir());\n } catch {\n return [];\n }\n const metas = await Promise.all(\n entries.map(async (entry) => {\n try {\n const raw = await fs.readFile(path.join(sessionsDir(), entry, \"meta.json\"), \"utf8\");\n const meta = JSON.parse(raw) as SessionMeta;\n if (meta.id === entry && (cwd === undefined || meta.cwd === cwd)) return meta;\n } catch {\n // 跳过损坏的会话目录\n }\n return null;\n }),\n );\n return metas\n .filter((meta): meta is SessionMeta => meta !== null)\n .sort((a, b) => b.updatedAt - a.updatedAt);\n }\n\n // Meta writes are serialized per instance and land atomically (tmp file +\n // rename) over a cached in-memory meta: the fire-and-forget title/usage\n // writers race the loop's own appends, and a truncated read once turned\n // fallbackMeta's empty defaults into a persisted wipe of model/cwd/usage.\n // A branded fallback meta is never written back for the same reason.\n // A write that still fails after retries is dropped rather than thrown:\n // meta.json is bookkeeping the cached meta rebuilds on the next write,\n // while the conversation itself lives in messages.jsonl — killing the\n // agent turn over a stale updatedAt would be the wrong trade.\n private writeMeta(meta: SessionMeta): Promise<void> {\n if (isFallbackMeta(meta)) return Promise.resolve();\n const tmp = `${this.metaPath()}.tmp-${process.pid}-${this.metaTmpSeq++}`;\n const run = this.metaWriteQueue.then(async () => {\n try {\n await withTransientFsRetry(async () => {\n await fs.writeFile(tmp, JSON.stringify(meta, null, 2));\n await fs.rename(tmp, this.metaPath());\n });\n } catch (error) {\n await fs.unlink(tmp).catch(() => {});\n debugWarn(\n `session ${this.id}: meta write failed (${(error as NodeJS.ErrnoException).code ?? String(error)}), skipping`,\n );\n }\n });\n this.metaWriteQueue = run.catch(() => {});\n return run;\n }\n\n // Debounced flush for append-driven updatedAt bumps. The trailing timer is\n // unref'd so a pending flush never keeps the process alive; close() flushes\n // synchronously on a clean exit, and a hard exit simply drops the trailing\n // bump — meta is bookkeeping.\n private markMetaDirty(): void {\n this.metaDirty = true;\n if (this.metaFlushTimer) return;\n const delay = Math.max(0, META_FLUSH_INTERVAL_MS - (Date.now() - this.metaLastFlushAt));\n if (delay === 0) {\n void this.flushMetaIfDirty();\n return;\n }\n this.metaFlushTimer = setTimeout(() => {\n this.metaFlushTimer = null;\n void this.flushMetaIfDirty();\n }, delay);\n this.metaFlushTimer.unref();\n }\n\n private async flushMetaIfDirty(): Promise<void> {\n if (!this.metaDirty) return;\n await this.flushMetaNow();\n }\n\n // Immediate flush for substantive meta changes; cancels a pending debounced\n // flush, which the write makes redundant either way (it serializes the live\n // cached meta).\n private async flushMetaNow(): Promise<void> {\n if (this.metaFlushTimer) {\n clearTimeout(this.metaFlushTimer);\n this.metaFlushTimer = null;\n }\n this.metaDirty = false;\n this.metaLastFlushAt = Date.now();\n if (this.cachedMeta) await this.writeMeta(this.cachedMeta);\n }\n\n // Flushes a pending debounced meta write and waits for in-flight writes to\n // settle; call when the store is dropped so a clean exit keeps the trailing\n // updatedAt bump.\n async close(): Promise<void> {\n await this.flushMetaIfDirty();\n await this.metaWriteQueue;\n }\n\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) return;\n const pending = this.pendingMeta;\n if (!pending) {\n this.initialized = true;\n return;\n }\n // Session directories hold file contents (checkpoints); keep them private\n // on shared machines. The mode is a no-op on Windows.\n await fs.mkdir(this.dir, { recursive: true, mode: 0o700 });\n await this.sweepTmpFiles();\n const meta: SessionMeta = {\n id: this.id,\n title: \"\",\n model: pending.model,\n cwd: pending.cwd,\n createdAt: pending.createdAt,\n updatedAt: pending.createdAt,\n };\n await this.writeMeta(meta);\n this.cachedMeta = meta;\n this.metaLastFlushAt = Date.now();\n this.initialized = true;\n }\n\n // Removes tmp orphans left behind by crashed atomic writes (both meta.json\n // and messages.jsonl write tmp file + rename); best-effort, the directory\n // may not even exist yet.\n private async sweepTmpFiles(): Promise<void> {\n let entries: string[];\n try {\n entries = await fs.readdir(this.dir);\n } catch {\n return;\n }\n await Promise.all(\n entries\n .filter(\n (entry) => entry.startsWith(\"meta.json.tmp-\") || entry.startsWith(\"messages.jsonl.tmp-\"),\n )\n .map((entry) => fs.unlink(path.join(this.dir, entry)).catch(() => {})),\n );\n }\n\n async append(message: CoreMessage): Promise<void> {\n await this.ensureInitialized();\n // Appends get the same transient-failure retries as meta writes, and a\n // failure that outlasts them degrades to a warning instead of throwing:\n // the agent loop awaits append without a catch, so one AV-locked file\n // must not kill the whole turn. The message still lives in the loop's\n // in-memory history and later appends keep working; only this line is\n // missing from messages.jsonl after a resume.\n try {\n await withTransientFsRetry(() =>\n fs.appendFile(this.messagesPath(), `${JSON.stringify(message)}\\n`),\n );\n } catch (error) {\n warn(\n `session ${this.id}: message append failed (${(error as NodeJS.ErrnoException).code ?? String(error)}), message kept in memory only`,\n );\n }\n const meta = await this.meta();\n meta.updatedAt = Date.now();\n this.markMetaDirty();\n }\n\n async replaceMessages(messages: CoreMessage[]): Promise<void> {\n await this.ensureInitialized();\n const content = messages.map((message) => JSON.stringify(message)).join(\"\\n\");\n // tmp + rename like writeMeta: messages.jsonl is the session's only\n // durable record, and a crash mid-writeFile must not leave it truncated.\n // A rewrite that fails past its retries is dropped with a warning rather\n // than thrown — the previous history stays intact on disk, which is the\n // safer side to land on.\n const tmp = `${this.messagesPath()}.tmp-${process.pid}-${this.messagesTmpSeq++}`;\n try {\n await withTransientFsRetry(async () => {\n await fs.writeFile(tmp, content ? `${content}\\n` : \"\");\n await fs.rename(tmp, this.messagesPath());\n });\n } catch (error) {\n await fs.unlink(tmp).catch(() => {});\n warn(\n `session ${this.id}: history rewrite failed (${(error as NodeJS.ErrnoException).code ?? String(error)}), kept the previous messages.jsonl`,\n );\n }\n const meta = await this.meta();\n meta.updatedAt = Date.now();\n await this.flushMetaNow();\n }\n\n async messages(): Promise<CoreMessage[]> {\n // Read in fixed-size chunks through a StringDecoder instead of buffering\n // the whole file: a resume used to hold the raw string, the split line\n // array and the parsed objects at once (3-4x the file size at peak). The\n // parsed history is now the only large allocation. Corrupt lines are\n // still skipped; an unreadable file still yields an empty history.\n let handle: fs.FileHandle | null = null;\n try {\n handle = await fs.open(this.messagesPath(), \"r\");\n const messages: CoreMessage[] = [];\n const decoder = new StringDecoder(\"utf8\");\n const buffer = Buffer.alloc(MESSAGES_READ_CHUNK_BYTES);\n let tail = \"\";\n const pushLine = (line: string) => {\n const trimmed = line.trim();\n if (!trimmed) return;\n try {\n messages.push(JSON.parse(trimmed) as CoreMessage);\n } catch {\n debugWarn(`session ${this.id}: skipping corrupt messages.jsonl line`);\n }\n };\n while (true) {\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);\n if (bytesRead === 0) break;\n const chunk = tail + decoder.write(buffer.subarray(0, bytesRead));\n const lines = chunk.split(\"\\n\");\n tail = lines.pop() ?? \"\";\n for (const line of lines) pushLine(line);\n }\n pushLine(tail + decoder.end());\n return messages;\n } catch {\n return [];\n } finally {\n await handle?.close().catch(() => {});\n }\n }\n\n private fallbackMeta(): SessionMeta {\n const now = Date.now();\n const meta: SessionMeta = {\n id: this.id,\n title: \"\",\n model: \"\",\n cwd: \"\",\n createdAt: now,\n updatedAt: now,\n };\n // Non-enumerable, so it neither shows up in JSON.stringify nor spreads.\n Object.defineProperty(meta, FALLBACK_META, { value: true, enumerable: false });\n return meta;\n }\n\n async meta(): Promise<SessionMeta> {\n await this.ensureInitialized();\n if (this.cachedMeta) return this.cachedMeta;\n try {\n const raw = await fs.readFile(this.metaPath(), \"utf8\");\n this.cachedMeta = JSON.parse(raw) as SessionMeta;\n return this.cachedMeta;\n } catch {\n // The fallback is neither cached nor written back: a transient read\n // failure must not turn into a persisted wipe of the real meta.\n debugWarn(`session ${this.id}: unreadable meta.json, using defaults`);\n return this.fallbackMeta();\n }\n }\n\n async setTitle(title: string): Promise<void> {\n await this.ensureInitialized();\n const meta = await this.meta();\n meta.title = title;\n meta.updatedAt = Date.now();\n await this.flushMetaNow();\n }\n\n // /model swaps the live loop mid-session; keep the recorded model in sync\n // or the session list and /usage attribute the whole session to whichever\n // model happened to be active at creation.\n async setModel(model: string): Promise<void> {\n await this.ensureInitialized();\n const meta = await this.meta();\n meta.model = model;\n meta.updatedAt = Date.now();\n await this.flushMetaNow();\n }\n\n async appendCheckpoint(record: CheckpointRecord, content: string | null): Promise<void> {\n await this.ensureInitialized();\n await appendCheckpointRecord(this.dir, record, content);\n }\n\n async listCheckpoints(): Promise<CheckpointRecord[]> {\n return listCheckpointRecords(this.dir);\n }\n\n async removeCheckpoints(ids: number[]): Promise<void> {\n await removeCheckpointRecords(this.dir, ids);\n }\n\n async addUsage(delta: {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n cachedPromptTokens?: number;\n cacheReadInputTokens?: number;\n }): Promise<void> {\n await this.ensureInitialized();\n const meta = await this.meta();\n const usage = meta.usage ?? {\n requests: 0,\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n };\n usage.requests += 1;\n usage.promptTokens += delta.promptTokens;\n usage.completionTokens += delta.completionTokens;\n usage.totalTokens += delta.totalTokens;\n if (delta.cachedPromptTokens) {\n usage.cachedPromptTokens = (usage.cachedPromptTokens ?? 0) + delta.cachedPromptTokens;\n }\n if (delta.cacheReadInputTokens) {\n usage.cacheReadInputTokens = (usage.cacheReadInputTokens ?? 0) + delta.cacheReadInputTokens;\n }\n meta.usage = usage;\n const day = dayKey(new Date());\n const byDay = meta.usageByDay ?? {};\n const bucket = byDay[day] ?? { promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n bucket.promptTokens += delta.promptTokens;\n bucket.completionTokens += delta.completionTokens;\n bucket.totalTokens += delta.totalTokens;\n byDay[day] = bucket;\n meta.usageByDay = byDay;\n await this.flushMetaNow();\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { sessionsDir } from \"../config/paths\";\nimport { pruneTreeReposExcept, removeTreeRepo } from \"../snapshot/git-tree\";\nimport { SessionStore } from \"./store\";\n\n// Deletes stored sessions and returns how many were removed. With a cwd only\n// sessions recorded for that directory go (directories with an unreadable\n// meta.json cannot be matched and stay); without it every session directory\n// under sessionsDir is removed. excludeId keeps the caller's live session on\n// disk (the REPL's /clear-sessions must not pull its own store away).\n//\n// Snapshot cleanup rides along: per-file checkpoints live inside the session\n// dir and go with it, while the per-project git-tree repos outlive sessions\n// on purpose (/undo across restarts). Once a project's last session is gone\n// its tree repo is unreachable, so it is removed here too — repos whose\n// project still has a surviving session (e.g. the excluded live one) stay.\nexport async function clearSessions(cwd?: string, excludeId?: string): Promise<number> {\n if (cwd !== undefined) {\n const metas = await SessionStore.list(cwd);\n let removed = 0;\n for (const meta of metas) {\n if (meta.id === excludeId) continue;\n await fs.rm(path.join(sessionsDir(), meta.id), { recursive: true, force: true });\n removed += 1;\n }\n if ((await SessionStore.list(cwd)).length === 0) {\n await removeTreeRepo(cwd);\n }\n return removed;\n }\n let entries: string[];\n try {\n entries = await fs.readdir(sessionsDir());\n } catch {\n return 0;\n }\n let removed = 0;\n for (const entry of entries) {\n if (entry === excludeId) continue;\n await fs.rm(path.join(sessionsDir(), entry), { recursive: true, force: true });\n removed += 1;\n }\n const survivors = await SessionStore.list();\n await pruneTreeReposExcept(survivors.map((meta) => meta.cwd));\n return removed;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { sessionsDir } from \"../config/paths\";\nimport type { CoreMessage } from \"../core/messages\";\nimport { type SessionMeta, SessionStore } from \"./store\";\n\nexport interface SessionListEntry {\n meta: SessionMeta;\n messageCount: number;\n}\n\n// Counts newline-terminated records in fixed-size chunks instead of reading\n// the whole file: listSessionEntries runs this for every session, so\n// `star -r` and the /resume picker used to load every full history just for\n// the row's message count. \"\\n\" (0x0A) never appears inside a multi-byte\n// UTF-8 sequence, so a raw byte scan is safe. Lines are written as compact\n// JSON (no raw whitespace), and a blank or whitespace-only fragment is not\n// counted — same semantics as the old readFile + split counter.\nconst COUNT_CHUNK_BYTES = 64 * 1024;\n\nasync function countMessages(id: string): Promise<number> {\n let handle: fs.FileHandle | null = null;\n try {\n handle = await fs.open(path.join(sessionsDir(), id, \"messages.jsonl\"), \"r\");\n const buffer = Buffer.alloc(COUNT_CHUNK_BYTES);\n let count = 0;\n let lineHasContent = false;\n while (true) {\n const { bytesRead } = await handle.read(buffer, 0, COUNT_CHUNK_BYTES, null);\n if (bytesRead === 0) break;\n for (const byte of buffer.subarray(0, bytesRead)) {\n if (byte === 0x0a) {\n if (lineHasContent) count += 1;\n lineHasContent = false;\n } else if (byte !== 0x0d && byte !== 0x20 && byte !== 0x09) {\n lineHasContent = true;\n }\n }\n }\n if (lineHasContent) count += 1;\n return count;\n } catch {\n return 0;\n } finally {\n await handle?.close().catch(() => {});\n }\n}\n\nexport async function listSessionEntries(cwd?: string): Promise<SessionListEntry[]> {\n const metas = await SessionStore.list(cwd);\n return Promise.all(\n metas.map(async (meta) => ({ meta, messageCount: await countMessages(meta.id) })),\n );\n}\n\nexport async function findLatestSession(cwd: string): Promise<SessionMeta | null> {\n const [latest] = await SessionStore.list(cwd);\n return latest ?? null;\n}\n\n// Reads only the head of messages.jsonl and extracts the first user message's\n// text, whitespace-collapsed onto one line — the /resume picker needs a\n// preview per session and must not parse every full history for it. The final\n// partial line at the read boundary and any corrupt lines are skipped.\n// Returns null when no user text is found (or the file is unreadable).\nexport async function sessionPreview(id: string, maxBytes = 4096): Promise<string | null> {\n let handle: fs.FileHandle | null = null;\n try {\n handle = await fs.open(path.join(sessionsDir(), id, \"messages.jsonl\"), \"r\");\n const buffer = Buffer.alloc(maxBytes);\n const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);\n const head = buffer.toString(\"utf8\", 0, bytesRead);\n for (const line of head.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const message = JSON.parse(trimmed) as CoreMessage;\n if (message.role !== \"user\") continue;\n const text =\n typeof message.content === \"string\"\n ? message.content\n : message.content\n .filter((part) => part.type === \"text\")\n .map((part) => (part.type === \"text\" ? part.text : \"\"))\n .join(\" \");\n const collapsed = text.replace(/\\s+/g, \" \").trim();\n if (collapsed) return collapsed;\n } catch {\n // Half line at the read boundary or a corrupt line — keep looking.\n }\n }\n return null;\n } catch {\n return null;\n } finally {\n await handle?.close().catch(() => {});\n }\n}\n\nexport function shortSessionId(id: string): string {\n const dash = id.lastIndexOf(\"-\");\n return dash === -1 ? id : id.slice(dash + 1);\n}\n\nexport async function resolveSessionId(query: string): Promise<string | null> {\n const metas = await SessionStore.list();\n const exact = metas.find((meta) => meta.id === query);\n if (exact) return exact.id;\n const matches = metas.filter(\n (meta) => shortSessionId(meta.id) === query || meta.id.startsWith(query),\n );\n return matches.length === 1 ? (matches[0]?.id ?? null) : null;\n}\n\nexport function relativeTime(timestamp: number): string {\n const diff = Date.now() - timestamp;\n const minutes = Math.floor(diff / 60_000);\n if (minutes < 1) return \"just now\";\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n if (days < 30) return `${days}d ago`;\n const months = Math.floor(days / 30);\n if (months < 12) return `${months}mo ago`;\n return `${Math.floor(months / 12)}y ago`;\n}\n\nexport function formatSessionEntries(\n entries: SessionListEntry[],\n options: { showCwd?: boolean } = {},\n): string {\n return entries\n .map(({ meta, messageCount }) => {\n const title = meta.title || \"(untitled)\";\n const base =\n `${shortSessionId(meta.id)} ${title} ` +\n `${messageCount} messages updated ${relativeTime(meta.updatedAt)}`;\n return options.showCwd ? `${base} [${meta.cwd}]` : base;\n })\n .join(\"\\n\");\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nfunction readPackageVersion(): string {\n try {\n const pkgPath = path.join(path.dirname(fileURLToPath(import.meta.url)), \"..\", \"package.json\");\n const pkg: unknown = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n if (typeof pkg === \"object\" && pkg !== null) {\n const version = (pkg as { version?: unknown }).version;\n if (typeof version === \"string\") return version;\n }\n } catch {\n // fall through to placeholder\n }\n return \"0.0.0\";\n}\n\nexport const VERSION = readPackageVersion();\n","import fs from \"node:fs\";\nimport { envFilePath } from \"./paths\";\n\nconst ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n// Parses dotenv-style KEY=VALUE lines; blank lines and # comments are\n// skipped, surrounding quotes on the value are stripped.\nexport function parseEnvContent(content: string): Record<string, string> {\n const vars: Record<string, string> = {};\n for (const rawLine of content.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const eq = line.indexOf(\"=\");\n if (eq <= 0) continue;\n const key = line.slice(0, eq).trim();\n if (!ENV_KEY_PATTERN.test(key)) continue;\n let value = line.slice(eq + 1).trim();\n if (\n value.length >= 2 &&\n ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\")))\n ) {\n value = value.slice(1, -1);\n }\n vars[key] = value;\n }\n return vars;\n}\n\n// Fills process.env from ~/.star-cli/.env. Variables already set in the real\n// environment always win — the file only supplies missing values. Never\n// throws: a missing or unreadable file means no keys.\nexport function loadEnvFile(filePath: string = envFilePath()): void {\n let content: string;\n try {\n content = fs.readFileSync(filePath, \"utf8\");\n } catch {\n return;\n }\n for (const [key, value] of Object.entries(parseEnvContent(content))) {\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n\n// Returns content with key set to value: the existing KEY= line is replaced\n// in place (comments and other variables untouched), otherwise a new line is\n// appended.\nexport function upsertEnvContent(content: string, key: string, value: string): string {\n const entry = `${key}=${value.replace(/[\\r\\n]/g, \"\")}`;\n const pattern = new RegExp(`^\\\\s*${key}\\\\s*=`);\n const lines = content.split(\"\\n\");\n while (lines.length > 0 && (lines[lines.length - 1] ?? \"\").trim() === \"\") lines.pop();\n for (let i = 0; i < lines.length; i++) {\n if (pattern.test(lines[i] ?? \"\")) {\n lines[i] = entry;\n return `${lines.join(\"\\n\")}\\n`;\n }\n }\n lines.push(entry);\n return `${lines.join(\"\\n\")}\\n`;\n}\n"],"mappings":";;;;;;;;;;;;;AAYO,SAAS,cAAc,MAAsB;AAClD,MAAI,IAAI,KAAK,QAAQ,CAAC,EAAE,QAAQ,OAAO,EAAE;AACzC,MAAI,EAAE,SAAS,GAAG,GAAG;AACnB,SAAK;AAAA,EACP,WAAW,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,WAAW,GAAG;AACxC,SAAK;AAAA,EACP;AACA,SAAO;AACT;AAKO,SAAS,aAAa,GAAmB;AAC9C,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO,OAAO,CAAC;AACxC,MAAI,IAAI,IAAM,QAAO,OAAO,CAAC;AAC7B,MAAI,IAAI,IAAW,QAAO,IAAI,IAAI,KAAM,QAAQ,CAAC,CAAC;AAClD,SAAO,IAAI,IAAI,KAAW,QAAQ,CAAC,CAAC;AACtC;AAQO,SAAS,eAAe,OAAmB,OAA+C;AAC/F,MAAI,OAAO,gBAAgB,UAAa,MAAM,oBAAoB,QAAW;AAC3E,WAAO;AAAA,EACT;AACA,QAAM,SAAS,KAAK,IAAI,KAAK,IAAI,MAAM,sBAAsB,GAAG,CAAC,GAAG,MAAM,YAAY;AACtF,QAAM,aACH,MAAM,eAAe,UAAU,MAAM,cACtC,UAAU,MAAM,kBAAkB,MAAM,gBACvC,MAAM,wBAAwB,MAAM,MAAM,kBAAkB;AAC/D,UAAQ,YAAY,MAAM,mBAAmB,MAAM,mBAAmB;AACxE;AAEO,SAAS,aACd,OACA,WACA,OACQ;AACR,QAAM,OAAO,eAAe,OAAO,KAAK;AACxC,MAAI,SAAS,MAAM;AACjB,QAAI,UAAU,QAAW;AACvB,YAAM,UAAU;AAAA,QACd,MAAM,gBAAgB,SAAY,gBAAgB;AAAA,QAClD,MAAM,oBAAoB,SAAY,oBAAoB;AAAA,MAC5D,EAAE,OAAO,CAAC,MAAM,MAAM,IAAI;AAC1B,UAAI,QAAQ,WAAW,GAAG;AACxB,eAAO,4BAA4B,MAAM,IAAI,eAAe,QAAQ,CAAC,CAAC;AAAA,MACxE;AAAA,IACF;AACA,WAAO,oDAAoD,SAAS;AAAA,EACtE;AACA,QAAM,aACJ,OAAO,mBAAmB,SAAY,MAAM,MAAM,cAAc,kBAAkB;AACpF,SAAO,oBAAoB,cAAc,IAAI,CAAC,KAAK,OAAO,IAAI,OAAO,OAAO,WAAW,eAAe,OAAO,eAAe,gBAAgB,UAAU;AACxJ;;;AC7DA,IAAM,eAAe;AAErB,IAAM,gBAAgB;AACtB,IAAM,OAAO;AAEN,SAAS,eAAe,MAAsB;AACnD,MAAI,CAAC,aAAa,KAAK,IAAI,EAAG,QAAO;AACrC,SAAO,KAAK,QAAQ,MAAM,IAAI,EAAE,QAAQ,eAAe,EAAE;AAC3D;;;ACJA,IAAM,MAAM;AACZ,IAAM,QAAQ,GAAG,GAAG;AAEpB,SAAS,OAAO,MAAc,MAAc,MAAsB;AAChE,QAAM,SAAS,OAAO,GAAG,GAAG,GAAG,IAAI,MAAM;AACzC,SAAO,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,GAAG,MAAM;AAC/C;AAEA,IAAM,OAAO,CAAC,GAAW,MAAc,OAAO,KAAK,GAAG,CAAC;AACvD,IAAM,MAAM,CAAC,GAAW,MAAc,OAAO,KAAK,GAAG,CAAC;AACtD,IAAM,SAAS,CAAC,GAAW,MAAc,OAAO,KAAK,GAAG,CAAC;AACzD,IAAM,YAAY,CAAC,GAAW,MAAc,OAAO,KAAK,GAAG,CAAC;AAC5D,IAAM,SAAS,CAAC,GAAW,MAAc,OAAO,KAAK,GAAG,CAAC;AACzD,IAAM,SAAS,CAAC,GAAW,MAAc,OAAO,MAAM,GAAG,CAAC;AAC1D,IAAM,WAAW,CAAC,GAAW,MAAc,OAAO,QAAQ,GAAG,CAAC;AAKvD,SAAS,UAAU,MAAsB;AAC9C,MAAI,QAAQ;AACZ,aAAW,MAAM,MAAM;AACrB,UAAM,KAAK,GAAG,YAAY,CAAC,KAAK;AAChC,QAAK,MAAM,OAAU,MAAM,OAAW,OAAO,QAAW,MAAM,SAAU,MAAM,OAAS;AACrF;AAAA,IACF;AACA,QACG,MAAM,QAAU,MAAM,QACtB,MAAM,SAAU,MAAM,SACtB,MAAM,SAAU,MAAM,SACtB,MAAM,SAAU,MAAM,SACtB,MAAM,SAAU,MAAM,SACtB,MAAM,SAAU,MAAM,SACtB,MAAM,SAAU,MAAM,SACtB,MAAM,SAAU,MAAM,SACtB,MAAM,UAAW,MAAM,UACvB,MAAM,UAAW,MAAM,QACxB;AACA,eAAS;AAAA,IACX,OAAO;AACL,eAAS;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,WAAW;AACjB,IAAM,UAAU;AAChB,IAAM,UAAU;AAChB,IAAM,YAAY;AAClB,IAAM,iBAAiB;AAEvB,IAAM,uBAAuB;AAItB,SAAS,aAAa,MAAc,OAAO,IAAY;AAC5D,SAAO,KACJ,MAAM,aAAa,EACnB,IAAI,CAAC,SAAS,UAAU;AAEvB,QAAI,QAAQ,MAAM,EAAG,QAAO,OAAO,SAAS,IAAI;AAChD,WAAO,QACJ,QAAQ,UAAU,CAAC,IAAI,QAAgB,IAAI,WAAW,GAAG,KAAK,IAAI,CAAC,EACnE;AAAA,MACC;AAAA,MACA,CAAC,IAAI,OAAe,QAAgB,GAAG,UAAU,OAAO,IAAI,CAAC,GAAG,IAAI,KAAK,GAAG,KAAK,IAAI,CAAC;AAAA,IACxF,EACC,QAAQ,SAAS,CAAC,IAAI,MAAc,UAAkB,KAAK,QAAQ,OAAO,IAAI,CAAC,EAC/E,QAAQ,WAAW,CAAC,IAAI,MAAc,OAAO,GAAG,IAAI,CAAC,EACrD,QAAQ,gBAAgB,CAAC,IAAI,MAAc,OAAO,GAAG,IAAI,CAAC,EAC1D,QAAQ,sBAAsB,CAAC,IAAI,KAAa,MAAc,MAAM,OAAO,GAAG,IAAI,CAAC;AAAA,EACxF,CAAC,EACA,KAAK,EAAE;AACZ;AAEA,IAAM,aAAa;AACnB,IAAM,QAAQ;AACd,IAAM,WAAW;AACjB,IAAM,WAAW;AACjB,IAAM,0BAA0B;AAEhC,SAAS,iBAAiB,MAAuB;AAC/C,QAAM,QAAQ,cAAc,IAAI;AAChC,SACE,UAAU,QACV,MAAM,SAAS,KACf,MAAM,MAAM,CAAC,SAAS,wBAAwB,KAAK,KAAK,KAAK,CAAC,CAAC;AAEnE;AAEA,SAAS,cAAc,MAA+B;AACpD,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AACnC,QAAM,WAAW,QAAQ,QAAQ,OAAO,EAAE,EAAE,QAAQ,OAAO,EAAE;AAC7D,SAAO,SAAS,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AACtD;AAEA,IAAM,mBAAmB;AAIzB,SAAS,SAAS,MAAc,OAAyB;AACvD,QAAM,QAAkB,CAAC;AACzB,MAAI,UAAU;AACd,MAAI,eAAe;AACnB,aAAW,MAAM,MAAM;AACrB,UAAM,IAAI,UAAU,EAAE;AACtB,QAAI,eAAe,KAAK,eAAe,IAAI,OAAO;AAChD,YAAM,YAAY,QAAQ,YAAY,GAAG;AACzC,UAAI,YAAY,KAAK,OAAO,KAAK;AAC/B,cAAM,KAAK,QAAQ,MAAM,GAAG,SAAS,CAAC;AACtC,kBAAU,QAAQ,MAAM,YAAY,CAAC,IAAI;AAAA,MAC3C,OAAO;AACL,cAAM,KAAK,OAAO;AAClB,kBAAU,OAAO,MAAM,KAAK;AAAA,MAC9B;AACA,qBAAe,UAAU,OAAO;AAAA,IAClC,OAAO;AACL,iBAAW;AACX,sBAAgB;AAAA,IAClB;AAAA,EACF;AACA,MAAI,QAAQ,SAAS,KAAK,MAAM,WAAW,EAAG,OAAM,KAAK,OAAO;AAChE,SAAO;AACT;AAEA,SAAS,YAAY,OAAiB,MAAc,UAA4B;AAC9E,QAAM,OAAmB,CAAC;AAC1B,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,cAAc,IAAI;AAChC,QAAI,MAAO,MAAK,KAAK,KAAK;AAAA,EAC5B;AACA,MAAI,KAAK,SAAS,EAAG,QAAO;AAC5B,QAAM,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC;AAI7D,QAAM,SAAS,MAAM;AAAA,IAAK,EAAE,QAAQ,YAAY;AAAA,IAAG,CAAC,GAAG,WACrD,KAAK,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC,QAAQ,UAAU,IAAI,MAAM,KAAK,EAAE,CAAC,CAAC;AAAA,EAChE;AAEA,QAAM,aAAa,MACjB,IAAI,OAAO,OAAO,CAAC,KAAK,UAAU,MAAM,OAAO,CAAC,IAAI,KAAK,cAAc;AACzE,SAAO,WAAW,IAAI,UAAU;AAC9B,UAAM,SAAS,OAAO,QAAQ,KAAK,IAAI,GAAG,MAAM,CAAC;AACjD,QAAI,OAAO,MAAM,MAAM,UAAa,OAAO,MAAM,KAAK,iBAAkB;AACxE,WAAO,MAAM,KAAK;AAAA,EACpB;AAGA,QAAM,SAAS,IAAI,IAAI,UAAK,IAAI,CAAC;AACjC,QAAM,YAAY,CAAC,KAAe,UAA8C;AAC9E,UAAM,UAAU,OAAO,IAAI,CAAC,OAAO,WAAW,SAAS,IAAI,MAAM,KAAK,IAAI,KAAK,CAAC;AAChF,UAAM,SAAS,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC,SAAS,KAAK,MAAM,CAAC;AAC7D,UAAM,MAAgB,CAAC;AACvB,aAAS,OAAO,GAAG,OAAO,QAAQ,QAAQ;AACxC,UAAI;AAAA,QACF,KAAK,OACF,IAAI,CAAC,OAAO,WAAW;AACtB,gBAAM,MAAM,QAAQ,MAAM,IAAI,IAAI,KAAK;AACvC,iBAAO,MAAM,GAAG,IAAI,IAAI,OAAO,KAAK,IAAI,GAAG,QAAQ,UAAU,GAAG,CAAC,CAAC;AAAA,QACpE,CAAC,EACA,KAAK,MAAM,EACX,QAAQ,CAAC;AAAA,MACd;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,QAAM,YAAY,IAAI,KAAK,OAAO,IAAI,CAAC,UAAU,SAAI,OAAO,KAAK,CAAC,EAAE,KAAK,oBAAK,CAAC,IAAI,IAAI;AACvF,QAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,IAAI;AAC5B,SAAO;AAAA,IACL,GAAG,UAAU,QAAQ,CAAC,SAAS,KAAK,MAAM,IAAI,CAAC;AAAA,IAC/C;AAAA,IACA,GAAG,KAAK,QAAQ,CAAC,KAAK,UAAU;AAC9B,YAAMA,SAAQ,UAAU,KAAK,CAAC,SAAS,aAAa,MAAM,IAAI,CAAC;AAC/D,aAAO,QAAQ,KAAK,SAAS,IAAI,CAAC,GAAGA,QAAO,SAAS,IAAIA;AAAA,IAC3D,CAAC;AAAA,EACH;AACF;AAQO,SAAS,qBAAqB,OAAyB;AAC5D,MAAI,UAAU;AACd,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC,KAAK;AACzB,QAAI,SAAS,KAAK,IAAI,EAAG,WAAU,CAAC;AACpC,UAAM,SAAS,KAAK,SAAS,GAAG;AAEhC,UAAM,aAAa,IAAI,KAAK,MAAM,WAAW,MAAM,IAAI,CAAC,KAAK,IAAI,SAAS,GAAG;AAC7E,QAAI,CAAC,WAAW,EAAE,UAAU,YAAa,QAAO,IAAI;AAAA,EACtD;AACA,SAAO;AACT;AAKO,SAAS,eAAe,MAAc,OAAO,IAAI,WAAW,IAAY;AAC7E,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,QAAM,MAAgB,CAAC;AACvB,MAAI,UAAU;AACd,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC,KAAK;AACzB,QAAI,SAAS;AACX,UAAI,SAAS,KAAK,IAAI,GAAG;AACvB,kBAAU;AAAA,MACZ,OAAO;AACL,YAAI,KAAK,GAAG,IAAI,YAAO,IAAI,CAAC,GAAG,IAAI,EAAE;AAAA,MACvC;AACA;AAAA,IACF;AACA,UAAM,QAAQ,SAAS,KAAK,IAAI;AAChC,QAAI,OAAO;AACT,gBAAU;AACV,YAAM,OAAO,KACV,KAAK,EACL,MAAM,MAAM,CAAC,GAAG,UAAU,CAAC,EAC3B,KAAK;AACR,UAAI,KAAM,KAAI,KAAK,IAAI,YAAO,IAAI,IAAI,IAAI,CAAC;AAC3C;AAAA,IACF;AACA,UAAM,UAAU,WAAW,KAAK,IAAI;AACpC,QAAI,SAAS;AACX,UAAI,KAAK,SAAS,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC;AACzC;AAAA,IACF;AACA,QAAI,MAAM,KAAK,IAAI,GAAG;AACpB,UAAI,KAAK,IAAI,sDAAc,IAAI,CAAC;AAChC;AAAA,IACF;AACA,UAAM,QAAQ,SAAS,KAAK,IAAI;AAChC,QAAI,OAAO;AACT,UAAI,KAAK,GAAG,IAAI,YAAO,IAAI,CAAC,GAAG,OAAO,aAAa,MAAM,CAAC,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC,EAAE;AACjF;AAAA,IACF;AAEA,UAAM,cAAc,cAAc,IAAI;AACtC,QAAI,eAAe,IAAI,IAAI,MAAM,UAAU,iBAAiB,MAAM,IAAI,CAAC,KAAK,EAAE,GAAG;AAC/E,YAAM,QAAQ,CAAC,IAAI;AACnB,UAAI,IAAI,IAAI;AACZ,aAAO,IAAI,MAAM,UAAU,cAAc,MAAM,CAAC,KAAK,EAAE,MAAM,MAAM;AACjE,cAAM,KAAK,MAAM,CAAC,KAAK,EAAE;AACzB,aAAK;AAAA,MACP;AACA,UAAI,KAAK,GAAG,YAAY,OAAO,MAAM,QAAQ,CAAC;AAC9C,UAAI,IAAI;AACR;AAAA,IACF;AACA,QAAI,KAAK,aAAa,MAAM,IAAI,CAAC;AAAA,EACnC;AACA,SAAO,IAAI,KAAK,IAAI;AACtB;;;AC3QO,SAAS,cAAc,MAAe,YAAY,KAAa;AACpE,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,UAAU,IAAI,KAAK,OAAO,IAAI;AAAA,EAC5C,QAAQ;AACN,WAAO,OAAO,IAAI;AAAA,EACpB;AACA,SAAO,KAAK,SAAS,YAAY,GAAG,KAAK,MAAM,GAAG,SAAS,CAAC,QAAQ;AACtE;AAEO,SAAS,aAAa,MAAc,WAAW,IAA0C;AAC9F,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,MAAI,MAAM,UAAU,SAAU,QAAO,EAAE,MAAM,WAAW,MAAM;AAC9D,SAAO,EAAE,MAAM,MAAM,MAAM,GAAG,QAAQ,EAAE,KAAK,IAAI,GAAG,WAAW,KAAK;AACtE;AAEO,SAAS,gBAAgB,OAAuB;AACrD,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK;AACjC,MAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AAC5D,SAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AAC9C;AAIO,SAAS,qBAAqB,SAAyB;AAC5D,QAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;AAC7C,MAAI,QAAQ,GAAI,QAAO,GAAG,KAAK;AAC/B,QAAM,UAAU,KAAK,MAAM,QAAQ,EAAE;AACrC,QAAM,OAAO,QAAQ;AACrB,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO,IAAI,IAAI;AAC3C,SAAO,GAAG,KAAK,MAAM,UAAU,EAAE,CAAC,IAAI,UAAU,EAAE,IAAI,IAAI;AAC5D;AAQO,SAAS,sBACd,MACA,kBAC4C;AAC5C,QAAM,cAAc,KAAK,YAAY,IAAI;AACzC,MAAI,cAAc,EAAG,QAAO;AAC5B,QAAM,WAAW,KAAK,MAAM,GAAG,WAAW;AAC1C,QAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,QAAM,QAAQ,qBAAqB,KAAK;AACxC,MAAI,QAAQ,iBAAkB,QAAO;AACrC,QAAM,YAAY,MAAM,MAAM,GAAG,KAAK,EAAE,KAAK,IAAI;AACjD,SAAO,EAAE,WAAW,MAAM,KAAK,MAAM,UAAU,SAAS,CAAC,EAAE;AAC7D;AAEO,SAAS,kBAAkB,OAAsB;AACtD,MAAI,MAAM,QAAQ,SAAS,8BAA8B,GAAG;AAC1D,WAAO,GAAG,MAAM,OAAO;AAAA,EACzB;AACA,SAAO,MAAM;AACf;AAEO,SAAS,gBAAgB,SAA8B;AAC5D,QAAM,UAAU,QAAQ;AACxB,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,WAAO,QACJ,OAAO,CAAC,SAAS,KAAK,SAAS,MAAM,EACrC,IAAI,CAAC,SAAU,UAAU,OAAO,KAAK,OAAO,EAAG,EAC/C,KAAK,GAAG;AAAA,EACb;AACA,SAAO;AACT;AAEO,SAAS,qBAAqB,UAA2C;AAC9E,QAAM,UAA4B,CAAC;AACnC,MAAI,YAAY;AAChB,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,SAAS,UAAU,QAAQ,SAAS,aAAa;AAC3D,YAAM,OAAO,gBAAgB,OAAO;AACpC,UAAI,MAAM;AAGR,gBAAQ,KAAK,EAAE,IAAI,QAAQ,QAAQ,MAAM,QAAQ,MAAM,MAAM,eAAe,IAAI,EAAE,CAAC;AAAA,MACrF,OAAO;AACL;AAAA,MACF;AAAA,IACF,WAAW,QAAQ,SAAS,QAAQ;AAClC;AAAA,IACF;AAAA,EACF;AACA,MAAI,YAAY,GAAG;AACjB,YAAQ,KAAK;AAAA,MACX,IAAI,QAAQ;AAAA,MACZ,MAAM;AAAA,MACN,MAAM,YAAY,SAAS;AAAA,IAC7B,CAAC;AAAA,EACH;AACA,SAAO;AACT;;;ACtGA,SAAS,UAAU,SAAS,YAAY;AACxC,OAAO,UAAU;AAUV,IAAM,oBAAoB,MAAM;AAChC,IAAM,kBAAkB,IAAI,OAAO;AACnC,IAAM,0BAA0B;AAChC,IAAM,wBAAwB;AAI9B,IAAM,wBAAwB;AAErC,IAAM,mBAA2C;AAAA,EAC/C,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AACX;AAEO,SAAS,cAAc,UAAiC;AAC7D,SAAO,iBAAiB,KAAK,QAAQ,QAAQ,EAAE,YAAY,CAAC,KAAK;AACnE;AAKA,eAAsB,eAAe,UAAkB,KAAyC;AAC9F,QAAM,WAAW,cAAc,QAAQ;AACvC,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,MAAM,KAAK,QAAQ,KAAK,QAAQ;AACtC,QAAM,KAAK,MAAM,KAAK,GAAG,EAAE,MAAM,MAAM,IAAI;AAC3C,MAAI,CAAC,MAAM,CAAC,GAAG,OAAO,KAAK,GAAG,OAAO,gBAAiB,QAAO;AAC7D,QAAM,MAAM,MAAM,SAAS,GAAG,EAAE,MAAM,MAAM,IAAI;AAChD,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,SAAS,MAAM,wBAAwB;AAAA,IAC3C,MAAM;AAAA,IACN;AAAA,IACA,MAAM,IAAI,SAAS,QAAQ;AAAA,EAC7B,CAAC;AACD,SAAO,OAAO;AAChB;AAUA,IAAM,aACJ;AAEK,SAAS,cAAc,MAA8B;AAC1D,QAAM,WAAqB,CAAC;AAC5B,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,SAAkC,CAAC;AACzC,aAAW,SAAS,KAAK,SAAS,UAAU,GAAG;AAC7C,UAAM,UAAU,MAAM,CAAC;AACvB,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,YAAY,UAAa,SAAS,OAAW;AACjD,WAAO,KAAK,CAAC,MAAM,OAAO,MAAM,QAAQ,KAAK,MAAM,CAAC;AACpD,QAAI,CAAC,KAAK,IAAI,OAAO,GAAG;AACtB,WAAK,IAAI,OAAO;AAChB,eAAS,KAAK,OAAO;AAAA,IACvB;AAAA,EACF;AACA,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,EAAE,WAAW,MAAM,SAAS;AAAA,EACrC;AACA,MAAI,QAAQ;AACZ,MAAI,OAAO;AACX,aAAW,CAAC,OAAO,GAAG,KAAK,QAAQ;AACjC,aAAS,KAAK,MAAM,MAAM,KAAK;AAC/B,WAAO;AAAA,EACT;AACA,WAAS,KAAK,MAAM,IAAI;AACxB,QAAM,YAAY,MACf,QAAQ,aAAa,IAAI,EACzB,QAAQ,WAAW,MAAM,EACzB,KAAK;AACR,SAAO,EAAE,WAAW,SAAS;AAC/B;AAcA,SAAS,gBAAgB,KAAsB;AAC7C,QAAM,QAAQ,IAAI,SAAS,GAAG,GAAI;AAClC,SAAO,MAAM,SAAS,CAAC;AACzB;AASA,eAAe,eACb,KACA,QACA,OACA,OACA,QACe;AACf,MAAI,MAAM,OAAQ;AAClB,QAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC,EAAE,MAAM,MAAM,IAAI;AAC5E,MAAI,CAAC,QAAS;AACd,QAAM,UAAU,QACb,OAAO,CAAC,UAAU,MAAM,YAAY,KAAK,MAAM,OAAO,CAAC,EACvD,OAAO,CAAC,UAAU,EAAE,MAAM,YAAY,KAAK,UAAU,IAAI,MAAM,IAAI,EAAE,EACrE,OAAO,CAAC,UAAU,CAAC,gBAAgB,MAAM,IAAI,CAAC,EAC9C,OAAO,CAAC,UAAU,CAAC,SAAS,KAAK,KAAK,KAAK,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,CAAC,EAC5E,KAAK,CAAC,GAAG,MAAM;AACd,UAAM,UAAU,OAAO,EAAE,YAAY,CAAC,IAAI,OAAO,EAAE,YAAY,CAAC;AAChE,WAAO,YAAY,IAAI,UAAU,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,EAC9D,CAAC;AACH,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,QAAI,MAAM,SAAS,uBAAuB;AACxC,YAAM,SAAS;AACf;AAAA,IACF;AACA,UAAM,QAAQ,QAAQ,CAAC;AACvB,QAAI,CAAC,MAAO;AACZ,UAAM,SAAS;AACf,UAAM,OAAO,MAAM,QAAQ,SAAS;AACpC,UAAM,QAAQ,MAAM,YAAY;AAChC,QAAI,MAAM,QAAQ,yBAAyB;AACzC,YAAM,SAAS;AACf,YAAM,MAAM,KAAK,GAAG,MAAM,GAAG,OAAO,wBAAS,qBAAM,GAAG,MAAM,IAAI,GAAG,QAAQ,MAAM,EAAE,EAAE;AACrF,UAAI,SAAS,QAAQ,uBAAuB;AAC1C,cAAM;AAAA,UACJ,KAAK,KAAK,KAAK,MAAM,IAAI;AAAA,UACzB,GAAG,MAAM,GAAG,OAAO,SAAS,WAAM;AAAA,UAClC,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAAW,SAAS,QAAQ,uBAAuB;AACjD,YAAM,eAAe,KAAK,KAAK,KAAK,MAAM,IAAI,GAAG,QAAQ,QAAQ,GAAG,OAAO,MAAM;AAAA,IACnF;AAAA,EACF;AACF;AAIA,eAAe,eACb,KACA,SACA,QACwB;AACxB,QAAM,UAAU,GAAG,QAAQ,QAAQ,OAAO,GAAG,EAAE,QAAQ,QAAQ,EAAE,CAAC;AAClE,QAAM,QAAsB,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,MAAM;AACvF,QAAM,QAAQ,MAAM,QAAQ,GAAG,EAAE,MAAM,MAAM,IAAI;AACjD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,eAAe,KAAK,IAAI,GAAG,OAAO,MAAM;AAC9C,MAAI,MAAM,QAAQ;AAChB,UAAM,MAAM,KAAK,oCAAoC;AAAA,EACvD,WAAW,MAAM,QAAQ,MAAM,OAAO;AACpC,UAAM,MAAM;AAAA,MACV,mBAAmB,MAAM,QAAQ,MAAM,KAAK;AAAA,IAC9C;AAAA,EACF;AACA,SAAO,QAAQ,OAAO;AAAA,EAAqB,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA;AACnE;AAEA,eAAsB,gBAAgB,MAAc,KAAwC;AAC1F,QAAM,EAAE,WAAW,SAAS,IAAI,cAAc,IAAI;AAClD,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO,EAAE,OAAO,MAAM,UAAU,CAAC,GAAG,SAAS,CAAC,GAAG,QAAQ,CAAC,EAAE;AAAA,EAC9D;AACA,QAAM,SAAmB,CAAC;AAC1B,QAAM,WAAqB,CAAC;AAC5B,QAAM,UAAyB,CAAC;AAChC,QAAM,SAAuB,CAAC;AAC9B,MAAI;AACJ,aAAW,WAAW,UAAU;AAC9B,UAAM,MAAM,KAAK,QAAQ,KAAK,OAAO;AACrC,QAAI,gBAAgB,GAAG,GAAG;AACxB,cAAQ,KAAK,EAAE,MAAM,SAAS,QAAQ,iBAAiB,CAAC;AACxD;AAAA,IACF;AACA,UAAM,WAAW,cAAc,OAAO;AACtC,QAAI,UAAU;AACZ,YAAMC,MAAK,MAAM,KAAK,GAAG,EAAE,MAAM,MAAM,IAAI;AAC3C,UAAI,CAACA,OAAM,CAACA,IAAG,OAAO,GAAG;AACvB,gBAAQ,KAAK,EAAE,MAAM,SAAS,QAAQA,MAAK,mBAAmB,iBAAiB,CAAC;AAChF;AAAA,MACF;AACA,UAAIA,IAAG,OAAO,iBAAiB;AAC7B,gBAAQ,KAAK,EAAE,MAAM,SAAS,QAAQ,kBAAkB,CAAC;AACzD;AAAA,MACF;AACA,YAAMC,OAAM,MAAM,SAAS,GAAG,EAAE,MAAM,MAAM,IAAI;AAChD,UAAI,CAACA,MAAK;AACR,gBAAQ,KAAK,EAAE,MAAM,SAAS,QAAQ,aAAa,CAAC;AACpD;AAAA,MACF;AACA,YAAM,SAAS,MAAM,wBAAwB;AAAA,QAC3C,MAAM;AAAA,QACN;AAAA,QACA,MAAMA,KAAI,SAAS,QAAQ;AAAA,MAC7B,CAAC;AACD,eAAS,KAAK,OAAO;AACrB,aAAO,KAAK,OAAO,KAAK;AACxB;AAAA,IACF;AACA,UAAM,KAAK,MAAM,KAAK,GAAG,EAAE,MAAM,MAAM,IAAI;AAC3C,QAAI,CAAC,IAAI;AACP,cAAQ,KAAK,EAAE,MAAM,SAAS,QAAQ,iBAAiB,CAAC;AACxD;AAAA,IACF;AACA,QAAI,GAAG,YAAY,GAAG;AACpB,UAAI,WAAW,QAAW;AACxB,iBAAS,sBAAsB,GAAG,KAAK;AAAA,MACzC;AACA,YAAM,QAAQ,MAAM,eAAe,KAAK,SAAS,UAAU,MAAS;AACpE,UAAI,CAAC,OAAO;AACV,gBAAQ,KAAK,EAAE,MAAM,SAAS,QAAQ,aAAa,CAAC;AACpD;AAAA,MACF;AACA,eAAS,KAAK,OAAO;AACrB,aAAO,KAAK,KAAK;AACjB;AAAA,IACF;AACA,QAAI,GAAG,OAAO,mBAAmB;AAC/B,cAAQ,KAAK,EAAE,MAAM,SAAS,QAAQ,WAAW,oBAAoB,IAAI,WAAW,CAAC;AACrF;AAAA,IACF;AACA,UAAM,MAAM,MAAM,SAAS,GAAG,EAAE,MAAM,MAAM,IAAI;AAChD,QAAI,CAAC,KAAK;AACR,cAAQ,KAAK,EAAE,MAAM,SAAS,QAAQ,aAAa,CAAC;AACpD;AAAA,IACF;AACA,QAAI,gBAAgB,GAAG,GAAG;AACxB,cAAQ,KAAK,EAAE,MAAM,SAAS,QAAQ,cAAc,CAAC;AACrD;AAAA,IACF;AACA,aAAS,KAAK,OAAO;AACrB,WAAO,KAAK,QAAQ,OAAO;AAAA,EAAS,IAAI,SAAS,MAAM,CAAC;AAAA,YAAe;AAAA,EACzE;AACA,QAAM,QAAQ,CAAC,WAAW,GAAG,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,KAAK,MAAM,KAAK;AACjF,SAAO,EAAE,OAAO,UAAU,SAAS,OAAO;AAC5C;;;ACnQO,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACJ7B,OAAO,QAAQ;AACf,OAAOC,WAAU;AAiBjB,SAAS,eAAe,YAA4B;AAClD,SAAOA,MAAK,KAAK,YAAY,aAAa;AAC5C;AAEA,SAAS,UAAU,YAA4B;AAC7C,SAAOA,MAAK,KAAK,eAAe,UAAU,GAAG,YAAY;AAC3D;AAMA,IAAM,mBAAmB,oBAAI,IAA2B;AAExD,SAAS,kBAAqB,YAAoB,IAAkC;AAClF,QAAM,SAAS,iBAAiB,IAAI,UAAU,KAAK,QAAQ,QAAQ;AACnE,QAAM,MAAM,OAAO,KAAK,EAAE;AAC1B,mBAAiB;AAAA,IACf;AAAA,IACA,IAAI;AAAA,MACF,MAAM;AAAA,MAAC;AAAA,MACP,MAAM;AAAA,MAAC;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,sBAAsB,YAAoB,IAAoB;AAC5E,SAAOA,MAAK,KAAK,eAAe,UAAU,GAAG,GAAG,EAAE,WAAW;AAC/D;AAEA,eAAsB,sBAAsB,YAAiD;AAC3F,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,GAAG,SAAS,UAAU,UAAU,GAAG,MAAM;AAAA,EACvD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC;AAAA,EAC3C,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAKA,eAAsB,uBACpB,YACA,QACA,SACe;AACf,QAAM,kBAAkB,YAAY,YAAY;AAC9C,UAAM,GAAG,MAAM,eAAe,UAAU,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3E,QAAI,OAAO,WAAW,YAAY,MAAM;AACtC,YAAM,GAAG,UAAU,sBAAsB,YAAY,OAAO,EAAE,GAAG,SAAS,MAAM;AAAA,IAClF;AACA,UAAM,UAAU,MAAM,sBAAsB,UAAU;AACtD,YAAQ,KAAK,MAAM;AACnB,UAAM,GAAG,UAAU,UAAU,UAAU,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,EAC5E,CAAC;AACH;AAEA,eAAsB,wBAAwB,YAAoB,KAA8B;AAC9F,MAAI,IAAI,WAAW,EAAG;AACtB,QAAM,kBAAkB,YAAY,YAAY;AAC9C,UAAM,OAAO,IAAI,IAAI,GAAG;AACxB,UAAM,UAAU,MAAM,sBAAsB,UAAU;AACtD,UAAM,OAAO,QAAQ,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,OAAO,EAAE,CAAC;AAC5D,QAAI,KAAK,WAAW,QAAQ,OAAQ;AACpC,UAAM,GAAG,MAAM,eAAe,UAAU,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3E,UAAM,GAAG,UAAU,UAAU,UAAU,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACvE,eAAW,MAAM,KAAK;AACpB,YAAM,GAAG,GAAG,sBAAsB,YAAY,EAAE,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,IACpE;AAAA,EACF,CAAC;AACH;AAKA,eAAsB,qBAAqB,YAA6C;AACtF,QAAM,UAAU,MAAM,sBAAsB,UAAU;AACtD,SAAO,QAAQ,IAAI,CAAC,YAAY;AAAA,IAC9B,GAAG;AAAA,IACH,SAAS;AAAA,IACT,aACE,OAAO,WAAW,CAAC,OAAO,kBACtB,sBAAsB,YAAY,OAAO,EAAE,IAC3C;AAAA,EACR,EAAE;AACJ;;;AC/GA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,qBAAqB;AAwC9B,SAAS,WAAW,KAAmB;AACrC,QAAM,MAAM,CAAC,MAAc,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;AACpD,QAAM,QACJ,GAAG,IAAI,YAAY,CAAC,GAAG,IAAI,IAAI,SAAS,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC,CAAC,GAChE,IAAI,IAAI,SAAS,CAAC,CAAC,GAAG,IAAI,IAAI,WAAW,CAAC,CAAC,GAAG,IAAI,IAAI,WAAW,CAAC,CAAC;AACxE,QAAM,SAAS,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,EAAE,OAAO,GAAG,GAAG;AACnE,SAAO,GAAG,KAAK,IAAI,MAAM;AAC3B;AAEA,SAAS,UAAU,SAAuB;AACxC,MAAI,QAAQ,IAAI,eAAe,IAAK,SAAQ,OAAO,MAAM,cAAc,OAAO;AAAA,CAAI;AACpF;AAIA,SAAS,KAAK,SAAuB;AACnC,UAAQ,OAAO,MAAM,cAAc,OAAO;AAAA,CAAI;AAChD;AAMA,IAAM,wBAAwB;AAC9B,IAAM,0BAA0B;AAEhC,SAAS,mBAAmB,OAAyB;AACnD,QAAM,OAAQ,MAAgC;AAC9C,SAAO,SAAS,WAAW,SAAS,YAAY,SAAS;AAC3D;AAEA,eAAe,qBAAqB,WAAkD;AACpF,MAAI;AACJ,WAAS,UAAU,GAAG,UAAU,uBAAuB,WAAW;AAChE,QAAI;AACF,YAAM,UAAU;AAChB;AAAA,IACF,SAAS,OAAO;AACd,kBAAY;AACZ,UAAI,mBAAmB,KAAK,KAAK,UAAU,IAAI,uBAAuB;AACpE,cAAM,IAAI;AAAA,UAAQ,CAAC,YACjB,WAAW,SAAS,2BAA2B,UAAU,EAAE;AAAA,QAC7D;AAAA,MACF,OAAO;AACL;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM;AACR;AAKA,IAAM,gBAA+B,uBAAO,mBAAmB;AAE/D,SAAS,eAAe,MAA4B;AAClD,SAAQ,KAA4C,aAAa,MAAM;AACzE;AAOA,IAAM,yBAAyB;AAI/B,IAAM,4BAA4B,KAAK;AAEhC,SAAS,OAAO,MAAoB;AACzC,QAAM,MAAM,CAAC,MAAc,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;AACpD,SAAO,GAAG,KAAK,YAAY,CAAC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC;AACjF;AAEO,IAAM,eAAN,MAAM,cAAa;AAAA,EACf;AAAA,EACA;AAAA,EAED,cAAwE;AAAA,EACxE,cAAc;AAAA,EACd,aAAiC;AAAA,EACjC,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,iBAAgC,QAAQ,QAAQ;AAAA,EAChD,kBAAkB;AAAA,EAClB,iBAAwC;AAAA,EACxC,YAAY;AAAA,EAEZ,YAAY,IAAY;AAC9B,SAAK,KAAK;AACV,SAAK,MAAMC,MAAK,KAAK,YAAY,GAAG,EAAE;AAAA,EACxC;AAAA,EAEQ,WAAmB;AACzB,WAAOA,MAAK,KAAK,KAAK,KAAK,WAAW;AAAA,EACxC;AAAA,EAEQ,eAAuB;AAC7B,WAAOA,MAAK,KAAK,KAAK,KAAK,gBAAgB;AAAA,EAC7C;AAAA,EAEA,aAAa,OAAO,KAAa,OAAsC;AACrE,UAAM,QAAQ,IAAI,cAAa,WAAW,oBAAI,KAAK,CAAC,CAAC;AACrD,UAAM,cAAc,EAAE,KAAK,OAAO,WAAW,KAAK,IAAI,EAAE;AACxD,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,KAAK,IAA0C;AAC1D,UAAM,QAAQ,IAAI,cAAa,EAAE;AACjC,QAAI;AACJ,QAAI;AACF,YAAM,MAAMC,IAAG,SAAS,MAAM,SAAS,GAAG,MAAM;AAAA,IAClD,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,UAAI,KAAK,OAAO,GAAI,QAAO;AAAA,IAC7B,QAAQ;AAGN,gBAAU,WAAW,EAAE,qCAAqC;AAAA,IAC9D;AACA,UAAM,MAAM,cAAc;AAC1B,UAAM,cAAc;AACpB,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,KAAK,KAAsC;AACtD,QAAI;AACJ,QAAI;AACF,gBAAU,MAAMA,IAAG,QAAQ,YAAY,CAAC;AAAA,IAC1C,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AACA,UAAM,QAAQ,MAAM,QAAQ;AAAA,MAC1B,QAAQ,IAAI,OAAO,UAAU;AAC3B,YAAI;AACF,gBAAM,MAAM,MAAMA,IAAG,SAASD,MAAK,KAAK,YAAY,GAAG,OAAO,WAAW,GAAG,MAAM;AAClF,gBAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,cAAI,KAAK,OAAO,UAAU,QAAQ,UAAa,KAAK,QAAQ,KAAM,QAAO;AAAA,QAC3E,QAAQ;AAAA,QAER;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AACA,WAAO,MACJ,OAAO,CAAC,SAA8B,SAAS,IAAI,EACnD,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,UAAU,MAAkC;AAClD,QAAI,eAAe,IAAI,EAAG,QAAO,QAAQ,QAAQ;AACjD,UAAM,MAAM,GAAG,KAAK,SAAS,CAAC,QAAQ,QAAQ,GAAG,IAAI,KAAK,YAAY;AACtE,UAAM,MAAM,KAAK,eAAe,KAAK,YAAY;AAC/C,UAAI;AACF,cAAM,qBAAqB,YAAY;AACrC,gBAAMC,IAAG,UAAU,KAAK,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACrD,gBAAMA,IAAG,OAAO,KAAK,KAAK,SAAS,CAAC;AAAA,QACtC,CAAC;AAAA,MACH,SAAS,OAAO;AACd,cAAMA,IAAG,OAAO,GAAG,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AACnC;AAAA,UACE,WAAW,KAAK,EAAE,wBAAyB,MAAgC,QAAQ,OAAO,KAAK,CAAC;AAAA,QAClG;AAAA,MACF;AAAA,IACF,CAAC;AACD,SAAK,iBAAiB,IAAI,MAAM,MAAM;AAAA,IAAC,CAAC;AACxC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAsB;AAC5B,SAAK,YAAY;AACjB,QAAI,KAAK,eAAgB;AACzB,UAAM,QAAQ,KAAK,IAAI,GAAG,0BAA0B,KAAK,IAAI,IAAI,KAAK,gBAAgB;AACtF,QAAI,UAAU,GAAG;AACf,WAAK,KAAK,iBAAiB;AAC3B;AAAA,IACF;AACA,SAAK,iBAAiB,WAAW,MAAM;AACrC,WAAK,iBAAiB;AACtB,WAAK,KAAK,iBAAiB;AAAA,IAC7B,GAAG,KAAK;AACR,SAAK,eAAe,MAAM;AAAA,EAC5B;AAAA,EAEA,MAAc,mBAAkC;AAC9C,QAAI,CAAC,KAAK,UAAW;AACrB,UAAM,KAAK,aAAa;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eAA8B;AAC1C,QAAI,KAAK,gBAAgB;AACvB,mBAAa,KAAK,cAAc;AAChC,WAAK,iBAAiB;AAAA,IACxB;AACA,SAAK,YAAY;AACjB,SAAK,kBAAkB,KAAK,IAAI;AAChC,QAAI,KAAK,WAAY,OAAM,KAAK,UAAU,KAAK,UAAU;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,UAAM,KAAK,iBAAiB;AAC5B,UAAM,KAAK;AAAA,EACb;AAAA,EAEA,MAAc,oBAAmC;AAC/C,QAAI,KAAK,YAAa;AACtB,UAAM,UAAU,KAAK;AACrB,QAAI,CAAC,SAAS;AACZ,WAAK,cAAc;AACnB;AAAA,IACF;AAGA,UAAMA,IAAG,MAAM,KAAK,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACzD,UAAM,KAAK,cAAc;AACzB,UAAM,OAAoB;AAAA,MACxB,IAAI,KAAK;AAAA,MACT,OAAO;AAAA,MACP,OAAO,QAAQ;AAAA,MACf,KAAK,QAAQ;AAAA,MACb,WAAW,QAAQ;AAAA,MACnB,WAAW,QAAQ;AAAA,IACrB;AACA,UAAM,KAAK,UAAU,IAAI;AACzB,SAAK,aAAa;AAClB,SAAK,kBAAkB,KAAK,IAAI;AAChC,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBAA+B;AAC3C,QAAI;AACJ,QAAI;AACF,gBAAU,MAAMA,IAAG,QAAQ,KAAK,GAAG;AAAA,IACrC,QAAQ;AACN;AAAA,IACF;AACA,UAAM,QAAQ;AAAA,MACZ,QACG;AAAA,QACC,CAAC,UAAU,MAAM,WAAW,gBAAgB,KAAK,MAAM,WAAW,qBAAqB;AAAA,MACzF,EACC,IAAI,CAAC,UAAUA,IAAG,OAAOD,MAAK,KAAK,KAAK,KAAK,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC,CAAC;AAAA,IACzE;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,SAAqC;AAChD,UAAM,KAAK,kBAAkB;AAO7B,QAAI;AACF,YAAM;AAAA,QAAqB,MACzBC,IAAG,WAAW,KAAK,aAAa,GAAG,GAAG,KAAK,UAAU,OAAO,CAAC;AAAA,CAAI;AAAA,MACnE;AAAA,IACF,SAAS,OAAO;AACd;AAAA,QACE,WAAW,KAAK,EAAE,4BAA6B,MAAgC,QAAQ,OAAO,KAAK,CAAC;AAAA,MACtG;AAAA,IACF;AACA,UAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,SAAK,YAAY,KAAK,IAAI;AAC1B,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,MAAM,gBAAgB,UAAwC;AAC5D,UAAM,KAAK,kBAAkB;AAC7B,UAAM,UAAU,SAAS,IAAI,CAAC,YAAY,KAAK,UAAU,OAAO,CAAC,EAAE,KAAK,IAAI;AAM5E,UAAM,MAAM,GAAG,KAAK,aAAa,CAAC,QAAQ,QAAQ,GAAG,IAAI,KAAK,gBAAgB;AAC9E,QAAI;AACF,YAAM,qBAAqB,YAAY;AACrC,cAAMA,IAAG,UAAU,KAAK,UAAU,GAAG,OAAO;AAAA,IAAO,EAAE;AACrD,cAAMA,IAAG,OAAO,KAAK,KAAK,aAAa,CAAC;AAAA,MAC1C,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAMA,IAAG,OAAO,GAAG,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACnC;AAAA,QACE,WAAW,KAAK,EAAE,6BAA8B,MAAgC,QAAQ,OAAO,KAAK,CAAC;AAAA,MACvG;AAAA,IACF;AACA,UAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,SAAK,YAAY,KAAK,IAAI;AAC1B,UAAM,KAAK,aAAa;AAAA,EAC1B;AAAA,EAEA,MAAM,WAAmC;AAMvC,QAAI,SAA+B;AACnC,QAAI;AACF,eAAS,MAAMA,IAAG,KAAK,KAAK,aAAa,GAAG,GAAG;AAC/C,YAAM,WAA0B,CAAC;AACjC,YAAM,UAAU,IAAI,cAAc,MAAM;AACxC,YAAM,SAAS,OAAO,MAAM,yBAAyB;AACrD,UAAI,OAAO;AACX,YAAM,WAAW,CAAC,SAAiB;AACjC,cAAM,UAAU,KAAK,KAAK;AAC1B,YAAI,CAAC,QAAS;AACd,YAAI;AACF,mBAAS,KAAK,KAAK,MAAM,OAAO,CAAgB;AAAA,QAClD,QAAQ;AACN,oBAAU,WAAW,KAAK,EAAE,wCAAwC;AAAA,QACtE;AAAA,MACF;AACA,aAAO,MAAM;AACX,cAAM,EAAE,UAAU,IAAI,MAAM,OAAO,KAAK,QAAQ,GAAG,OAAO,QAAQ,IAAI;AACtE,YAAI,cAAc,EAAG;AACrB,cAAM,QAAQ,OAAO,QAAQ,MAAM,OAAO,SAAS,GAAG,SAAS,CAAC;AAChE,cAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,eAAO,MAAM,IAAI,KAAK;AACtB,mBAAW,QAAQ,MAAO,UAAS,IAAI;AAAA,MACzC;AACA,eAAS,OAAO,QAAQ,IAAI,CAAC;AAC7B,aAAO;AAAA,IACT,QAAQ;AACN,aAAO,CAAC;AAAA,IACV,UAAE;AACA,YAAM,QAAQ,MAAM,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACtC;AAAA,EACF;AAAA,EAEQ,eAA4B;AAClC,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,OAAoB;AAAA,MACxB,IAAI,KAAK;AAAA,MACT,OAAO;AAAA,MACP,OAAO;AAAA,MACP,KAAK;AAAA,MACL,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAEA,WAAO,eAAe,MAAM,eAAe,EAAE,OAAO,MAAM,YAAY,MAAM,CAAC;AAC7E,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAA6B;AACjC,UAAM,KAAK,kBAAkB;AAC7B,QAAI,KAAK,WAAY,QAAO,KAAK;AACjC,QAAI;AACF,YAAM,MAAM,MAAMA,IAAG,SAAS,KAAK,SAAS,GAAG,MAAM;AACrD,WAAK,aAAa,KAAK,MAAM,GAAG;AAChC,aAAO,KAAK;AAAA,IACd,QAAQ;AAGN,gBAAU,WAAW,KAAK,EAAE,wCAAwC;AACpE,aAAO,KAAK,aAAa;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,OAA8B;AAC3C,UAAM,KAAK,kBAAkB;AAC7B,UAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,SAAK,QAAQ;AACb,SAAK,YAAY,KAAK,IAAI;AAC1B,UAAM,KAAK,aAAa;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,OAA8B;AAC3C,UAAM,KAAK,kBAAkB;AAC7B,UAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,SAAK,QAAQ;AACb,SAAK,YAAY,KAAK,IAAI;AAC1B,UAAM,KAAK,aAAa;AAAA,EAC1B;AAAA,EAEA,MAAM,iBAAiB,QAA0B,SAAuC;AACtF,UAAM,KAAK,kBAAkB;AAC7B,UAAM,uBAAuB,KAAK,KAAK,QAAQ,OAAO;AAAA,EACxD;AAAA,EAEA,MAAM,kBAA+C;AACnD,WAAO,sBAAsB,KAAK,GAAG;AAAA,EACvC;AAAA,EAEA,MAAM,kBAAkB,KAA8B;AACpD,UAAM,wBAAwB,KAAK,KAAK,GAAG;AAAA,EAC7C;AAAA,EAEA,MAAM,SAAS,OAMG;AAChB,UAAM,KAAK,kBAAkB;AAC7B,UAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,UAAM,QAAQ,KAAK,SAAS;AAAA,MAC1B,UAAU;AAAA,MACV,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB,aAAa;AAAA,IACf;AACA,UAAM,YAAY;AAClB,UAAM,gBAAgB,MAAM;AAC5B,UAAM,oBAAoB,MAAM;AAChC,UAAM,eAAe,MAAM;AAC3B,QAAI,MAAM,oBAAoB;AAC5B,YAAM,sBAAsB,MAAM,sBAAsB,KAAK,MAAM;AAAA,IACrE;AACA,QAAI,MAAM,sBAAsB;AAC9B,YAAM,wBAAwB,MAAM,wBAAwB,KAAK,MAAM;AAAA,IACzE;AACA,SAAK,QAAQ;AACb,UAAM,MAAM,OAAO,oBAAI,KAAK,CAAC;AAC7B,UAAM,QAAQ,KAAK,cAAc,CAAC;AAClC,UAAM,SAAS,MAAM,GAAG,KAAK,EAAE,cAAc,GAAG,kBAAkB,GAAG,aAAa,EAAE;AACpF,WAAO,gBAAgB,MAAM;AAC7B,WAAO,oBAAoB,MAAM;AACjC,WAAO,eAAe,MAAM;AAC5B,UAAM,GAAG,IAAI;AACb,SAAK,aAAa;AAClB,UAAM,KAAK,aAAa;AAAA,EAC1B;AACF;;;AClfA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAgBjB,eAAsB,cAAc,KAAc,WAAqC;AACrF,MAAI,QAAQ,QAAW;AACrB,UAAM,QAAQ,MAAM,aAAa,KAAK,GAAG;AACzC,QAAIC,WAAU;AACd,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,OAAO,UAAW;AAC3B,YAAMC,IAAG,GAAGC,MAAK,KAAK,YAAY,GAAG,KAAK,EAAE,GAAG,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC/E,MAAAF,YAAW;AAAA,IACb;AACA,SAAK,MAAM,aAAa,KAAK,GAAG,GAAG,WAAW,GAAG;AAC/C,YAAM,eAAe,GAAG;AAAA,IAC1B;AACA,WAAOA;AAAA,EACT;AACA,MAAI;AACJ,MAAI;AACF,cAAU,MAAMC,IAAG,QAAQ,YAAY,CAAC;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,UAAU;AACd,aAAW,SAAS,SAAS;AAC3B,QAAI,UAAU,UAAW;AACzB,UAAMA,IAAG,GAAGC,MAAK,KAAK,YAAY,GAAG,KAAK,GAAG,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC7E,eAAW;AAAA,EACb;AACA,QAAM,YAAY,MAAM,aAAa,KAAK;AAC1C,QAAM,qBAAqB,UAAU,IAAI,CAAC,SAAS,KAAK,GAAG,CAAC;AAC5D,SAAO;AACT;;;AC9CA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAiBjB,IAAM,oBAAoB,KAAK;AAE/B,eAAe,cAAc,IAA6B;AACxD,MAAI,SAA+B;AACnC,MAAI;AACF,aAAS,MAAMC,IAAG,KAAKC,MAAK,KAAK,YAAY,GAAG,IAAI,gBAAgB,GAAG,GAAG;AAC1E,UAAM,SAAS,OAAO,MAAM,iBAAiB;AAC7C,QAAI,QAAQ;AACZ,QAAI,iBAAiB;AACrB,WAAO,MAAM;AACX,YAAM,EAAE,UAAU,IAAI,MAAM,OAAO,KAAK,QAAQ,GAAG,mBAAmB,IAAI;AAC1E,UAAI,cAAc,EAAG;AACrB,iBAAW,QAAQ,OAAO,SAAS,GAAG,SAAS,GAAG;AAChD,YAAI,SAAS,IAAM;AACjB,cAAI,eAAgB,UAAS;AAC7B,2BAAiB;AAAA,QACnB,WAAW,SAAS,MAAQ,SAAS,MAAQ,SAAS,GAAM;AAC1D,2BAAiB;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AACA,QAAI,eAAgB,UAAS;AAC7B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT,UAAE;AACA,UAAM,QAAQ,MAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACtC;AACF;AAEA,eAAsB,mBAAmB,KAA2C;AAClF,QAAM,QAAQ,MAAM,aAAa,KAAK,GAAG;AACzC,SAAO,QAAQ;AAAA,IACb,MAAM,IAAI,OAAO,UAAU,EAAE,MAAM,cAAc,MAAM,cAAc,KAAK,EAAE,EAAE,EAAE;AAAA,EAClF;AACF;AAEA,eAAsB,kBAAkB,KAA0C;AAChF,QAAM,CAAC,MAAM,IAAI,MAAM,aAAa,KAAK,GAAG;AAC5C,SAAO,UAAU;AACnB;AAOA,eAAsB,eAAe,IAAY,WAAW,MAA8B;AACxF,MAAI,SAA+B;AACnC,MAAI;AACF,aAAS,MAAMD,IAAG,KAAKC,MAAK,KAAK,YAAY,GAAG,IAAI,gBAAgB,GAAG,GAAG;AAC1E,UAAM,SAAS,OAAO,MAAM,QAAQ;AACpC,UAAM,EAAE,UAAU,IAAI,MAAM,OAAO,KAAK,QAAQ,GAAG,UAAU,CAAC;AAC9D,UAAM,OAAO,OAAO,SAAS,QAAQ,GAAG,SAAS;AACjD,eAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,CAAC,QAAS;AACd,UAAI;AACF,cAAM,UAAU,KAAK,MAAM,OAAO;AAClC,YAAI,QAAQ,SAAS,OAAQ;AAC7B,cAAM,OACJ,OAAO,QAAQ,YAAY,WACvB,QAAQ,UACR,QAAQ,QACL,OAAO,CAAC,SAAS,KAAK,SAAS,MAAM,EACrC,IAAI,CAAC,SAAU,KAAK,SAAS,SAAS,KAAK,OAAO,EAAG,EACrD,KAAK,GAAG;AACjB,cAAM,YAAY,KAAK,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACjD,YAAI,UAAW,QAAO;AAAA,MACxB,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT,UAAE;AACA,UAAM,QAAQ,MAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACtC;AACF;AAEO,SAAS,eAAe,IAAoB;AACjD,QAAM,OAAO,GAAG,YAAY,GAAG;AAC/B,SAAO,SAAS,KAAK,KAAK,GAAG,MAAM,OAAO,CAAC;AAC7C;AAEA,eAAsB,iBAAiB,OAAuC;AAC5E,QAAM,QAAQ,MAAM,aAAa,KAAK;AACtC,QAAM,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,KAAK;AACpD,MAAI,MAAO,QAAO,MAAM;AACxB,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,SAAS,eAAe,KAAK,EAAE,MAAM,SAAS,KAAK,GAAG,WAAW,KAAK;AAAA,EACzE;AACA,SAAO,QAAQ,WAAW,IAAK,QAAQ,CAAC,GAAG,MAAM,OAAQ;AAC3D;AAEO,SAAS,aAAa,WAA2B;AACtD,QAAM,OAAO,KAAK,IAAI,IAAI;AAC1B,QAAM,UAAU,KAAK,MAAM,OAAO,GAAM;AACxC,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,QAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;AACrC,MAAI,QAAQ,GAAI,QAAO,GAAG,KAAK;AAC/B,QAAM,OAAO,KAAK,MAAM,QAAQ,EAAE;AAClC,MAAI,OAAO,GAAI,QAAO,GAAG,IAAI;AAC7B,QAAM,SAAS,KAAK,MAAM,OAAO,EAAE;AACnC,MAAI,SAAS,GAAI,QAAO,GAAG,MAAM;AACjC,SAAO,GAAG,KAAK,MAAM,SAAS,EAAE,CAAC;AACnC;AAEO,SAAS,qBACd,SACA,UAAiC,CAAC,GAC1B;AACR,SAAO,QACJ,IAAI,CAAC,EAAE,MAAM,aAAa,MAAM;AAC/B,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,OACJ,GAAG,eAAe,KAAK,EAAE,CAAC,KAAK,KAAK,KACjC,YAAY,sBAAsB,aAAa,KAAK,SAAS,CAAC;AACnE,WAAO,QAAQ,UAAU,GAAG,IAAI,MAAM,KAAK,GAAG,MAAM;AAAA,EACtD,CAAC,EACA,KAAK,IAAI;AACd;;;AC7IA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,qBAAqB;AAE9B,SAAS,qBAA6B;AACpC,MAAI;AACF,UAAM,UAAUA,MAAK,KAAKA,MAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,MAAM,cAAc;AAC5F,UAAM,MAAe,KAAK,MAAMD,IAAG,aAAa,SAAS,MAAM,CAAC;AAChE,QAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC3C,YAAM,UAAW,IAA8B;AAC/C,UAAI,OAAO,YAAY,SAAU,QAAO;AAAA,IAC1C;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEO,IAAM,UAAU,mBAAmB;;;AClB1C,OAAOE,SAAQ;AAGf,IAAM,kBAAkB;AAIjB,SAAS,gBAAgB,SAAyC;AACvE,QAAM,OAA+B,CAAC;AACtC,aAAW,WAAW,QAAQ,MAAM,OAAO,GAAG;AAC5C,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,EAAG;AACnC,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,MAAM,EAAG;AACb,UAAM,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACnC,QAAI,CAAC,gBAAgB,KAAK,GAAG,EAAG;AAChC,QAAI,QAAQ,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK;AACpC,QACE,MAAM,UAAU,MACd,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAC1C,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,IAC9C;AACA,cAAQ,MAAM,MAAM,GAAG,EAAE;AAAA,IAC3B;AACA,SAAK,GAAG,IAAI;AAAA,EACd;AACA,SAAO;AACT;AAKO,SAAS,YAAY,WAAmB,YAAY,GAAS;AAClE,MAAI;AACJ,MAAI;AACF,cAAUC,IAAG,aAAa,UAAU,MAAM;AAAA,EAC5C,QAAQ;AACN;AAAA,EACF;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,gBAAgB,OAAO,CAAC,GAAG;AACnE,QAAI,QAAQ,IAAI,GAAG,MAAM,QAAW;AAClC,cAAQ,IAAI,GAAG,IAAI;AAAA,IACrB;AAAA,EACF;AACF;AAKO,SAAS,iBAAiB,SAAiB,KAAa,OAAuB;AACpF,QAAM,QAAQ,GAAG,GAAG,IAAI,MAAM,QAAQ,WAAW,EAAE,CAAC;AACpD,QAAM,UAAU,IAAI,OAAO,QAAQ,GAAG,OAAO;AAC7C,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,SAAO,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,CAAC,KAAK,IAAI,KAAK,MAAM,GAAI,OAAM,IAAI;AACpF,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,QAAI,QAAQ,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG;AAChC,YAAM,CAAC,IAAI;AACX,aAAO,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA;AAAA,IAC5B;AAAA,EACF;AACA,QAAM,KAAK,KAAK;AAChB,SAAO,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA;AAC5B;","names":["lines","st","buf","path","fs","path","path","fs","fs","path","removed","fs","path","fs","path","fs","path","fs","path","fs","fs"]}