flavor-code 1.2.8 → 1.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +35 -0
  2. package/README.zh-CN.md +36 -6
  3. package/dist/agent/loop.d.ts +4 -0
  4. package/dist/agent/types.d.ts +10 -0
  5. package/dist/{app-NL2O75LQ.js → app-USAFQVSX.js} +379 -12
  6. package/dist/{chunk-NVLYFU6P.js → chunk-F2HMO5Q2.js} +28 -11
  7. package/dist/{chunk-6X5PHLZI.js → chunk-G32MCAZA.js} +1 -1
  8. package/dist/{chunk-N2S7USST.js → chunk-RQTYHUMK.js} +2 -1
  9. package/dist/{chunk-ED4R7ZCP.js → chunk-WGYNTR4Q.js} +1820 -492
  10. package/dist/{chunk-MBLAXTUP.js → chunk-XFCJXRJ2.js} +15 -5
  11. package/dist/{claude-ink-WPWPEL5A.js → claude-ink-ORUA7GWG.js} +2 -2
  12. package/dist/cli.js +9 -8
  13. package/dist/config/protected-file.d.ts +6 -0
  14. package/dist/context/manager.d.ts +2 -0
  15. package/dist/context/workspace-instructions.d.ts +10 -0
  16. package/dist/desktop/main.js +12725 -5181
  17. package/dist/desktop/pixel-worker.js +73 -0
  18. package/dist/desktop/preload.cjs +80 -2
  19. package/dist/desktop-renderer/assets/index-D-s27J39.js +151 -0
  20. package/dist/desktop-renderer/assets/index-DMZBSGrp.css +1 -0
  21. package/dist/desktop-renderer/index.html +3 -4
  22. package/dist/harness/local.d.ts +4 -1
  23. package/dist/jobs/registry.d.ts +48 -0
  24. package/dist/{load-RN3WBX4C.js → load-6ZMEBHSG.js} +1 -1
  25. package/dist/permissions/engine.d.ts +7 -0
  26. package/dist/production.d.ts +13 -1
  27. package/dist/sdk/index.js +4 -4
  28. package/dist/terminal/service.d.ts +57 -0
  29. package/dist/tools/files.d.ts +10 -0
  30. package/dist/tools/jobs.d.ts +4 -0
  31. package/dist/tools/runtime.d.ts +2 -0
  32. package/dist/tools/shell.d.ts +13 -3
  33. package/dist/tools/terminal.d.ts +3 -0
  34. package/dist/tools/types.d.ts +76 -4
  35. package/dist/tools/web.d.ts +53 -0
  36. package/package.json +13 -2
  37. package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +752 -1
  38. package/dist/desktop-renderer/assets/index-57tXtXhI.js +0 -143
  39. package/dist/desktop-renderer/assets/index-Cm_ktmXm.css +0 -1
package/README.md CHANGED
@@ -35,8 +35,38 @@ Flavor Code connects to OpenAI, Anthropic, or compatible services and works with
35
35
  | 🧭 | **Controlled progress on complex tasks** | Task plans, sub-agents, steering, follow-ups, `/loop`, and `/goal` |
36
36
  | ⏪ | **Traceable, resumable results** | Full timeline, checkpoints, rewind, traces, diffs, and failure audits |
37
37
  | 🧠 | **Local long-term context** | Memory, Skills, plugins, and project guides stored on your machine |
38
+ | 🎨 | **D2C design-to-code** | Import Pixso exports; the agent generates Vue/React implementations with automatic pixel-level visual evaluation (Electron only) |
38
39
  | 🛡️ | **Clear permission boundaries** | Independent control over read, write, Shell, network, and destructive actions; Docker supported |
39
40
 
41
+ ## 1.2.9 Runtime Productivity
42
+
43
+ 1.2.9 adds layered project instructions, safe writes, background jobs, persistent terminals, and native web tools. Usually you can just describe the goal in natural language and the agent picks the right tool; when you need precise control, name the tool and its parameters explicitly in the prompt.
44
+
45
+ | Feature | How to use |
46
+ | --- | --- |
47
+ | **Layered project instructions** | Put `AGENTS.md` / `CLAUDE.md` in the project root or subdirectories; use `AGENTS.local.md` / `CLAUDE.local.md` for local additions in the same directory. Root rules load at startup; subdirectory rules load automatically when the agent touches files there. |
48
+ | **Per-turn change summary** | No configuration needed. After a successful `Write`, `Edit`, or `ApplyPatch`, the turn shows a color-coded `CHANGESET` receipt with workspace-relative paths, `CREATE` / `UPDATE` / `DELETE` operations, per-file line counts, and a total. At most 8 files are shown, with an explicit shown/total footer when more changed. |
49
+ | **File version protection** | No configuration needed. If the IDE, a formatter, or another process modifies a file after the agent read it, the next write fails with `Stale file`; ask the agent to re-read before editing. |
50
+ | **Standard tool presentation protocol** | Tool authors can declare `outputSchema`, `renderForModel`, `presentCall`, and `presentResult`, so the same result can use an appropriate form in the model context, CLI, and desktop. The CLI visually separates file diffs, web evidence, job receipts, foreground `COMMAND` output, and persistent `TERMINAL` output from the final answer. |
51
+ | **Background Shell / Jobs** | Say "start the dev server in the background" and the agent calls `Shell` with `background: true`. Use `JobList` to view jobs, `JobRead` for incremental output, `JobWait` to wait, and `JobKill` to stop. The CLI shows a color-bordered `JOB` receipt separating job metadata, logs, and the final answer; logs show at most the latest 12 lines and lists at most 8 items. Windows prefers UTF-8 and falls back automatically on GBK/GB18030 system diagnostics. |
52
+ | **Foreground command results** | Foreground `Shell` calls render as state-colored `COMMAND` receipts with separate command, stdout, stderr, and exit regions. Long output keeps the first and last 8 lines and explicitly folds the middle; persistent PTY output uses the distinct `TERMINAL` label. |
53
+ | **Desktop background status** | Electron automatically shows the number of running jobs in the session title bar, updated live on start, output, exit, or cancel. |
54
+ | **Persistent PTY** | Say "open a persistent terminal and keep interacting". The agent uses `TerminalOpen` to create a terminal, `TerminalWrite` for input, `TerminalRead` for incremental output, and `TerminalClose` to close it. |
55
+ | **Unified D2C/E2E process lifecycle** | No usage change. Preview and backend services still start/stop from the E2E/D2C workbench, but the underlying layer unifies output limits, process-tree termination, and idempotent cleanup. |
56
+ | **Native WebSearch** | Say "search the web for ...", or explicitly ask for `WebSearch`. It uses keyless DuckDuckGo Lite by default and degrades to Bing on connection failure, HTTP rejection, or no parseable results; up to 20 results per call. The CLI puts the top 5 into a bordered `WEB SEARCH` evidence block with titles and compact sources in search order. |
57
+ | **Native WebFetch** | Say "read this page: `https://...`", or explicitly ask for `WebFetch`. Supports HTTP(S), redirects, HTML-to-text, timeouts, and response size limits, and is compatible with Clash/TUN Fake-IP DNS. Direct access to Fake-IP, intranet, or cloud metadata addresses is still blocked; network operations still follow Flavor permission approval. |
58
+
59
+ Common precise usage:
60
+
61
+ ```text
62
+ Start npm run dev with Shell in background mode, then use JobRead to inspect the startup logs.
63
+ Open a persistent terminal, run a Python REPL in it, execute two snippets, then close the terminal.
64
+ Use WebSearch to find the official TypeScript 7 migration notes, then WebFetch the most relevant official page.
65
+ This directory has its own conventions; follow src/payments/AGENTS.md before modifying code here.
66
+ ```
67
+
68
+ See [Technical Design Report §38](./技术方案报告.md#38-129-运行时生产力与原生-web-能力) for tool parameters, state machines, security boundaries, and extension interfaces; acceptance criteria are in the [Runtime productivity spec](./docs/specs/2026-08-13-runtime-productivity-waves.md).
69
+
40
70
  ## Quick Start
41
71
 
42
72
  > [!IMPORTANT]
@@ -167,6 +197,8 @@ npm run desktop:dist # Windows NSIS installer
167
197
 
168
198
  The desktop app provides project and session switching, streaming Markdown, tool and diff views, permission confirmations, task status, and management of Skills, MCP, memory, and models.
169
199
 
200
+ The **D2C** module in the sidebar supports a complete design-to-code loop: import a Pixso-exported HTML directory, choose a target framework (Vue 3 / React), and submit the generation task to the current session. The agent implements it under `src/d2c-output/<task>/` following the `d2c-pixso` skill (SOP); a Vite dev server then starts automatically for pixel-level comparison, producing a visual-fidelity score and a structured diff report (region offsets, color deviations, font differences). The results workbench offers overlay, curtain, flicker, and heatmap comparison modes, an SVG annotation layer, and a severity-sorted issue list, so each diff can be accepted or rejected individually and trigger module-level fixes. Once visual review passes, you can import a Swagger/OpenAPI document to auto-generate Axios wrappers and an Express mock server, moving into API integration and interactive acceptance.
201
+
170
202
  ### VS Code / Qoder
171
203
 
172
204
  ```bash
@@ -338,6 +370,9 @@ npm run build
338
370
  - [Runtime reliability spec](./docs/specs/2026-07-26-runtime-reliability.md)
339
371
  - [Control plane, sandbox & VS Code spec](./docs/specs/2026-07-29-control-plane-sandbox-vscode.md)
340
372
  - [Multimodal image attachments spec](./docs/specs/2026-07-30-multimodal-image-attachments.md)
373
+ - [D2C design-to-code spec](./docs/specs/2026-08-09-d2c-design-to-code.md)
374
+ - [D2C review & integration spec](./docs/specs/2026-08-10-d2c-review-and-integration.md)
375
+ - [1.2.9 runtime productivity spec](./docs/specs/2026-08-13-runtime-productivity-waves.md)
341
376
  - [VS Code next steps](./docs/specs/2026-08-01-flavor-code-vscode-next.md)
342
377
 
343
378
  ## Security Notes
package/README.zh-CN.md CHANGED
@@ -32,12 +32,42 @@ Flavor Code 接入 OpenAI、Anthropic 或兼容服务,在受控工作区内使
32
32
  | | 能力 | 你得到什么 |
33
33
  | --- | --- | --- |
34
34
  | 🖥️ | **一个运行时,三个入口** | CLI、Electron 与 VS Code 共享模型配置、会话和工具能力 |
35
- | 🧭 | **复杂任务可控推进** | 任务计划、子 Agent、steering、follow-up、`/loop` 和 `/goal` |
36
- | ⏪ | **结果可追溯、可恢复** | 完整时间线、checkpoint、rewind、trace、Diff 和失败审计 |
37
- | 🧠 | **本地长期上下文** | 记忆、Skill、插件和项目指南均保存在本机 |
38
- | 🛡️ | **明确的权限边界** | 分别控制读、写、Shell、网络和破坏性操作,也可使用 Docker |
39
-
40
- ## 快速开始
35
+ | 🧭 | **复杂任务可控推进** | 任务计划、子 Agent、steering、follow-up、`/loop` 和 `/goal` |
36
+ | ⏪ | **结果可追溯、可恢复** | 完整时间线、checkpoint、rewind、trace、Diff 和失败审计 |
37
+ | 🧠 | **本地长期上下文** | 记忆、Skill、插件和项目指南均保存在本机 |
38
+ | 🎨 | **D2C 设计转代码** | 导入 Pixso 导出结果,由 Agent 生成 Vue/React 实现并自动进行像素级视觉评估(仅 Electron) |
39
+ | 🛡️ | **明确的权限边界** | 分别控制读、写、Shell、网络和破坏性操作,也可使用 Docker |
40
+
41
+ ## 1.2.9 运行时生产力
42
+
43
+ 1.2.9 新增分层项目指令、安全写入、后台任务、持久终端和原生 Web 工具。通常只需用自然语言描述目标,Agent 会选择合适的工具;需要精确控制时,也可以在提示词中明确指定工具和参数。
44
+
45
+ | 功能 | 使用方式 |
46
+ | --- | --- |
47
+ | **分层项目指令** | 在项目根目录或子目录放置 `AGENTS.md` / `CLAUDE.md`;同目录需要本地补充时使用 `AGENTS.local.md` / `CLAUDE.local.md`。根规则启动时加载,子目录规则在 Agent 访问该目录文件时自动加载。 |
48
+ | **每轮成果物汇总** | 无需配置。`Write`、`Edit` 或 `ApplyPatch` 成功后,回合结束会显示带语义色的 `CHANGESET` 收据,使用工作区相对路径列出 `CREATE` / `UPDATE` / `DELETE` 操作、各文件行数和总计。最多展示 8 个文件,超出时明确显示已展示数与总数。 |
49
+ | **文件版本保护** | 无需配置。如果 IDE、格式化器或其他进程在 Agent 读取后修改了文件,后续写入会报 `Stale file`;让 Agent 重新读取后再修改即可。 |
50
+ | **标准工具展示协议** | 工具作者可声明 `outputSchema`、`renderForModel`、`presentCall` 和 `presentResult`,让同一结果在模型上下文、CLI 与桌面端分别使用合适的形式。CLI 会把文件 Diff、Web 证据、Job 运行收据、前台 `COMMAND` 和持久 `TERMINAL` 与最终回答明确分开。 |
51
+ | **后台 Shell / Job** | 提示“在后台启动开发服务器”,Agent 会调用 `Shell` 并设置 `background: true`。使用 `JobList` 查看任务、`JobRead` 增量读取输出、`JobWait` 等待结束、`JobKill` 停止任务。CLI 使用带状态色边界的 `JOB` 收据区分任务元数据、日志与最终回答;日志最多显示最近 12 行,列表最多显示 8 项。Windows 优先使用 UTF-8,遇到 GBK/GB18030 系统诊断时自动回退。 |
52
+ | **前台命令结果** | 前台 `Shell` 显示为带状态色的 `COMMAND` 收据,分别展示命令、stdout、stderr 和退出状态。长输出保留开头 8 行与结尾 8 行,并明确折叠中间部分;持久 PTY 使用独立的 `TERMINAL` 标签。 |
53
+ | **桌面后台状态** | Electron 会在会话标题栏自动显示运行中的 Job 数量,并在任务启动、输出、退出或取消时实时更新。 |
54
+ | **持久 PTY** | 提示“打开一个持久终端并继续交互”。Agent 使用 `TerminalOpen` 创建终端、`TerminalWrite` 输入、`TerminalRead` 增量读取输出,并用 `TerminalClose` 关闭。 |
55
+ | **D2C/E2E 统一进程生命周期** | 使用方式不变。预览和后端服务仍从 E2E/D2C 工作台启动或停止,底层统一处理输出限制、进程树终止和幂等清理。 |
56
+ | **原生 WebSearch** | 提示“搜索 Web 上的……”,或明确要求使用 `WebSearch`。默认使用无需密钥的 DuckDuckGo Lite;连接失败、HTTP 拒绝或没有可解析结果时自动降级到 Bing。单次最多返回 20 条;CLI 将前 5 条放入带边界的 `WEB SEARCH` 证据块,按搜索排名显示标题和紧凑来源。 |
57
+ | **原生 WebFetch** | 提示“读取这个网页:`https://...`”,或明确要求使用 `WebFetch`。支持 HTTP(S)、重定向、HTML 转文本、超时和响应大小限制,并兼容 Clash/TUN Fake-IP DNS。直接访问 Fake-IP、内网或云元数据地址仍会被拦截;网络操作继续遵守 Flavor 权限审批。 |
58
+
59
+ 常见的精确用法:
60
+
61
+ ```text
62
+ 使用 Shell 后台模式启动 npm run dev,然后通过 JobRead 检查启动日志。
63
+ 打开持久终端,在其中运行 Python REPL,连续执行两段代码后关闭终端。
64
+ 使用 WebSearch 搜索 TypeScript 7 官方迁移说明,再用 WebFetch 读取最相关的官方页面。
65
+ 这个目录有独立约定,请先遵守 src/payments/AGENTS.md 再修改代码。
66
+ ```
67
+
68
+ 原生工具的参数、状态机、安全边界和扩展接口详见[技术方案报告第 38 节](./技术方案报告.md#38-129-运行时生产力与原生-web-能力);验收规格见[运行时生产力规范](./docs/specs/2026-08-13-runtime-productivity-waves.md)。
69
+
70
+ ## 快速开始
41
71
 
42
72
  > [!IMPORTANT]
43
73
  > CLI 需要 Node.js 20 或更高版本。Windows 桌面端也可以直接从 [Releases](https://github.com/YachuanWzh/flavor-code/releases) 下载。
@@ -19,13 +19,17 @@ export interface AgentLoopOptions {
19
19
  maxExtensions?: number;
20
20
  hasActiveProgress?(): boolean;
21
21
  agent?: "main" | "subagent";
22
+ ownerId?: string;
22
23
  hallucinationGuard?: HallucinationGuard;
23
24
  }
24
25
  export declare class AgentLoop {
25
26
  #private;
26
27
  constructor(options: AgentLoopOptions);
27
28
  get modelId(): string;
29
+ get iterationLimitMode(): "standard" | "d2c";
28
30
  setModel(modelId: string): void;
29
31
  setFallbackModel(modelId: string): void;
32
+ /** D2C keeps a hard cap, but may automatically expand it up to ten times. */
33
+ setIterationLimitMode(mode: "standard" | "d2c"): void;
30
34
  run(request: AgentRunRequest): AsyncIterable<AgentEvent>;
31
35
  }
@@ -32,6 +32,12 @@ export interface TaskSnapshot {
32
32
  };
33
33
  foregroundTaskId?: string;
34
34
  }
35
+ export interface TurnDeliverable {
36
+ path: string;
37
+ operation: "create" | "update" | "delete";
38
+ added: number;
39
+ removed: number;
40
+ }
35
41
  export type AgentEvent = {
36
42
  type: "model-start";
37
43
  id: string;
@@ -48,6 +54,7 @@ export type AgentEvent = {
48
54
  input: unknown;
49
55
  label?: string;
50
56
  hint?: string;
57
+ presentation?: import("../tools/types.js").ToolPresentation;
51
58
  } | {
52
59
  type: "tool-end";
53
60
  id: string;
@@ -60,6 +67,9 @@ export type AgentEvent = {
60
67
  snapshot: TaskSnapshot;
61
68
  } | {
62
69
  type: "tasks-cleared";
70
+ } | {
71
+ type: "deliverables";
72
+ files: readonly TurnDeliverable[];
63
73
  } | {
64
74
  type: "usage";
65
75
  inputTokens: number;
@@ -15,8 +15,8 @@ import {
15
15
  packageVersion,
16
16
  redactErrorText,
17
17
  transcriptReducer
18
- } from "./chunk-ED4R7ZCP.js";
19
- import "./chunk-MBLAXTUP.js";
18
+ } from "./chunk-WGYNTR4Q.js";
19
+ import "./chunk-XFCJXRJ2.js";
20
20
  import {
21
21
  Box_default,
22
22
  ClockContext,
@@ -33,10 +33,10 @@ import {
33
33
  useStdout,
34
34
  use_app_default,
35
35
  use_input_default
36
- } from "./chunk-NVLYFU6P.js";
36
+ } from "./chunk-F2HMO5Q2.js";
37
37
  import {
38
38
  execFileNoThrow
39
- } from "./chunk-N2S7USST.js";
39
+ } from "./chunk-RQTYHUMK.js";
40
40
  import "./chunk-S32JRDZK.js";
41
41
 
42
42
  // src/ui/app.tsx
@@ -2452,11 +2452,11 @@ function TerminalLayout({
2452
2452
  /* @__PURE__ */ jsx5(Box_default, { height: 1 }),
2453
2453
  completed.map((turn, index) => /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
2454
2454
  index > 0 ? /* @__PURE__ */ jsx5(TurnSeparator, { width: columns }) : null,
2455
- /* @__PURE__ */ jsx5(TurnView, { turn, interactive: false })
2455
+ /* @__PURE__ */ jsx5(TurnView, { turn, interactive: false, workspaceName })
2456
2456
  ] }, turn.id)),
2457
2457
  activeWithoutTasks === void 0 ? null : /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
2458
2458
  completed.length > 0 ? /* @__PURE__ */ jsx5(TurnSeparator, { width: columns }) : null,
2459
- /* @__PURE__ */ jsx5(TurnView, { turn: activeWithoutTasks, interactive: activeSession })
2459
+ /* @__PURE__ */ jsx5(TurnView, { turn: activeWithoutTasks, interactive: activeSession, workspaceName })
2460
2460
  ] })
2461
2461
  ] }),
2462
2462
  /* @__PURE__ */ jsx5(
@@ -2708,11 +2708,15 @@ function scrollUp(scroll, amount) {
2708
2708
  function TurnSeparator({ width }) {
2709
2709
  return /* @__PURE__ */ jsx5(Text, { dimColor: true, children: "\u2500".repeat(Math.max(1, width - 1)) });
2710
2710
  }
2711
- function TurnView({ turn, interactive }) {
2711
+ function TurnView({
2712
+ turn,
2713
+ interactive,
2714
+ workspaceName
2715
+ }) {
2712
2716
  if (turn.kind === "compaction") {
2713
2717
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: 1, borderStyle: "round", borderColor: "ansi:yellowBright", paddingX: 1, children: [
2714
2718
  /* @__PURE__ */ jsx5(Text, { color: "ansi:yellowBright", bold: true, children: turn.prompt }),
2715
- turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive }, block.id) : /* @__PURE__ */ jsx5(Box_default, { children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`))
2719
+ turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName }, block.id) : /* @__PURE__ */ jsx5(Box_default, { children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`))
2716
2720
  ] });
2717
2721
  }
2718
2722
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: 1, children: [
@@ -2723,7 +2727,7 @@ function TurnView({ turn, interactive }) {
2723
2727
  turn.prompt
2724
2728
  ] })
2725
2729
  ] }),
2726
- /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
2730
+ /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
2727
2731
  ] });
2728
2732
  }
2729
2733
  var DIFF_CONTENT = fileDiffLineStyle("context").contentColor;
@@ -2901,20 +2905,383 @@ function PromptLine({
2901
2905
  }
2902
2906
  function StatusBlockView({
2903
2907
  block,
2904
- interactive
2908
+ interactive,
2909
+ workspaceName
2905
2910
  }) {
2906
2911
  const visibleBlock = cliToolTitle(block);
2907
2912
  const outcome = cliToolOutcome(block);
2908
- const primary = visibleBlock.activity === "model" || visibleBlock.task !== void 0 ? /* @__PURE__ */ jsx5(TaskStatusLine, { block: visibleBlock, interactive }) : visibleBlock.state === "completed" && visibleBlock.presentation !== void 0 ? /* @__PURE__ */ jsx5(FileDiffView, { presentation: visibleBlock.presentation }) : /* @__PURE__ */ jsx5(StatusLine, { block: visibleBlock, interactive });
2913
+ const primary = visibleBlock.activity === "model" || visibleBlock.task !== void 0 ? /* @__PURE__ */ jsx5(TaskStatusLine, { block: visibleBlock, interactive }) : visibleBlock.state === "completed" && visibleBlock.presentation !== void 0 ? /* @__PURE__ */ jsx5(ToolPresentationView, { presentation: visibleBlock.presentation, workspaceName }) : /* @__PURE__ */ jsx5(StatusLine, { block: visibleBlock, interactive });
2909
2914
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
2910
2915
  primary,
2911
2916
  outcome === void 0 ? null : /* @__PURE__ */ jsx5(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ jsxs4(Text, { dimColor: true, children: [
2912
2917
  "\u2514 ",
2913
2918
  outcome
2914
2919
  ] }) }),
2915
- block.details === void 0 ? null : /* @__PURE__ */ jsx5(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.details }) })
2920
+ block.details === void 0 || block.presentation?.kind === "changeset" ? null : /* @__PURE__ */ jsx5(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.details }) })
2921
+ ] });
2922
+ }
2923
+ function ToolPresentationView({
2924
+ presentation,
2925
+ workspaceName
2926
+ }) {
2927
+ if (presentation.kind === "changeset") return /* @__PURE__ */ jsx5(ChangeSetPresentationView, { presentation, workspaceName });
2928
+ if (presentation.kind === "file-change") return /* @__PURE__ */ jsx5(FileDiffView, { presentation });
2929
+ if (presentation.kind === "terminal") return /* @__PURE__ */ jsx5(CommandPresentationView, { presentation });
2930
+ if (presentation.kind === "web") return /* @__PURE__ */ jsx5(WebPresentationView, { presentation });
2931
+ if (presentation.kind === "job") return /* @__PURE__ */ jsx5(JobPresentationView, { presentation });
2932
+ return /* @__PURE__ */ jsx5(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ jsxs4(Text, { children: [
2933
+ presentation.title,
2934
+ presentation.summary ? ` \xB7 ${presentation.summary}` : ""
2935
+ ] }) });
2936
+ }
2937
+ var CLI_CHANGESET_FILE_LIMIT = 8;
2938
+ var CHANGESET_TONE = "#b99bf8";
2939
+ function ChangeSetPresentationView({
2940
+ presentation,
2941
+ workspaceName
2942
+ }) {
2943
+ const visibleFiles = presentation.files.slice(0, CLI_CHANGESET_FILE_LIMIT);
2944
+ const added = presentation.files.reduce((total, file) => total + file.added, 0);
2945
+ const removed = presentation.files.reduce((total, file) => total + file.removed, 0);
2946
+ const fileLabel = presentation.files.length === 1 ? "FILE" : "FILES";
2947
+ const showing = presentation.files.length > visibleFiles.length ? ` \xB7 Showing ${visibleFiles.length} of ${presentation.files.length}` : "";
2948
+ return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
2949
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
2950
+ /* @__PURE__ */ jsx5(Text, { color: CHANGESET_TONE, children: "\u250C\u2500 " }),
2951
+ /* @__PURE__ */ jsxs4(Text, { color: CHANGESET_TONE, bold: true, children: [
2952
+ "CHANGESET \xB7 ",
2953
+ presentation.files.length,
2954
+ " ",
2955
+ fileLabel
2956
+ ] })
2957
+ ] }),
2958
+ visibleFiles.map((file) => {
2959
+ const operation = file.operation.toUpperCase();
2960
+ const stats = `+${file.added} -${file.removed}`;
2961
+ return /* @__PURE__ */ jsxs4(Box_default, { children: [
2962
+ /* @__PURE__ */ jsx5(Text, { color: CHANGESET_TONE, children: "\u2502 " }),
2963
+ /* @__PURE__ */ jsx5(Text, { color: changeSetOperationColor(file.operation), bold: true, children: operation.padEnd(8) }),
2964
+ /* @__PURE__ */ jsxs4(Text, { children: [
2965
+ /* @__PURE__ */ jsxs4(Text, { color: "ansi:green", children: [
2966
+ "+",
2967
+ file.added
2968
+ ] }),
2969
+ " ",
2970
+ /* @__PURE__ */ jsxs4(Text, { color: "#e06c50", children: [
2971
+ "-",
2972
+ file.removed
2973
+ ] }),
2974
+ " ".repeat(Math.max(1, 9 - stats.length))
2975
+ ] }),
2976
+ /* @__PURE__ */ jsx5(Text, { color: "ansi:whiteBright", wrap: "truncate-end", children: workspaceRelativePath(file.path, workspaceName) })
2977
+ ] }, `${file.operation}:${file.path}`);
2978
+ }),
2979
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
2980
+ /* @__PURE__ */ jsx5(Text, { color: CHANGESET_TONE, children: "\u2514\u2500 " }),
2981
+ /* @__PURE__ */ jsxs4(Text, { children: [
2982
+ /* @__PURE__ */ jsxs4(Text, { color: "ansi:green", children: [
2983
+ "+",
2984
+ added
2985
+ ] }),
2986
+ " ",
2987
+ /* @__PURE__ */ jsxs4(Text, { color: "#e06c50", children: [
2988
+ "-",
2989
+ removed
2990
+ ] }),
2991
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: showing })
2992
+ ] })
2993
+ ] })
2994
+ ] });
2995
+ }
2996
+ function changeSetOperationColor(operation) {
2997
+ if (operation === "create") return "ansi:green";
2998
+ if (operation === "delete") return "#e06c50";
2999
+ return "ansi:cyanBright";
3000
+ }
3001
+ function workspaceRelativePath(value, workspaceName) {
3002
+ const normalized = value.replace(/\\/gu, "/");
3003
+ const marker = `/${workspaceName}/`;
3004
+ const markerIndex = normalized.toLocaleLowerCase().lastIndexOf(marker.toLocaleLowerCase());
3005
+ if (markerIndex >= 0) return normalized.slice(markerIndex + marker.length);
3006
+ const rootPrefix = `${workspaceName}/`;
3007
+ if (normalized.toLocaleLowerCase().startsWith(rootPrefix.toLocaleLowerCase())) return normalized.slice(rootPrefix.length);
3008
+ return normalized;
3009
+ }
3010
+ var CLI_COMMAND_OUTPUT_LINE_LIMIT = 16;
3011
+ function CommandPresentationView({
3012
+ presentation
3013
+ }) {
3014
+ const state = presentation.state ?? commandState(presentation.exitCode);
3015
+ const tone = jobStateColor(state);
3016
+ const label = presentation.variant === "terminal" ? "TERMINAL" : "COMMAND";
3017
+ const hasStdout = jobOutputLines(presentation.stdout ?? "").length > 0;
3018
+ const hasStderr = jobOutputLines(presentation.stderr ?? "").length > 0;
3019
+ const perStreamLimit = hasStdout && hasStderr ? Math.floor(CLI_COMMAND_OUTPUT_LINE_LIMIT / 2) : CLI_COMMAND_OUTPUT_LINE_LIMIT;
3020
+ const stdout = boundedCommandLines(presentation.stdout ?? "", perStreamLimit);
3021
+ const stderr = boundedCommandLines(presentation.stderr ?? "", perStreamLimit);
3022
+ return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
3023
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3024
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u250C\u2500 " }),
3025
+ /* @__PURE__ */ jsxs4(Text, { color: tone, bold: true, children: [
3026
+ label,
3027
+ " \xB7 ",
3028
+ state.toUpperCase()
3029
+ ] })
3030
+ ] }),
3031
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3032
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2502 " }),
3033
+ /* @__PURE__ */ jsx5(Text, { color: "ansi:whiteBright", bold: true, wrap: "truncate-end", children: presentation.command ?? presentation.title })
3034
+ ] }),
3035
+ stdout.total === 0 ? null : /* @__PURE__ */ jsx5(CommandOutputSection, { label: "OUTPUT", value: stdout, tone, error: false }),
3036
+ stderr.total === 0 ? null : /* @__PURE__ */ jsx5(CommandOutputSection, { label: "ERROR", value: stderr, tone, error: true }),
3037
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3038
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2514\u2500 " }),
3039
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: commandFooter(presentation, state) })
3040
+ ] })
3041
+ ] });
3042
+ }
3043
+ function CommandOutputSection({
3044
+ label,
3045
+ value,
3046
+ tone,
3047
+ error
3048
+ }) {
3049
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
3050
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3051
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u251C\u2500 " }),
3052
+ /* @__PURE__ */ jsx5(Text, { color: error ? "#e06c50" : tone, bold: true, children: label }),
3053
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` \xB7 ${value.total} ${value.total === 1 ? "LINE" : "LINES"}` })
3054
+ ] }),
3055
+ value.head.map((line, index) => /* @__PURE__ */ jsx5(CommandOutputLine, { line, tone, error }, `head:${index}`)),
3056
+ value.hidden === 0 ? null : /* @__PURE__ */ jsxs4(Box_default, { children: [
3057
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2502 " }),
3058
+ /* @__PURE__ */ jsxs4(Text, { dimColor: true, children: [
3059
+ "\u2026 ",
3060
+ value.hidden,
3061
+ " ",
3062
+ value.hidden === 1 ? "line" : "lines",
3063
+ " hidden"
3064
+ ] })
3065
+ ] }),
3066
+ value.tail.map((line, index) => /* @__PURE__ */ jsx5(CommandOutputLine, { line, tone, error }, `tail:${index}`))
3067
+ ] });
3068
+ }
3069
+ function CommandOutputLine({ line, tone, error }) {
3070
+ return /* @__PURE__ */ jsxs4(Box_default, { children: [
3071
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2502 " }),
3072
+ /* @__PURE__ */ jsx5(Text, { color: error ? "#e06c50" : "#b8bcc2", wrap: "truncate-end", children: line === "" ? " " : line })
3073
+ ] });
3074
+ }
3075
+ function boundedCommandLines(output, limit) {
3076
+ const lines = jobOutputLines(output);
3077
+ if (lines.length <= limit) return { total: lines.length, head: lines, tail: [], hidden: 0 };
3078
+ const headCount = Math.ceil(limit / 2);
3079
+ const tailCount = Math.floor(limit / 2);
3080
+ return {
3081
+ total: lines.length,
3082
+ head: lines.slice(0, headCount),
3083
+ tail: lines.slice(-tailCount),
3084
+ hidden: lines.length - headCount - tailCount
3085
+ };
3086
+ }
3087
+ function commandState(exitCode) {
3088
+ if (exitCode === void 0) return "running";
3089
+ return exitCode === 0 ? "completed" : "failed";
3090
+ }
3091
+ function commandFooter(presentation, state) {
3092
+ const parts = [presentation.exitCode === void 0 ? state : presentation.exitCode === null ? "no exit code" : `exit ${presentation.exitCode}`];
3093
+ if (presentation.truncated === true) parts.push("output truncated");
3094
+ return parts.join(" \xB7 ");
3095
+ }
3096
+ var CLI_WEB_RESULT_LIMIT = 5;
3097
+ function WebPresentationView({
3098
+ presentation
3099
+ }) {
3100
+ const items = presentation.items ?? [];
3101
+ if (presentation.items === void 0) return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
3102
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3103
+ /* @__PURE__ */ jsx5(Text, { color: "#5f87af", children: "\u250C\u2500 " }),
3104
+ /* @__PURE__ */ jsx5(Text, { color: "ansi:cyanBright", bold: true, children: "WEB FETCH" }),
3105
+ presentation.summary === void 0 ? null : /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` \xB7 ${presentation.summary}` })
3106
+ ] }),
3107
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3108
+ /* @__PURE__ */ jsx5(Text, { color: "#5f87af", children: "\u2502 " }),
3109
+ /* @__PURE__ */ jsx5(Text, { color: "ansi:whiteBright", wrap: "truncate-end", children: presentation.url ?? presentation.title })
3110
+ ] }),
3111
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3112
+ /* @__PURE__ */ jsx5(Text, { color: "#5f87af", children: "\u2514\u2500 " }),
3113
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: "Page content added to context" })
3114
+ ] })
3115
+ ] });
3116
+ const visibleItems = items.slice(0, CLI_WEB_RESULT_LIMIT);
3117
+ const query = presentation.title.replace(/^Search:\s*/iu, "").trim();
3118
+ return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
3119
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3120
+ /* @__PURE__ */ jsx5(Text, { color: "#5f87af", children: "\u250C\u2500 " }),
3121
+ /* @__PURE__ */ jsx5(Text, { color: "ansi:cyanBright", bold: true, children: "WEB SEARCH" }),
3122
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` \xB7 ${items.length} ${items.length === 1 ? "RESULT" : "RESULTS"}` })
3123
+ ] }),
3124
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3125
+ /* @__PURE__ */ jsx5(Text, { color: "#5f87af", children: "\u2502 " }),
3126
+ /* @__PURE__ */ jsx5(Text, { color: "ansi:whiteBright", bold: true, wrap: "truncate-end", children: query })
3127
+ ] }),
3128
+ /* @__PURE__ */ jsx5(Text, { color: "#5f87af", children: "\u2502" }),
3129
+ visibleItems.map((item, index) => /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
3130
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3131
+ /* @__PURE__ */ jsx5(Text, { color: "#5f87af", children: "\u2502 " }),
3132
+ /* @__PURE__ */ jsx5(Text, { color: "ansi:cyanBright", bold: true, children: String(index + 1).padStart(2, "0") }),
3133
+ /* @__PURE__ */ jsxs4(Text, { color: "ansi:whiteBright", wrap: "truncate-end", children: [
3134
+ " ",
3135
+ item.title
3136
+ ] })
3137
+ ] }),
3138
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3139
+ /* @__PURE__ */ jsx5(Text, { color: "#5f87af", children: "\u2502 " }),
3140
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, wrap: "truncate-end", children: compactWebSource(item.url) })
3141
+ ] })
3142
+ ] }, item.url)),
3143
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3144
+ /* @__PURE__ */ jsx5(Text, { color: "#5f87af", children: "\u2514\u2500 " }),
3145
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: items.length > visibleItems.length ? `Showing ${visibleItems.length} of ${items.length}` : `${items.length} ${items.length === 1 ? "source" : "sources"}` })
3146
+ ] })
3147
+ ] });
3148
+ }
3149
+ function compactWebSource(value) {
3150
+ try {
3151
+ const url = new URL(value);
3152
+ const hostname = url.hostname.replace(/^www\./iu, "");
3153
+ const path = url.pathname === "/" ? "" : url.pathname.replace(/\/$/u, "");
3154
+ return `${hostname}${path}`;
3155
+ } catch {
3156
+ return value;
3157
+ }
3158
+ }
3159
+ var CLI_JOB_LOG_LINE_LIMIT = 12;
3160
+ var CLI_JOB_LIST_LIMIT = 8;
3161
+ function JobPresentationView({
3162
+ presentation
3163
+ }) {
3164
+ if (presentation.action === "list") return /* @__PURE__ */ jsx5(JobListPresentationView, { presentation });
3165
+ const state = presentation.state ?? "running";
3166
+ const tone = jobStateColor(state);
3167
+ const lines = jobOutputLines(presentation.output ?? "");
3168
+ const visibleLines = lines.slice(-CLI_JOB_LOG_LINE_LIMIT);
3169
+ const hiddenLines = lines.length - visibleLines.length;
3170
+ const showLog = presentation.action === "read" || presentation.action === "kill" || lines.length > 0;
3171
+ return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
3172
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3173
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u250C\u2500 " }),
3174
+ /* @__PURE__ */ jsxs4(Text, { color: tone, bold: true, children: [
3175
+ "JOB \xB7 ",
3176
+ state.toUpperCase()
3177
+ ] })
3178
+ ] }),
3179
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3180
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2502 " }),
3181
+ /* @__PURE__ */ jsx5(Text, { color: "ansi:whiteBright", bold: true, children: presentation.id ?? "unknown job" }),
3182
+ presentation.jobKind === void 0 ? null : /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` \xB7 ${presentation.jobKind}` })
3183
+ ] }),
3184
+ presentation.label === void 0 ? null : /* @__PURE__ */ jsxs4(Box_default, { children: [
3185
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2502 " }),
3186
+ /* @__PURE__ */ jsx5(Text, { color: "ansi:whiteBright", wrap: "truncate-end", children: presentation.label })
3187
+ ] }),
3188
+ presentation.error === void 0 ? null : /* @__PURE__ */ jsxs4(Box_default, { children: [
3189
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2502 " }),
3190
+ /* @__PURE__ */ jsx5(Text, { color: "#e06c50", wrap: "truncate-end", children: presentation.error })
3191
+ ] }),
3192
+ showLog ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
3193
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3194
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u251C\u2500 " }),
3195
+ /* @__PURE__ */ jsx5(Text, { color: tone, bold: true, children: "LOG" }),
3196
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: lines.length === 0 ? " \xB7 NO NEW OUTPUT" : ` \xB7 ${lines.length} ${lines.length === 1 ? "LINE" : "LINES"}` })
3197
+ ] }),
3198
+ hiddenLines > 0 ? /* @__PURE__ */ jsxs4(Box_default, { children: [
3199
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2502 " }),
3200
+ /* @__PURE__ */ jsxs4(Text, { dimColor: true, children: [
3201
+ "\u2026 ",
3202
+ hiddenLines,
3203
+ " earlier ",
3204
+ hiddenLines === 1 ? "line" : "lines",
3205
+ " hidden"
3206
+ ] })
3207
+ ] }) : null,
3208
+ visibleLines.map((line, index) => /* @__PURE__ */ jsx5(JobLogLine, { line, tone }, `${index}:${line}`))
3209
+ ] }) : null,
3210
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3211
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2514\u2500 " }),
3212
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: jobFooter(presentation) })
3213
+ ] })
2916
3214
  ] });
2917
3215
  }
3216
+ function JobListPresentationView({
3217
+ presentation
3218
+ }) {
3219
+ const jobs = presentation.jobs ?? [];
3220
+ const visibleJobs = jobs.slice(0, CLI_JOB_LIST_LIMIT);
3221
+ return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
3222
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3223
+ /* @__PURE__ */ jsx5(Text, { color: "#d7a657", children: "\u250C\u2500 " }),
3224
+ /* @__PURE__ */ jsxs4(Text, { color: "#d7a657", bold: true, children: [
3225
+ "JOBS \xB7 ",
3226
+ jobs.length
3227
+ ] })
3228
+ ] }),
3229
+ jobs.length === 0 ? /* @__PURE__ */ jsxs4(Box_default, { children: [
3230
+ /* @__PURE__ */ jsx5(Text, { color: "#d7a657", children: "\u2502 " }),
3231
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: "No background jobs" })
3232
+ ] }) : visibleJobs.map((job) => /* @__PURE__ */ jsxs4(Box_default, { children: [
3233
+ /* @__PURE__ */ jsx5(Text, { color: "#d7a657", children: "\u2502 " }),
3234
+ /* @__PURE__ */ jsx5(Text, { color: jobStateColor(job.state), bold: true, children: job.state.toUpperCase().padEnd(9) }),
3235
+ /* @__PURE__ */ jsxs4(Text, { color: "ansi:whiteBright", children: [
3236
+ " ",
3237
+ job.id
3238
+ ] }),
3239
+ /* @__PURE__ */ jsxs4(Text, { dimColor: true, wrap: "truncate-end", children: [
3240
+ " \xB7 ",
3241
+ job.label
3242
+ ] })
3243
+ ] }, job.id)),
3244
+ /* @__PURE__ */ jsxs4(Box_default, { children: [
3245
+ /* @__PURE__ */ jsx5(Text, { color: "#d7a657", children: "\u2514\u2500 " }),
3246
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: jobs.length > visibleJobs.length ? `Showing ${visibleJobs.length} of ${jobs.length}` : `${jobs.length} ${jobs.length === 1 ? "job" : "jobs"}` })
3247
+ ] })
3248
+ ] });
3249
+ }
3250
+ function JobLogLine({ line, tone }) {
3251
+ const error = /^(?:\[stderr\]|ERR(?:OR)?(?:!|:|\s)|npm ERR!)/iu.test(line.trimStart());
3252
+ const stage = /^={3}.*={3}$/u.test(line.trim());
3253
+ return /* @__PURE__ */ jsxs4(Box_default, { children: [
3254
+ /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2502 " }),
3255
+ /* @__PURE__ */ jsx5(
3256
+ Text,
3257
+ {
3258
+ ...error ? { color: "#e06c50" } : stage ? { color: "ansi:cyanBright", bold: true } : { dimColor: true },
3259
+ wrap: "truncate-end",
3260
+ children: line === "" ? " " : line
3261
+ }
3262
+ )
3263
+ ] });
3264
+ }
3265
+ function jobOutputLines(output) {
3266
+ if (output === "") return [];
3267
+ const lines = output.replace(/\r\n?/gu, "\n").split("\n");
3268
+ if (lines.at(-1) === "") lines.pop();
3269
+ return lines;
3270
+ }
3271
+ function jobStateColor(state) {
3272
+ if (state === "completed") return "ansi:green";
3273
+ if (state === "failed") return "#e06c50";
3274
+ if (state === "cancelled") return "#858585";
3275
+ return "#d7a657";
3276
+ }
3277
+ function jobFooter(presentation) {
3278
+ const parts = [];
3279
+ if (presentation.exitCode !== void 0) parts.push(presentation.exitCode === null ? "no exit code" : `exit ${presentation.exitCode}`);
3280
+ else parts.push(presentation.state ?? "running");
3281
+ if (presentation.cursor !== void 0) parts.push(`cursor ${presentation.cursor}`);
3282
+ if (presentation.truncated === true) parts.push("output truncated");
3283
+ return parts.join(" \xB7 ");
3284
+ }
2918
3285
  function cliToolTitle(block) {
2919
3286
  if (block.tool === void 0 || block.presentation !== void 0) return block;
2920
3287
  const input = record(block.tool.input);