diff --git a/.tickets/_docs/ROADMAP.md b/.tickets/_docs/ROADMAP.md new file mode 100644 index 00000000..2cf31a19 --- /dev/null +++ b/.tickets/_docs/ROADMAP.md @@ -0,0 +1,93 @@ +# KaitenCode Roadmap + +> Authoritative "what's done / what's left" as of **2026-06-12**, verified against +> `main` HEAD. Supersedes the tier table in [STATUS.md](./STATUS.md) and the +> session-scoped [handoffs/REMAINING_WORK.md](./handoffs/REMAINING_WORK.md) (both +> kept for history). Released: **v1.0.0** (Linux `.deb` + AppImage, 2026-06-07). + +Context: the goal is to **daily-drive KaitenCode as the primary surface for +agent-driven dev work** (in place of raw terminals). Priorities below are ranked +by that goal — interactive-mode reliability first, since it's the path that +replaces the terminal. + +--- + +## ✅ Shipped & wired + +- Source-of-truth refactor (Fronts 1–3): all surfaces (UI / HTTP API / MCP / chef) + funnel through shared `pipeline::*_service`; MCP routes through `/api/*`. +- Reliability cluster (PR #256): queue-stall, worktree leak, startup false-success, + over-spawn, sentinel scan — fixed before v1. +- Interactive mode (PR #257): real `claude`/`codex` TUI in per-task tmux, opt-in + setting + per-chat toggle. +- Terminal-harness B1–B4: create-UI options, interactive promotion, chef terminal, + CLI health check. +- Local HTTP API on by default (PR #258). +- Discord MVP (PR #260): task→thread + agent-output streaming (one-way). +- Trigger UX redesign phase 1 (PR #261): sentence-builder + recipes. +- Settings runtime tab (PR #262): backend `AppSettings` via System → Runtime. +- MCP: 25 tools; source attribution + recursion guard (migration 046); + mark_complete/dependencies route through `/api/*`. + +--- + +## 🔴 Critical now (blocks comfortable daily-driving) + +1. **Interactive readiness hard-kill.** Readiness is a 5s glyph-scrape that + *hard-kills* the tmux session on slow cold start / CLI UI change + (`chat/bridge.rs` ~2469/2610). Make it quiescence-based (adaptive), not a fixed + budget. → board task "Interactive: adaptive readiness". +2. **Interactive codex parity.** `--append-system-prompt` likely invalid for + interactive codex (would fail to start); `--resume` not wired (`spawn_interactive_cli` + warns + ignores `resume_id`). Verify against a real codex binary; drop/guard the + bad flag. Claude is solid. → board task "Interactive: codex parity". +3. **Two-claude footgun (env hygiene).** Stale `/usr/local/bin/claude` (2.1.138 npm + copy) can win in some PATHs. App now resolves absolute paths correctly, but + `rm /usr/local/bin/claude` removes the ambiguity. One-liner, do it. + +## 🟡 Important (rough edges, not blockers) + +4. **Interactive completion is panel-bound.** The "agent done" advisory only fires + while the panel is attached — no persisted flag / card badge. Needs a migration + + badge so it survives panel close. → board task "Interactive: persist advisory". +5. **`effort_level` not wired into the trigger path.** It's a real DB field + has a + `thinking-selector.tsx` and works in the interactive chat panel, but no + `effort_level`→CLI plumbing in `pipeline/spawn.rs` (the headless `spawn_cli` + path). Either wire `--effort`/`-c model_reasoning_effort` into the trigger + command or document it as interactive-only. +6. **Checklist as per-workspace roadmap + MCP surface.** Checklist has zero + `/api/*` routes and zero MCP tools. Make it agent-readable/maintainable so it + can serve as the project roadmap. **Spec:** [specs/CHECKLIST_AS_ROADMAP.md](./specs/CHECKLIST_AS_ROADMAP.md). + Planned, deferred 2026-06-12. +7. **MCP UI-only gaps.** No MCP tool for `update_column`/`delete_column`/ + `reorder_columns`, `update_script`/`delete_script`, per-task runtime-mode + override, or agent control (inject/interrupt/pause/restart/switch-model). Each + needs an `/api/*` route first. Add as needed (the checklist spec establishes the + route+tool pattern). + +## ⚪ Inert / deferred (known, intentional) + +- Custom keyboard shortcuts render but are inert (`shortcuts-tab.tsx:54`). +- Providers OpenRouter / Google AI / Ollama are "Coming soon" cards. +- Voice/Whisper built but gated out of the default build (`voice` Cargo feature). +- macOS release: matrix disabled pending Apple Developer ID + notarization secrets. + Windows unsupported by design (tmux/jq/sh). +- Discord: one-way only; reply-routing / `#chef` / bidirectional are later slices + (`.tickets/discord/`). +- MCP source-attribution Part 3 (spawned-by badge + human/agent filter); interactive/ + managed spawn-path env threading (headless done). + +--- + +## Needs testing (no automated coverage) + +- Interactive codex round-trip (resume, append-system-prompt, sentinel) on a real + binary — gates item #2. +- Live CLI smoke test (throwaway-tmux round-trip) — designed in B4, not built. +- Discord thread streaming end-to-end. +- 5-agent-per-workspace concurrency under real load (queue promotion). + +## Suggested order + +`#1 readiness` → `#2 codex parity` (+ its testing) → `#3 rm stale claude` (free) → +`#4 persist advisory` → `#5 effort wiring` → `#6 checklist/roadmap MCP` → `#7 MCP gaps`. diff --git a/.tickets/_docs/STATUS.md b/.tickets/_docs/STATUS.md index 16afafbb..23011dc0 100644 --- a/.tickets/_docs/STATUS.md +++ b/.tickets/_docs/STATUS.md @@ -42,7 +42,7 @@ Phase 6 🟡 — CliSessionManager removed, but AgentRunner + PtyManager still u ### MCP Server (kaitencode-mcp) -Standalone Rust binary (19 tools), shares Cargo workspace with Tauri app for WAL-compatible concurrent DB access. 17 unit tests. +Standalone Rust binary (25 tools), shares Cargo workspace with Tauri app for WAL-compatible concurrent DB access. 18+ unit tests. ### Database diff --git a/.tickets/_docs/specs/CHECKLIST_AS_ROADMAP.md b/.tickets/_docs/specs/CHECKLIST_AS_ROADMAP.md new file mode 100644 index 00000000..55525017 --- /dev/null +++ b/.tickets/_docs/specs/CHECKLIST_AS_ROADMAP.md @@ -0,0 +1,114 @@ +# Spec — Checklist as Per-Workspace Roadmap (+ MCP/HTTP surface) + +> Status: **PLANNED, not started** (deferred 2026-06-12 — "more critical things first"). +> Owner: TBD. Effort: ~1 day (routes + MCP tools + tests + rebuild). + +## Goal + +Repurpose the existing **checklist** feature as a high-level, per-workspace +**project roadmap** that both the human (UI) and agents (MCP) can read and +maintain. Today the checklist is a release-readiness widget with *no* agent +surface; this spec closes that gap without inventing a new feature. + +## Why checklist (not pipeline templates) + +They are genuinely different things (verified 2026-06-12): + +| | Pipeline Template | **Checklist** | +|---|---|---| +| Scope | Global, stateless blueprint | **Already per-workspace** (1 per ws) | +| Captures | Column names/icons/triggers | Categories → items + state | +| Item state | — | `checked`, `notes`, `position`, `linked_task_id`, auto-detect | +| Agent surface | 3 MCP tools | **none** | + +The checklist is already workspace-scoped, ordered, supports notes, can **link an +item to a board task** (`linked_task_id`), and can **auto-detect** completion from +repo state (file-exists / file-contains / file-absent / command-succeeds). That is +a near-perfect roadmap primitive — it just isn't reachable by agents. + +## Current state (what exists) + +- **DB** (`src-tauri/src/db/checklist.rs`): full CRUD already implemented as + `(conn, …)` functions — `insert_checklist`, `get_workspace_checklist`, + `insert_checklist_category`, `list_checklist_categories`, + `insert_checklist_item`, `list_checklist_items`, `update_checklist_item` + (toggle `checked`), `update_checklist_item_details`, `delete_checklist_item`, + `link_checklist_item_to_task`, `create_checklist_item_with_detect`, … +- **Tauri commands** (`src-tauri/src/commands/checklist.rs`): 15 commands wrapping + the above (used by the UI). +- **Tables** (migration `005_checklists.sql`, `019_checklist_autodetect.sql`): + `checklists(workspace_id)`, `checklist_categories(checklist_id, name, icon, + position)`, `checklist_items(category_id, text, checked, notes, position, + detect_type, detect_config, auto_detected, linked_task_id)`. + +## Gap + +- **Zero `/api/*` routes** for checklist. MCP mutation tools route through + `/api/*`, so nothing is reachable from MCP today. +- **Zero MCP tools.** +- One structural constraint: **exactly one checklist per workspace** + (`commands/checklist.rs:134`). Fine for a *single* roadmap per project; only a + blocker if we later want multiple named roadmaps per workspace. + +## Design + +Mirror the established `create_column` pattern exactly: an `/api/*` handler that +takes `AxumState>`, grabs a conn via `get_db!(api)`, calls the +`db::checklist::*` fn directly, then emits an event and returns `ok_response`. +Add a new event `checklist:changed { workspaceId }` (typed struct, +`#[serde(rename_all = "camelCase")]`) + a `useChecklistSync` hook so UI refreshes +live when an agent edits the roadmap. + +### New HTTP routes (`src-tauri/src/api.rs`, registered in the `protected` router ~L893) + +| Route | Calls | Notes | +|---|---|---| +| `GET /api/checklist?workspace_id=` | `get_workspace_checklist` + categories + items | read; returns full nested roadmap | +| `POST /api/checklist/ensure` | `insert_checklist` if absent | idempotent; create-on-first-use | +| `POST /api/checklist/category` | `insert_checklist_category` | add roadmap phase | +| `POST /api/checklist/item` | `insert_checklist_item` / `create_checklist_item_with_detect` | add milestone (optional auto-detect) | +| `POST /api/checklist/item/update` | `update_checklist_item` (toggle) + `update_checklist_item_details` (text/notes) + `link_checklist_item_to_task` | the workhorse | +| `POST /api/checklist/item/delete` | `delete_checklist_item` | | + +All request structs `#[derive(Deserialize)]` snake_case (match existing API +convention — note: the API request structs are snake_case, e.g. `MoveTaskReq` +uses `id`/`target_column_id`, *not* camelCase). + +### New MCP tools (`mcp-server/src/main.rs`) + +| Tool | Route | Scope | +|---|---|---| +| `get_checklist` | `GET /api/checklist` | read-only — works without app | +| `checklist_update` | `POST /api/checklist/item/update` | toggle done / edit text / link task | +| `checklist_add_item` | `POST /api/checklist/item` | add milestone | +| `checklist_add_category` | `POST /api/checklist/category` | add phase | + +Read-only `get_checklist` should also support the test-only direct-DB fallback +(like the other read tools); mutations require the running app (`/api/*`). + +### Phasing + +1. **P1 (minimal, unblocks roadmap):** `GET /api/checklist` + `POST + /api/checklist/item/update` → MCP `get_checklist` + `checklist_update`. + Agents can read the roadmap and tick milestones. +2. **P2 (agent-maintainable):** add category + item create/delete routes + tools. + Agents can build/restructure the roadmap. +3. **P3 (optional):** drop the 1-per-workspace constraint + add a `name` column to + `checklists` for multiple named roadmaps per workspace (migration after 047). + Only if multi-roadmap is wanted. + +### Tests + +- MCP: extend `mcp-server/src/main.rs` test module (currently 18 tests) with a + `test_checklist_update_*` round-trip against the test-DB fallback. +- API: a handler test if the api.rs test harness supports it; otherwise rely on + the MCP round-trip + a manual smoke test after rebuild. + +## Open decisions (for whoever picks this up) + +- **Single vs multiple roadmaps per workspace** — P3 above. Default: keep single + (zero schema change) until there's a concrete need. +- **Auto-detect as roadmap signal** — should `checklist_update` allow agents to + set `detect_config` so a milestone self-checks from repo state? Powerful for a + roadmap ("done when `cargo test` passes"), but lets agents wire shell commands. + Default: P2 read-only on detect; no agent-set detect until reviewed. diff --git a/.tickets/_docs/specs/CHEF_TOOL_BASED_CONTEXT.md b/.tickets/_docs/specs/CHEF_TOOL_BASED_CONTEXT.md new file mode 100644 index 00000000..57c69a2c --- /dev/null +++ b/.tickets/_docs/specs/CHEF_TOOL_BASED_CONTEXT.md @@ -0,0 +1,102 @@ +# Spec — Tool-based board context for the chef (orchestrator) + +> Status: **IMPLEMENTED behind `KAITENCODE_CHEF_TOOLS=1`** (2026-06-13). Verified +> end-to-end: the chef calls `mcp__kaitencode__get_board` with the workspace id and +> answers from the live board. Default-off; falls back to the embedded board when +> the flag is off OR `kaitencode-mcp` can't be located. +> +> **Open follow-ups before promoting to default:** +> - **Bundle `kaitencode-mcp`** with the app (tauri `externalBin`/resources) — it +> isn't shipped today, so the feature only works where the binary is on PATH or a +> sibling of the exe (dev). Resolution: `commands/orchestrator/stream_cli.rs::locate_kaitencode_mcp`. +> - The chef still runs `ToolSearch` before the real tool call (a built-in of the +> installed claude, not removed by `--strict-mcp-config`) — harmless, just an extra step. +> - Writes still use the action-block protocol; only reads moved to the tool. Moving +> writes to MCP too (create_task/move_task) is a separate, larger step. +> - API chef (`stream_api.rs`) unchanged — this is CLI-only. + +## Problem + +Today the chef embeds the **entire board state in the prompt every turn**: +- `context::build_cli_system_prompt` puts a `[id] title - description(≤120c)` + snapshot of every task (across all columns) + the action-protocol prompts into + `--system-prompt`. +- `chef.rs::augment_message` historically *also* prepended that snapshot to the + user message (now gated to `--resume` turns only — the quick dedup). + +Consequences: +- Prompt size scales with board size, not with what the turn needs. +- It feeds the argv/E2BIG ceiling (see the stdin fix — the `--system-prompt` + board is still on argv; a large enough board can still trip 128 KiB). +- Most turns ("rename task X", "what's in Review?") don't need the *whole* board. + +## Goal + +Stop embedding the board. Give the chef a **read tool** and let the model fetch +board state on demand — tiny prompts, scales to any board, removes the E2BIG +residual. + +## Design + +The chef CLI path spawns `claude -p`. Two ways to give it a read tool: + +### Option A — MCP (preferred) +Wire the existing **`kaitencode-mcp`** server (already exposes `get_board`, +`get_task`, `get_workspaces`, …) into the chef's claude invocation via +`--mcp-config` (or `--mcp-server`). The model calls `get_board` when it needs +state. +- `kaitencode-mcp` is already spawned in some flows (see `bridge.rs:1586` env + threading) and shares the rusqlite build for WAL-safe concurrent reads, so + read tools work without the app for the DB, but mutations still route through + `/api/*`. +- **VERIFIED 2026-06-13 (spike):** `claude -p --mcp-config + --allowedTools "mcp__kaitencode__get_workspaces,..." --output-format stream-json` + loads `kaitencode-mcp`, the model calls `mcp__kaitencode__get_workspaces`, the + tool_result (real DB data) comes back, and the answer is correct. The + tool_use/tool_result events appear in the stream-json (transcript already renders + tool calls). claude 2.1.177. → **Option A is GO.** + - Nuance: in the spike, claude had the *user's* global MCP servers loaded too, so + it went through `ToolSearch` (deferred-tool discovery) before the real call. + Chef should spawn with an **isolated** config (only kaitencode, via + `--strict-mcp-config` + `--mcp-config`) so there's no ToolSearch indirection and + the tool is directly available. Also pre-approve the read tools with + `--allowedTools` (no permission prompt in headless). + - Note: pass the prompt on **stdin** (the E2BIG fix already does) — a positional + prompt makes claude wait ~3s for stdin ("no stdin data received in 3s"). + +### Option B — extend the action protocol with a read action +Add a `get_board` "action" the model emits (like the existing `create_task` +blocks); the backend intercepts it, runs the read, and feeds the result back as a +follow-up turn. Reuses the existing `parse_cli_action_blocks` machinery — no MCP +dependency — but needs a multi-turn read/observe loop the chef doesn't have yet. + +**Recommendation:** Option A if the `-p` + MCP spike works; otherwise B. + +## Changes (Option A) + +1. `build_cli_system_prompt`: drop the `Current tasks:` snapshot; replace with a + one-liner — "Call `get_board` to see the current board; call it again after you + change something." Keep columns list (cheap, stable) + the action protocol for + *writes*. +2. `chef.rs`: delete `augment_message` / `format_context_message` board injection + entirely (the quick-dedup branch goes away too). +3. Chef claude invocation (`runtime`/`session` pipe path for orchestrator): add + `--mcp-config` pointing at `kaitencode-mcp`. Likely a chef-only spawn flag so + per-task agents are unaffected. +4. Ensure the stream parser surfaces MCP tool-use/tool-result events into the + transcript (the agent transcript already renders tool calls — reuse). + +## Verification + +- Spike: `claude -p --mcp-config … "list tasks in Review"` headless → confirm a + `get_board` tool call + result in the `stream-json` output. +- Prompt size: a 200-task board should produce a *constant* small system prompt. +- Regression: chef can still create/move/rename tasks (writes unaffected). +- No E2BIG at any board size. + +## Open questions + +- Does `claude -p` support MCP headlessly in this version? (gates A) +- Read-staleness after a write: instruct the model to re-`get_board` after each + action (cheap) vs. auto-injecting a fresh snapshot post-write. +- Codex parity (codex chef MCP support is separate/unverified). diff --git a/CLAUDE.md b/CLAUDE.md index 66acd611..57512ded 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -231,9 +231,11 @@ mcp-server/ **App requirement:** Most mutation tools route through the Tauri app's HTTP API (port and bearer token discovered via `~/.kaitencode/api.port`) so triggers fire and `tasks:changed` events flow to the UI. If the app isn't running, those tools error out (production builds disable direct-DB fallback; only `cfg!(test)` allows it). Read-only tools work without the app. -**Known gaps (see `.tickets/_docs/MCP_DOGFOOD_REPORT.md`):** -- `mark_complete`, `add_dependency`, `remove_dependency` bypass the API and write the DB directly — no `tasks:changed` event, so the UI doesn't refresh in real-time. -- No recursion guard / source attribution on MCP-created tasks. See `.tickets/_docs/MCP_SELF_TASK_WORKFLOW.md` for a safe self-task pattern. +**Resolved (was a gap, now fixed):** `mark_complete`, `add_dependency`, `remove_dependency` now route through `/api/mark_complete` + `/api/set_dependencies` (emit `tasks:changed`); source attribution + recursion guard shipped (migration 046 — `created_by_task_id`/`created_by_agent_session_id`, `mcp_max_recursion_depth`). See `.tickets/_docs/MCP_SELF_TASK_WORKFLOW.md` for the self-task pattern. + +**Remaining gaps:** +- **No checklist tools.** Checklist is Tauri-IPC-only (15 commands in `commands/checklist.rs`), with zero `/api/*` routes — so no MCP tool can reach it yet. Adding `checklist_update`/`get_checklist` requires a new `/api/*` route first. +- **UI-only ops without MCP coverage:** `update_column`/`delete_column`/`reorder_columns`, `update_script`/`delete_script`, per-task runtime-mode override, and agent control (`inject_message`/`interrupt`/`pause`/`restart`/`switch_model`) — each needs an `/api/*` route before an MCP tool. ## Type System diff --git a/eslint.config.js b/eslint.config.js index 8b42ae6b..31cbba18 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -13,7 +13,12 @@ export default tseslint.config( ecmaVersion: 2020, globals: globals.browser, parserOptions: { - projectService: true, + // allowDefaultProject lets root config files (vite.config.ts) lint + // without being in tsconfig's include — otherwise the project service + // rejects them ("not found by the project service"). + projectService: { + allowDefaultProject: ['*.config.ts'], + }, tsconfigRootDir: import.meta.dirname, }, }, diff --git a/package.json b/package.json index 344792c9..24b42045 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "type": "module", "scripts": { "dev": "vite", + "profile": "VITE_REACT_SCAN=1 vite", "build": "tsc -b && vite build", "preview": "vite preview", "prepare": "husky", @@ -67,6 +68,7 @@ "jsdom": "^28.1.0", "lint-staged": "^15.5.2", "prettier": "^3.5.3", + "react-scan": "^0.5.7", "tailwindcss": "^4.0.0", "typescript": "^5.7.3", "typescript-eslint": "^8.24.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb2bc981..92e4b68e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,7 +86,7 @@ importers: version: 1.58.2 '@tailwindcss/vite': specifier: ^4.0.0 - version: 4.2.1(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.2.1(vite@6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)) '@tauri-apps/cli': specifier: ^2.4.0 version: 2.10.0 @@ -107,7 +107,7 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.7.0(vite@6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/coverage-v8': specifier: ^4.1.8 version: 4.1.8(vitest@4.1.8) @@ -125,13 +125,13 @@ importers: version: 9.27.0 eslint: specifier: ^9.21.0 - version: 9.39.3(jiti@2.6.1) + version: 9.39.3(jiti@2.7.0) eslint-plugin-react-hooks: specifier: ^5.1.0 - version: 5.2.0(eslint@9.39.3(jiti@2.6.1)) + version: 5.2.0(eslint@9.39.3(jiti@2.7.0)) eslint-plugin-react-refresh: specifier: ^0.4.19 - version: 0.4.26(eslint@9.39.3(jiti@2.6.1)) + version: 0.4.26(eslint@9.39.3(jiti@2.7.0)) globals: specifier: ^16.0.0 version: 16.5.0 @@ -147,6 +147,9 @@ importers: prettier: specifier: ^3.5.3 version: 3.8.1 + react-scan: + specifier: ^0.5.7 + version: 0.5.7(esbuild@0.27.3)(eslint@9.39.3(jiti@2.7.0))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.59.0) tailwindcss: specifier: ^4.0.0 version: 4.2.1 @@ -155,13 +158,13 @@ importers: version: 5.9.3 typescript-eslint: specifier: ^8.24.1 - version: 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + version: 8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) vite: specifier: 6.4.2 - version: 6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3) + version: 6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3) vitest: specifier: ^4.1.8 - version: 4.1.8(@types/node@20.19.39)(@vitest/coverage-v8@4.1.8)(jsdom@28.1.0)(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@20.19.39)(@vitest/coverage-v8@4.1.8)(jsdom@28.1.0)(vite@6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)) webdriverio: specifier: ^9.27.0 version: 9.27.0 @@ -357,6 +360,15 @@ packages: peerDependencies: react: '>=16.8.0' + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -732,6 +744,9 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iarna/toml@2.2.5': + resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + '@inquirer/ansi@1.0.2': resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} @@ -910,9 +925,394 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + '@nodable/entities@2.1.0': resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@opentelemetry/api-logs@0.214.0': + resolution: {integrity: sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.9.1': + resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/core@2.8.0': + resolution: {integrity: sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/instrumentation@0.214.0': + resolution: {integrity: sha512-MHqEX5Dk59cqVah5LiARMACku7jXSVk9iVDWOea4x3cr7VfdByeDCURK6o1lntT1JS/Tsovw01UJrBhN3/uC5w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/resources@2.8.0': + resolution: {integrity: sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.8.0': + resolution: {integrity: sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.41.1': + resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==} + engines: {node: '>=14'} + + '@oxc-parser/binding-android-arm-eabi@0.132.0': + resolution: {integrity: sha512-KrLaPWa5c9Y7LkW+rKkaUE3y7DBDrQtaf7rlsSDfv6KAHUjgzAIRA761Lrrp6//Yd/Rlie/yEOt9YENCoJnOcw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.132.0': + resolution: {integrity: sha512-SThDrSeamB/kG2+NxcJ5/wSLcV6dUqDknrPLqFYQ0ST/55mtBP4M7Q/f3QbubH6aAd11wpzZn/nwbVRSdobOpg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.132.0': + resolution: {integrity: sha512-Lc0f/TYoKBghE5/2Gsv7bLXk+TJZunx2Tf61X8hG4ARXdc8UYI26dCGccFSd1AyFbK3jfaNXtMnupggDbjPXdQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.132.0': + resolution: {integrity: sha512-RG2eJIpf7C21z9HSSXFw1bTArdpKe7Y4fwcJTwRq1yCSe1vSavaN9GA1sm9KqzemTLAGVktQ+7qBTGp0vQeUZg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.132.0': + resolution: {integrity: sha512-wQIPntPLtJ8NcBpvKPbEv3NqzV6k8eP8tP/jE9Rg8HTg/j7urZGFSsTCPCW5k77Qfw2DM4vRvc9p3I4yq/Shvw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.132.0': + resolution: {integrity: sha512-PixKEpeSe3yxQWqNyOCBALRYc72+Tj7ILDofUl3iXo25cVOzLA6jHUhmOINRtWIPh7dbUie3QNeabwaQpZTw6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.132.0': + resolution: {integrity: sha512-sCR+DzGHlyHKnbA2z9zWjTUhIo8Sy0enJl4RDsBwPmkxYynPatpwOAWe8W5127SlW0boqUWHGtr1NWn5UwIhXQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.132.0': + resolution: {integrity: sha512-sQBix5P2cW+IpzTcCwYxnh9yALrKSIkKJThspBvMGcygSMnbzkSvhN7SfuX1hvBk8y1XEChsdkU3ET0V5DmzUw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-arm64-musl@0.132.0': + resolution: {integrity: sha512-WozHg3Kc//8Sk756HXXgMbEAvqtG+Lzb9JOojwQzIGDtN78Az2dLttkb71akWYUF/8IgYfDSlfKh4Uot8is5Vw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-ppc64-gnu@0.132.0': + resolution: {integrity: sha512-CmX/ulNBOEwWTyVRmcpYKAcAizW6+OjtLJgo7fXoL9OqQvjF4VER8tPomv44vwzfSCy1BHbsB0ZlZYzYJNj4cA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-gnu@0.132.0': + resolution: {integrity: sha512-j9oQS+hM90SdhviNGWbPgT4+Rlq+ac++q/zjgwPD1mVHgxHzATvoRGtDx0sXGmFOQ9J9YkwAhYGb5MAHL6TAsA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-musl@0.132.0': + resolution: {integrity: sha512-bLz+Xi+Agnfmd7kWPEsSVwCn2k4EyIalZkNBcQ0OGIv9rqn8VgCPLNd03tM9mKX/5TdlvDXalz0q71BIrOPNqg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-s390x-gnu@0.132.0': + resolution: {integrity: sha512-U6t2qbJU0ypTfyj9QV3W1Y6mITDTL8ai/OR6NUn85vyHthOvobKWgXzU4tu0EskSzlpuVFz1g0jFGulDIUKHxQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxc-parser/binding-linux-x64-gnu@0.132.0': + resolution: {integrity: sha512-WcEaSNHFk8yz5YFlQQAlhq6jOFmZBB/RKE7uzhyCIf+pF1Lmv9gUH4221mle2Gd9iHyWT3ySNph8yZgb1xYdWg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-linux-x64-musl@0.132.0': + resolution: {integrity: sha512-iQrV4iJzQgRwK3BWRmQl1C3C6g3wYpXN2WLdQdyR+efoUnncdShZAVp9OgcojtlD3MDRbuOMGG3SjxF4fL4nlQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-openharmony-arm64@0.132.0': + resolution: {integrity: sha512-FWzmUGrZ6GUby4U7WIwcCtab6tdmlTO3xTRRKyb5kjIJVEiaUAT8animUG/nK8ZCA8gkRkPOTId4rl6uTqUmJQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.132.0': + resolution: {integrity: sha512-TlbMppxJI5CjWDes0QaP6G3aneVg1yikBu5QYI+DUShF9WDL66ccgKFNNGmi/Wybtszw6hxwAvv76T4DaPKnHw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.132.0': + resolution: {integrity: sha512-RH/NbFjGKqdUAUi7Oh3LQPxUk2hsWFEEQ38HSnbRQT8QjBZFKqL1fMbmsB3N4jy/KPh9iX94+9dmkEMBBbambw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.132.0': + resolution: {integrity: sha512-JUr4jQY9jxoIB/YTLXr6XofSi5xikj6p5/Ns1h0VOBDT0j1jKU+kMsv2xxv51RwnETcXpA1Yw/9oUAfcqfaqEA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.132.0': + resolution: {integrity: sha512-2dapgHpA5X8DSXF4AU36hJWYf6zP0tKjMXFRAZFBD62pkevW/uhFDXoFH9Y/3Fd2EtDrw5ByNnR1wVE9X9y0SQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.132.0': + resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} + + '@oxc-resolver/binding-android-arm-eabi@11.20.0': + resolution: {integrity: sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==} + cpu: [arm] + os: [android] + + '@oxc-resolver/binding-android-arm64@11.20.0': + resolution: {integrity: sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==} + cpu: [arm64] + os: [android] + + '@oxc-resolver/binding-darwin-arm64@11.20.0': + resolution: {integrity: sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@11.20.0': + resolution: {integrity: sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@11.20.0': + resolution: {integrity: sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0': + resolution: {integrity: sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm-musleabihf@11.20.0': + resolution: {integrity: sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@11.20.0': + resolution: {integrity: sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-musl@11.20.0': + resolution: {integrity: sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-ppc64-gnu@11.20.0': + resolution: {integrity: sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==} + cpu: [ppc64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-gnu@11.20.0': + resolution: {integrity: sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-musl@11.20.0': + resolution: {integrity: sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-s390x-gnu@11.20.0': + resolution: {integrity: sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==} + cpu: [s390x] + os: [linux] + + '@oxc-resolver/binding-linux-x64-gnu@11.20.0': + resolution: {integrity: sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-linux-x64-musl@11.20.0': + resolution: {integrity: sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-openharmony-arm64@11.20.0': + resolution: {integrity: sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==} + cpu: [arm64] + os: [openharmony] + + '@oxc-resolver/binding-wasm32-wasi@11.20.0': + resolution: {integrity: sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@11.20.0': + resolution: {integrity: sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@11.20.0': + resolution: {integrity: sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.66.0': + resolution: {integrity: sha512-f7kq8N51T4phpzqfBpA2qaVTI/KrkCmNwaj3t/97I/WLTDI+UhlP5GL9eER+zVxBhtlx5rKXWByJU1/zDAvyaw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.66.0': + resolution: {integrity: sha512-xu6QO71tdDS9mjmLZ3AqhtaVHBvdmsOKkYnReNNDgh+XiwnsipeQOIxbiYOOO0iAXycJ+GK0wdMSZP/2j/AmSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.66.0': + resolution: {integrity: sha512-HZ24VimSOC7mxuEA99e0H2FS0C1yO3+iW13jPRAk+e2njsUs3QeAXsafCDyaIrV/MirdOVez+etQNQsJE43zNQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.66.0': + resolution: {integrity: sha512-awhj8ZvJrrRSnXj7V++rpZvTmnl99L6mi0B7gg7Cp7BN6cKpzuI481bHNLvXGA9GB1/oEgA3ponuyoAc6Md12A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.66.0': + resolution: {integrity: sha512-KQF0oVV21/FjIqkRuL8Q1vh8ECsE5+ocdH5tcqTQ4ZnYuDVoYibQUNfqBjQaUsP6UIIda5Y75Wpm5p4RgQWiWw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.66.0': + resolution: {integrity: sha512-9u1rgwZSEXWb30vbFZzQ78HVXBo0WCKNwJ3a2InRUTNMRng+PUDIoSFmA+m4HdUfBaIqftShq8J8qHc+eE/Vig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.66.0': + resolution: {integrity: sha512-Ynot2HR1bHxUaNWoC280MVTDfZuaWuP3XfSMRDhyuZrVjhzoaBCVFlw8h8qeZjWKVUBhPWFIxB7AQTlK8Z2WWg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.66.0': + resolution: {integrity: sha512-xCbgzciGgo+A4aQZEknsNrNiIwY7sU5SfRuMmRjPIvZAgdF34cIHiKvwOsS5XRLjlTVSFwitmq6YclTtHTfU+g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-arm64-musl@1.66.0': + resolution: {integrity: sha512-hmo+ZB/lHkR1HdDmnziNpzSLmulnUSu10VEqX2Yex7OwvoBAbjJQLvy4gIBRV3AAwWnCvAxKp5Nv1GE6LU1QMg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-ppc64-gnu@1.66.0': + resolution: {integrity: sha512-2Invd4Uyy81mVooQC5FBtfxSNrvcX1OxbMlVQ6M2erRrNI2awFYF26YNW2yFxdVFZ4ffNOWKghtMjhnUPsXsVA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxlint/binding-linux-riscv64-gnu@1.66.0': + resolution: {integrity: sha512-s0iXPDQVdgayE3RGa/N2DZF7tjgg0TwEtD1sGoDxqPDGrIXgo45H0yHknT0f9A0yteASsweYZtDyTuVlM4aSag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-riscv64-musl@1.66.0': + resolution: {integrity: sha512-OekL4XFiu7RPK0JIZi8VeHgtIXPREf42t8Cy/rKEsC+P3gcqDgNAAGiyuUOpdbG4wwbfue1q4CHcCO7spSve6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-s390x-gnu@1.66.0': + resolution: {integrity: sha512-Ga1D0kj1SFslm34ThA/BdkUlyAYEnTsXyRC4pF0C5agZSwtGdHYWMTQWemUfBGp4RCG4QWXgdO+HmmmKqOtlBg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxlint/binding-linux-x64-gnu@1.66.0': + resolution: {integrity: sha512-p5jfP1wUZe/IC3qpQO84n9DRnf9g3lKRtLBlQq23ykyrDglHcVx7sWmVTlPuU6SBw8mNnPzyOn022G3XZHnlww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-linux-x64-musl@1.66.0': + resolution: {integrity: sha512-vUB/sYlYZorDL1ZD+o9mRv7zbsykrrFRtmgS6R8musZqLtrPRQn1gc1eGpuX+sfdccz42STl/AqldY6XRb2upQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-openharmony-arm64@1.66.0': + resolution: {integrity: sha512-yde+6p/F59xRkGR9H1HfngWRif1QRJjynZK349l+UI0H6w9hL3G8/AVaTHFyTtLVQ56qtNbX2/5Dc77n1ovnOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.66.0': + resolution: {integrity: sha512-O9GLucgoTdmOrbBX+EjzNe7o/Ze5TFOvXcib6bzUOtBOmj6cV+zw18NgB+cGKAkDw1Pdqs8vGkfHbbsLuDtXWg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.66.0': + resolution: {integrity: sha512-m3Pjwc2MfTcom4E4gOv7DyuGyt7OfGNCbmqDHd+N7EzXmP+ppHuudm2NjcA3AjV5TSeGxaguVF4SbTKHe1USYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.66.0': + resolution: {integrity: sha512-/DbBvw8UFBhja6PqudUjV4UtfsJr0Oa7jUjWVKB0g86lj/VwnPrkngn0sFql3c9RDA0O16dh7ozsXb6GjNAzBQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -922,6 +1322,14 @@ packages: engines: {node: '>=18'} hasBin: true + '@preact/signals-core@1.14.2': + resolution: {integrity: sha512-RZHdBj9ZF4n40Rp4jS052EHHjBWf96P9oNdXPfhQTovCuWY9iQn3Gq+gOTJSgBO9A/JBuPfMOWsSX/lIU9Pc/A==} + + '@preact/signals@2.9.1': + resolution: {integrity: sha512-xVqN8mJjbSN5IB/8Ubmd9NN+Ew6zJswoRxrjZbH3YsgkMshFeO6d8zxEFpHRTq9GJZx7cnPs2CnCpFqtGXGNsw==} + peerDependencies: + preact: '>= 10.25.0 || >=11.0.0-0' + '@promptbook/utils@0.69.5': resolution: {integrity: sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==} @@ -930,9 +1338,22 @@ packages: engines: {node: '>=18'} hasBin: true + '@react-grab/cli@0.1.44': + resolution: {integrity: sha512-gMDYY2rw6OWajCcDlXSIgs2LC432YJXSb3Lm5yM187uhRgBYddoEVULi36h+IolX3r7jSb3ew7vn9FfI8NSo0A==} + hasBin: true + '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.59.0': resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] @@ -1061,6 +1482,51 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@sentry-internal/server-utils@10.57.0': + resolution: {integrity: sha512-Qu8ETmX/ITzteG7Im46b9HOxKKzeaIeqNvftaIlFURu1RUQdHbtGerS7QOmXzwnhuqNGNeiCQYkduB798IfRqA==} + engines: {node: '>=18'} + + '@sentry/core@10.57.0': + resolution: {integrity: sha512-kntItTA2kiT0YpL7encXaF6mkdZMB+y48lwj8w1wkfBpfJAC7sifdgrzLQZqmsqVNE3crg9VfufaAGA+78uFMg==} + engines: {node: '>=18'} + + '@sentry/node-core@10.57.0': + resolution: {integrity: sha512-2v2IF6MfTiu7pimWEq2rYhZsmlwyNbs3bHUsrYFPeP/Rpa6ObDuUWPdVEzJjfyK+AqqYZYxZdV0l3+B13kTEmQ==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 + '@opentelemetry/exporter-trace-otlp-http': '>=0.57.0 <1' + '@opentelemetry/instrumentation': '>=0.57.1 <1' + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/semantic-conventions': ^1.39.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/core': + optional: true + '@opentelemetry/exporter-trace-otlp-http': + optional: true + '@opentelemetry/instrumentation': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + '@opentelemetry/semantic-conventions': + optional: true + + '@sentry/node@10.57.0': + resolution: {integrity: sha512-7KEStrJ97wPf1fA5nU5ONeTTcIIlh7oT8OMffEVA1PXmlhFoXhcQZVzr4rM+zj9tfMWT01og5Ng/Grgh3dN+FA==} + engines: {node: '>=18'} + + '@sentry/opentelemetry@10.57.0': + resolution: {integrity: sha512-iwRz8cEK0GOISG34aJRO8GdYOk3nfpuT6dT2GDQrxw8f7JjkJKx9LPU8MaenOFa4MhY+Z02hI6NNcrbsoI3cXg==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/semantic-conventions': ^1.39.0 + '@shikijs/core@3.23.0': resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} @@ -1288,6 +1754,9 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -1312,6 +1781,9 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -1428,6 +1900,10 @@ packages: resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.61.0': + resolution: {integrity: sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.56.1': resolution: {integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1598,6 +2074,11 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1612,9 +2093,24 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + agent-install@0.0.5: + resolution: {integrity: sha512-nHlms9BkP8ZiY79HrwCGiA2DcNaXrAaJrCM/BEqQ7MEsSKyCk+2A76xPGylIfASZSZE0SaU3T0bNSg4rBPIJAQ==} + hasBin: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -1683,6 +2179,9 @@ packages: async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + atomically@2.1.1: + resolution: {integrity: sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==} + b4a@1.8.0: resolution: {integrity: sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==} peerDependencies: @@ -1761,6 +2260,11 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bippy@0.5.41: + resolution: {integrity: sha512-jCP2pXXLhXqPrAN+iSEFZmLI4uUM4fjSqajh0K+TmM062VehfDT3ZJNkrTGyN701Z5XMejs9qAudSqkMGhSMKg==} + peerDependencies: + react: '>=17.0.1' + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -1856,10 +2360,17 @@ packages: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} + cjs-module-lexer@2.2.0: + resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} + cli-spinners@3.4.0: + resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} + engines: {node: '>=18.20'} + cli-truncate@4.0.0: resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} engines: {node: '>=18'} @@ -1911,6 +2422,13 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + conf@15.1.0: + resolution: {integrity: sha512-Uy5YN9KEu0WWDaZAVJ5FAmZoaJt9rdK6kH+utItPyGsCqCgaTKkrmZx3zoE0/3q6S3bcp3Ihkk+ZqPxWxFK5og==} + engines: {node: '>=20'} + + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1970,6 +2488,10 @@ packages: resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + debounce-fn@6.0.0: + resolution: {integrity: sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ==} + engines: {node: '>=18'} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -2015,6 +2537,9 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + deslop-js@0.0.24: + resolution: {integrity: sha512-ygcRwJXCUedo+hN1L4Ysm7luo4VWOvKEz/kRKWMlFp5bAtTKeXo+8fk/hQaJKsJ/nfahiqkhlYTlrKIR/5nsQg==} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -2049,6 +2574,10 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dot-prop@10.1.0: + resolution: {integrity: sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q==} + engines: {node: '>=20'} + dotenv@17.4.0: resolution: {integrity: sha512-kCKF62fwtzwYm0IGBNjRUjtJgMfGapII+FslMHIjMR5KTnwEmBmWLDRSnc3XSNP8bNy34tekgQyDT0hr7pERRQ==} engines: {node: '>=12'} @@ -2107,6 +2636,10 @@ packages: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} + env-paths@3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} @@ -2154,6 +2687,12 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + eslint-plugin-react-refresh@0.4.26: resolution: {integrity: sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==} peerDependencies: @@ -2163,6 +2702,10 @@ packages: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2209,6 +2752,9 @@ packages: estree-util-is-identifier-name@3.0.0: resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -2275,12 +2821,19 @@ packages: fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-xml-builder@1.1.7: resolution: {integrity: sha512-Yh7/7rQuMXICNr0oMYDR2yHP6oUvmQsTToFeOWj/kIDhAwQ+c4Ol/lbcwOmEM5OHYQmh6S6EQSQ1sljCKP36bQ==} @@ -2288,6 +2841,9 @@ packages: resolution: {integrity: sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==} hasBin: true + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} @@ -2454,6 +3010,12 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + hosted-git-info@7.0.2: resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} engines: {node: ^16.14.0 || >=18.0.0} @@ -2528,6 +3090,10 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + import-in-the-middle@3.0.2: + resolution: {integrity: sha512-LGLYRl0A2gtyUJb2WDliBHmk6TtlHwdDjxonacZ8QrEs/ZW+YDgNv2QAfjRQWpS8HqvNcq6GGnN6jrOa5FysDQ==} + engines: {node: '>=18'} + import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} @@ -2601,6 +3167,10 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -2694,6 +3264,10 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + js-tokens@10.0.0: resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} @@ -2728,6 +3302,12 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -2736,12 +3316,19 @@ packages: engines: {node: '>=6'} hasBin: true + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + lazystream@1.0.1: resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} engines: {node: '>= 0.6.3'} @@ -2876,6 +3463,10 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} + log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} @@ -2972,6 +3563,10 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -3076,6 +3671,10 @@ packages: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} @@ -3103,6 +3702,9 @@ packages: resolution: {integrity: sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==} engines: {node: '>=18.0.0'} + module-details-from-path@1.0.4: + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + motion-dom@12.34.3: resolution: {integrity: sha512-sYgFe+pR9aIM7o4fhs2aXtOI+oqlUd33N9Yoxcgo1Fv7M20sRkHtCmzE/VRNIcq7uNJ+qio+Xubt1FXH3pQ+eQ==} @@ -3197,6 +3799,31 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ora@9.4.0: + resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==} + engines: {node: '>=20'} + + oxc-parser@0.132.0: + resolution: {integrity: sha512-+0LAPHaqtfQlvWdpaAa09SmOaZZgP8C552xosEkGJ4+ruEwP1Vgx+sqBgcBCNfR6KDCmagGOZTde8wmAvcI/Hg==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-resolver@11.20.0: + resolution: {integrity: sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==} + + oxlint-plugin-react-doctor@0.5.2: + resolution: {integrity: sha512-9UTlyNoxVx+6JDFCP1RonbD8ySjoJ2x4YlDjTI2wRGbr0yRx4b+FLdciSxqNwrtkWsGmPEunQGblTRy1KQUmVg==} + engines: {node: ^20.19.0 || >=22.13.0} + + oxlint@1.66.0: + resolution: {integrity: sha512-N4LLxYLd94KEBqXDMDM5f+2PUpItTjDLreXe2Gn5KhjhCK4Qp2YUXaBi8Yu325ryOgKwt22m45fpD7nPOn69Yw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=0.22.1' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -3224,6 +3851,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-manager-detector@1.6.0: + resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -3313,6 +3943,9 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} + preact@10.29.2: + resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -3345,6 +3978,10 @@ packages: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -3365,11 +4002,28 @@ packages: query-selector-shadow-dom@1.0.1: resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-doctor@0.5.2: + resolution: {integrity: sha512-AJpF334K7l2QV98TpxjtkbsRyA75fAAeXildRqSG8PdanxLK+2QB1gZYKSI3NZi/eLZ2V3AbA2PC+rmLmUNRPA==} + engines: {node: ^20.19.0 || >=22.13.0} + hasBin: true + react-dom@19.2.4: resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: react: ^19.2.4 + react-grab@0.1.44: + resolution: {integrity: sha512-bDEwBdI90ljq2lhUtPqmWis/HwYB/CvfT0m5i+P9F83Pt0Ot8o9XL8v00s9jcWzdQUlsFDzmq2FO2CHUe8JY8A==} + hasBin: true + peerDependencies: + react: '>=17.0.0' + peerDependenciesMeta: + react: + optional: true + react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -3386,6 +4040,17 @@ packages: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} + react-scan@0.5.7: + resolution: {integrity: sha512-KRlq734yN6q/f2CZmZi9CWHuiqSzoLhPFLtcJOL6XM4lR54myyFcY81pG9QOwj+eBC1hIHm5n+Ntbtqiilu8Rg==} + hasBin: true + peerDependencies: + esbuild: '>=0.18.0' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + esbuild: + optional: true + react@19.2.4: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} @@ -3453,6 +4118,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-in-the-middle@8.0.1: + resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} + engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3471,6 +4140,10 @@ packages: resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} engines: {node: '>=10'} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} @@ -3486,6 +4159,9 @@ packages: resolution: {integrity: sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==} engines: {node: '>=0.12.0'} + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} @@ -3556,6 +4232,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -3620,6 +4299,10 @@ packages: std-env@4.1.0: resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + stdin-discarder@0.3.2: + resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} + engines: {node: '>=18'} + stream-buffers@3.0.3: resolution: {integrity: sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==} engines: {node: '>= 0.10.0'} @@ -3643,6 +4326,10 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string-width@8.2.1: + resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} + engines: {node: '>=20'} + string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -3679,6 +4366,12 @@ packages: strnum@2.3.0: resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==} + stubborn-fs@2.0.0: + resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} + + stubborn-utils@1.0.2: + resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==} + style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -3696,6 +4389,10 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + tailwindcss@4.2.1: resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} @@ -3793,6 +4490,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-fest@5.7.0: + resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} + engines: {node: '>=20'} + typescript-eslint@8.56.1: resolution: {integrity: sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3805,6 +4506,15 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -3834,6 +4544,10 @@ packages: unist-util-visit@5.1.0: resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + unplugin@3.0.0: + resolution: {integrity: sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==} + engines: {node: ^20.19.0 || >=22.12.0} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -3943,6 +4657,26 @@ packages: jsdom: optional: true + vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + + vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + + vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -3972,6 +4706,9 @@ packages: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -3989,6 +4726,9 @@ packages: resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + when-exit@2.1.5: + resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -4108,6 +4848,15 @@ packages: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zustand@5.0.11: resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==} engines: {node: '>=12.20.0'} @@ -4341,6 +5090,22 @@ snapshots: react: 19.2.4 tslib: 2.8.1 + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.25.12': optional: true @@ -4497,9 +5262,9 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.3(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.3(jiti@2.7.0))': dependencies: - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.3(jiti@2.7.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -4556,6 +5321,8 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@iarna/toml@2.2.5': {} + '@inquirer/ansi@1.0.2': {} '@inquirer/checkbox@4.3.2(@types/node@20.19.39)': @@ -4677,66 +5444,304 @@ snapshots: optionalDependencies: '@types/node': 20.19.39 - '@inquirer/type@3.0.10(@types/node@20.19.39)': - optionalDependencies: - '@types/node': 20.19.39 + '@inquirer/type@3.0.10(@types/node@20.19.39)': + optionalDependencies: + '@types/node': 20.19.39 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jest/diff-sequences@30.3.0': {} + + '@jest/expect-utils@30.3.0': + dependencies: + '@jest/get-type': 30.1.0 + + '@jest/get-type@30.1.0': {} + + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 20.19.39 + jest-regex-util: 30.0.1 + + '@jest/schemas@30.0.5': + dependencies: + '@sinclair/typebox': 0.34.49 + + '@jest/types@30.3.0': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 20.19.39 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@nodable/entities@2.1.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@opentelemetry/api-logs@0.214.0': + dependencies: + '@opentelemetry/api': 1.9.1 + + '@opentelemetry/api@1.9.1': {} + + '@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.214.0 + import-in-the-middle: 3.0.2 + require-in-the-middle: 8.0.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/semantic-conventions@1.41.1': {} + + '@oxc-parser/binding-android-arm-eabi@0.132.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.132.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.132.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.132.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.132.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.132.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.132.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.132.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.132.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.132.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.132.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.132.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.132.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.132.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.132.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.132.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.132.0': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.132.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.132.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.132.0': + optional: true + + '@oxc-project/types@0.132.0': {} + + '@oxc-resolver/binding-android-arm-eabi@11.20.0': + optional: true + + '@oxc-resolver/binding-android-arm64@11.20.0': + optional: true + + '@oxc-resolver/binding-darwin-arm64@11.20.0': + optional: true + + '@oxc-resolver/binding-darwin-x64@11.20.0': + optional: true + + '@oxc-resolver/binding-freebsd-x64@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-arm-musleabihf@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-ppc64-gnu@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-riscv64-musl@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@11.20.0': + optional: true + + '@oxc-resolver/binding-openharmony-arm64@11.20.0': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@11.20.0': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@11.20.0': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@11.20.0': + optional: true + + '@oxlint/binding-android-arm-eabi@1.66.0': + optional: true + + '@oxlint/binding-android-arm64@1.66.0': + optional: true - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.2.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 + '@oxlint/binding-darwin-arm64@1.66.0': + optional: true - '@jest/diff-sequences@30.3.0': {} + '@oxlint/binding-darwin-x64@1.66.0': + optional: true - '@jest/expect-utils@30.3.0': - dependencies: - '@jest/get-type': 30.1.0 + '@oxlint/binding-freebsd-x64@1.66.0': + optional: true - '@jest/get-type@30.1.0': {} + '@oxlint/binding-linux-arm-gnueabihf@1.66.0': + optional: true - '@jest/pattern@30.0.1': - dependencies: - '@types/node': 20.19.39 - jest-regex-util: 30.0.1 + '@oxlint/binding-linux-arm-musleabihf@1.66.0': + optional: true - '@jest/schemas@30.0.5': - dependencies: - '@sinclair/typebox': 0.34.49 + '@oxlint/binding-linux-arm64-gnu@1.66.0': + optional: true - '@jest/types@30.3.0': - dependencies: - '@jest/pattern': 30.0.1 - '@jest/schemas': 30.0.5 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 20.19.39 - '@types/yargs': 17.0.35 - chalk: 4.1.2 + '@oxlint/binding-linux-arm64-musl@1.66.0': + optional: true - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 + '@oxlint/binding-linux-ppc64-gnu@1.66.0': + optional: true - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 + '@oxlint/binding-linux-riscv64-gnu@1.66.0': + optional: true - '@jridgewell/resolve-uri@3.1.2': {} + '@oxlint/binding-linux-riscv64-musl@1.66.0': + optional: true - '@jridgewell/sourcemap-codec@1.5.5': {} + '@oxlint/binding-linux-s390x-gnu@1.66.0': + optional: true - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + '@oxlint/binding-linux-x64-gnu@1.66.0': + optional: true - '@nodable/entities@2.1.0': {} + '@oxlint/binding-linux-x64-musl@1.66.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.66.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.66.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.66.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.66.0': + optional: true '@pkgjs/parseargs@0.11.0': optional: true @@ -4745,6 +5750,13 @@ snapshots: dependencies: playwright: 1.58.2 + '@preact/signals-core@1.14.2': {} + + '@preact/signals@2.9.1(preact@10.29.2)': + dependencies: + '@preact/signals-core': 1.14.2 + preact: 10.29.2 + '@promptbook/utils@0.69.5': dependencies: spacetrim: 0.11.59 @@ -4764,8 +5776,27 @@ snapshots: - react-native-b4a - supports-color + '@react-grab/cli@0.1.44': + dependencies: + agent-install: 0.0.5 + commander: 14.0.3 + ignore: 7.0.5 + ora: 9.4.0 + package-manager-detector: 1.6.0 + picocolors: 1.1.1 + prompts: 2.4.2 + tinyexec: 1.2.4 + '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rollup/pluginutils@5.4.0(rollup@4.59.0)': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.59.0 + '@rollup/rollup-android-arm-eabi@4.59.0': optional: true @@ -4843,6 +5874,48 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} + '@sentry-internal/server-utils@10.57.0': + dependencies: + '@sentry/core': 10.57.0 + + '@sentry/core@10.57.0': {} + + '@sentry/node-core@10.57.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': + dependencies: + '@sentry/core': 10.57.0 + '@sentry/opentelemetry': 10.57.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + import-in-the-middle: 3.0.2 + optionalDependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@sentry/node@10.57.0': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + '@sentry-internal/server-utils': 10.57.0 + '@sentry/core': 10.57.0 + '@sentry/node-core': 10.57.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + '@sentry/opentelemetry': 10.57.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + import-in-the-middle: 3.0.2 + transitivePeerDependencies: + - '@opentelemetry/exporter-trace-otlp-http' + - supports-color + + '@sentry/opentelemetry@10.57.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + '@sentry/core': 10.57.0 + '@shikijs/core@3.23.0': dependencies: '@shikijs/types': 3.23.0 @@ -4943,12 +6016,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 - '@tailwindcss/vite@4.2.1(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3))': + '@tailwindcss/vite@4.2.1(vite@6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@tailwindcss/node': 4.2.1 '@tailwindcss/oxide': 4.2.1 tailwindcss: 4.2.1 - vite: 6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3) '@tauri-apps/api@2.10.1': {} @@ -5039,6 +6112,11 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5': @@ -5073,6 +6151,8 @@ snapshots: '@types/deep-eql@4.0.2': {} + '@types/esrecurse@4.3.1': {} + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.8 @@ -5144,15 +6224,15 @@ snapshots: '@types/node': 20.19.39 optional: true - '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.56.1 - '@typescript-eslint/type-utils': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.56.1 - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.3(jiti@2.7.0) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -5160,14 +6240,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.56.1 '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.56.1 debug: 4.4.3(supports-color@8.1.1) - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.3(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -5190,13 +6270,13 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) debug: 4.4.3(supports-color@8.1.1) - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.3(jiti@2.7.0) ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -5204,6 +6284,8 @@ snapshots: '@typescript-eslint/types@8.56.1': {} + '@typescript-eslint/types@8.61.0': {} + '@typescript-eslint/typescript-estree@8.56.1(typescript@5.9.3)': dependencies: '@typescript-eslint/project-service': 8.56.1(typescript@5.9.3) @@ -5219,13 +6301,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.7.0)) '@typescript-eslint/scope-manager': 8.56.1 '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.3(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -5237,7 +6319,7 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -5245,7 +6327,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - supports-color @@ -5261,7 +6343,7 @@ snapshots: obug: 2.1.2 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@20.19.39)(@vitest/coverage-v8@4.1.8)(jsdom@28.1.0)(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@20.19.39)(@vitest/coverage-v8@4.1.8)(jsdom@28.1.0)(vite@6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/expect@4.1.8': dependencies: @@ -5272,13 +6354,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.8(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.8(vite@6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3) '@vitest/pretty-format@2.1.9': dependencies: @@ -5520,6 +6602,10 @@ snapshots: dependencies: event-target-shim: 5.0.1 + acorn-import-attributes@1.9.5(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -5528,6 +6614,19 @@ snapshots: agent-base@7.1.4: {} + agent-install@0.0.5: + dependencies: + '@iarna/toml': 2.2.5 + commander: 14.0.3 + jsonc-parser: 3.3.1 + picocolors: 1.1.1 + prompts: 2.4.2 + yaml: 2.8.3 + + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 @@ -5535,6 +6634,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-colors@4.1.3: {} ansi-escapes@7.3.0: @@ -5606,6 +6712,11 @@ snapshots: async@3.2.6: {} + atomically@2.1.1: + dependencies: + stubborn-fs: 2.0.0 + when-exit: 2.1.5 + b4a@1.8.0: {} bail@2.0.2: {} @@ -5658,6 +6769,10 @@ snapshots: binary-extensions@2.3.0: {} + bippy@0.5.41(react@19.2.4): + dependencies: + react: 19.2.4 + boolbase@1.0.0: {} brace-expansion@1.1.14: @@ -5764,10 +6879,14 @@ snapshots: ci-info@4.4.0: {} + cjs-module-lexer@2.2.0: {} + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 + cli-spinners@3.4.0: {} + cli-truncate@4.0.0: dependencies: slice-ansi: 5.0.0 @@ -5816,6 +6935,20 @@ snapshots: concat-map@0.0.1: {} + conf@15.1.0: + dependencies: + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + atomically: 2.1.1 + debounce-fn: 6.0.0 + dot-prop: 10.1.0 + env-paths: 3.0.0 + json-schema-typed: 8.0.2 + semver: 7.8.2 + uint8array-extras: 1.5.0 + + confbox@0.2.4: {} + convert-source-map@2.0.0: {} core-util-is@1.0.3: {} @@ -5890,6 +7023,10 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' + debounce-fn@6.0.0: + dependencies: + mimic-function: 5.0.1 + debug@4.4.3(supports-color@8.1.1): dependencies: ms: 2.1.3 @@ -5925,6 +7062,15 @@ snapshots: dequal@2.0.3: {} + deslop-js@0.0.24: + dependencies: + '@oxc-project/types': 0.132.0 + fast-glob: 3.3.3 + minimatch: 10.2.5 + oxc-parser: 0.132.0 + oxc-resolver: 11.20.0 + typescript: 6.0.3 + detect-libc@2.1.2: {} devlop@1.1.0: @@ -5957,6 +7103,10 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + dot-prop@10.1.0: + dependencies: + type-fest: 5.7.0 + dotenv@17.4.0: {} eastasianwidth@0.2.0: {} @@ -6017,6 +7167,8 @@ snapshots: entities@7.0.1: {} + env-paths@3.0.0: {} + environment@1.1.0: {} error-ex@1.3.4: @@ -6099,28 +7251,46 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-plugin-react-hooks@5.2.0(eslint@9.39.3(jiti@2.6.1)): + eslint-plugin-react-hooks@5.2.0(eslint@9.39.3(jiti@2.7.0)): + dependencies: + eslint: 9.39.3(jiti@2.7.0) + + eslint-plugin-react-hooks@7.1.1(eslint@9.39.3(jiti@2.7.0)): dependencies: - eslint: 9.39.3(jiti@2.6.1) + '@babel/core': 7.29.0 + '@babel/parser': 7.29.7 + eslint: 9.39.3(jiti@2.7.0) + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color - eslint-plugin-react-refresh@0.4.26(eslint@9.39.3(jiti@2.6.1)): + eslint-plugin-react-refresh@0.4.26(eslint@9.39.3(jiti@2.7.0)): dependencies: - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.3(jiti@2.7.0) eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-visitor-keys@3.4.3: {} eslint-visitor-keys@4.2.1: {} eslint-visitor-keys@5.0.1: {} - eslint@9.39.3(jiti@2.6.1): + eslint@9.39.3(jiti@2.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 @@ -6155,7 +7325,7 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 2.6.1 + jiti: 2.7.0 transitivePeerDependencies: - supports-color @@ -6179,6 +7349,8 @@ snapshots: estree-util-is-identifier-name@3.0.0: {} + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 @@ -6265,10 +7437,20 @@ snapshots: fast-fifo@1.3.2: {} + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} + fast-uri@3.1.2: {} + fast-xml-builder@1.1.7: dependencies: path-expression-matcher: 1.5.0 @@ -6281,6 +7463,10 @@ snapshots: strnum: 2.3.0 xml-naming: 0.1.0 + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + fd-slicer@1.1.0: dependencies: pend: 1.2.0 @@ -6463,6 +7649,12 @@ snapshots: he@1.2.0: {} + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + hosted-git-info@7.0.2: dependencies: lru-cache: 10.4.3 @@ -6533,6 +7725,13 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-in-the-middle@3.0.2: + dependencies: + acorn: 8.16.0 + acorn-import-attributes: 1.9.5(acorn@8.16.0) + cjs-module-lexer: 2.2.0 + module-details-from-path: 1.0.4 + import-meta-resolve@4.2.0: {} imurmurhash@0.1.4: {} @@ -6593,6 +7792,8 @@ snapshots: is-hexadecimal@2.0.1: {} + is-interactive@2.0.0: {} + is-number@7.0.0: {} is-plain-obj@2.1.0: {} @@ -6687,6 +7888,8 @@ snapshots: jiti@2.6.1: {} + jiti@2.7.0: {} + js-tokens@10.0.0: {} js-tokens@4.0.0: {} @@ -6730,10 +7933,16 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + + json-schema-typed@8.0.2: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@2.2.3: {} + jsonc-parser@3.3.1: {} + jszip@3.10.1: dependencies: lie: 3.3.0 @@ -6745,6 +7954,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kleur@3.0.3: {} + lazystream@1.0.1: dependencies: readable-stream: 2.3.8 @@ -6868,6 +8079,11 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + log-update@6.1.0: dependencies: ansi-escapes: 7.3.0 @@ -7067,6 +8283,8 @@ snapshots: merge-stream@2.0.0: {} + merge2@1.4.1: {} + micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.3.0 @@ -7273,6 +8491,10 @@ snapshots: dependencies: brace-expansion: 5.0.6 + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + minimatch@3.1.5: dependencies: brace-expansion: 1.1.14 @@ -7314,6 +8536,8 @@ snapshots: modern-tar@0.7.6: {} + module-details-from-path@1.0.4: {} + motion-dom@12.34.3: dependencies: motion-utils: 12.29.2 @@ -7400,6 +8624,93 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ora@9.4.0: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 3.4.0 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 7.0.1 + stdin-discarder: 0.3.2 + string-width: 8.2.1 + + oxc-parser@0.132.0: + dependencies: + '@oxc-project/types': 0.132.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.132.0 + '@oxc-parser/binding-android-arm64': 0.132.0 + '@oxc-parser/binding-darwin-arm64': 0.132.0 + '@oxc-parser/binding-darwin-x64': 0.132.0 + '@oxc-parser/binding-freebsd-x64': 0.132.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.132.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.132.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.132.0 + '@oxc-parser/binding-linux-arm64-musl': 0.132.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.132.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.132.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.132.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.132.0 + '@oxc-parser/binding-linux-x64-gnu': 0.132.0 + '@oxc-parser/binding-linux-x64-musl': 0.132.0 + '@oxc-parser/binding-openharmony-arm64': 0.132.0 + '@oxc-parser/binding-wasm32-wasi': 0.132.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.132.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.132.0 + '@oxc-parser/binding-win32-x64-msvc': 0.132.0 + + oxc-resolver@11.20.0: + optionalDependencies: + '@oxc-resolver/binding-android-arm-eabi': 11.20.0 + '@oxc-resolver/binding-android-arm64': 11.20.0 + '@oxc-resolver/binding-darwin-arm64': 11.20.0 + '@oxc-resolver/binding-darwin-x64': 11.20.0 + '@oxc-resolver/binding-freebsd-x64': 11.20.0 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.20.0 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.20.0 + '@oxc-resolver/binding-linux-arm64-gnu': 11.20.0 + '@oxc-resolver/binding-linux-arm64-musl': 11.20.0 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.20.0 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.20.0 + '@oxc-resolver/binding-linux-riscv64-musl': 11.20.0 + '@oxc-resolver/binding-linux-s390x-gnu': 11.20.0 + '@oxc-resolver/binding-linux-x64-gnu': 11.20.0 + '@oxc-resolver/binding-linux-x64-musl': 11.20.0 + '@oxc-resolver/binding-openharmony-arm64': 11.20.0 + '@oxc-resolver/binding-wasm32-wasi': 11.20.0 + '@oxc-resolver/binding-win32-arm64-msvc': 11.20.0 + '@oxc-resolver/binding-win32-x64-msvc': 11.20.0 + + oxlint-plugin-react-doctor@0.5.2: + dependencies: + '@typescript-eslint/types': 8.61.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + oxc-parser: 0.132.0 + + oxlint@1.66.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.66.0 + '@oxlint/binding-android-arm64': 1.66.0 + '@oxlint/binding-darwin-arm64': 1.66.0 + '@oxlint/binding-darwin-x64': 1.66.0 + '@oxlint/binding-freebsd-x64': 1.66.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.66.0 + '@oxlint/binding-linux-arm-musleabihf': 1.66.0 + '@oxlint/binding-linux-arm64-gnu': 1.66.0 + '@oxlint/binding-linux-arm64-musl': 1.66.0 + '@oxlint/binding-linux-ppc64-gnu': 1.66.0 + '@oxlint/binding-linux-riscv64-gnu': 1.66.0 + '@oxlint/binding-linux-riscv64-musl': 1.66.0 + '@oxlint/binding-linux-s390x-gnu': 1.66.0 + '@oxlint/binding-linux-x64-gnu': 1.66.0 + '@oxlint/binding-linux-x64-musl': 1.66.0 + '@oxlint/binding-openharmony-arm64': 1.66.0 + '@oxlint/binding-win32-arm64-msvc': 1.66.0 + '@oxlint/binding-win32-ia32-msvc': 1.66.0 + '@oxlint/binding-win32-x64-msvc': 1.66.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -7436,6 +8747,8 @@ snapshots: package-json-from-dist@1.0.1: {} + package-manager-detector@1.6.0: {} + pako@1.0.11: {} parent-module@1.0.1: @@ -7520,6 +8833,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + preact@10.29.2: {} + prelude-ls@1.2.1: {} prettier@3.8.1: {} @@ -7546,6 +8861,11 @@ snapshots: progress@2.0.3: {} + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + property-information@7.1.0: {} proxy-agent@6.5.0: @@ -7572,11 +8892,44 @@ snapshots: query-selector-shadow-dom@1.0.1: {} + queue-microtask@1.2.3: {} + + react-doctor@0.5.2(eslint@9.39.3(jiti@2.7.0)): + dependencies: + '@babel/code-frame': 7.29.7 + '@sentry/node': 10.57.0 + agent-install: 0.0.5 + conf: 15.1.0 + confbox: 0.2.4 + deslop-js: 0.0.24 + eslint-plugin-react-hooks: 7.1.1(eslint@9.39.3(jiti@2.7.0)) + jiti: 2.7.0 + magicast: 0.5.3 + oxlint: 1.66.0 + oxlint-plugin-react-doctor: 0.5.2 + prompts: 2.4.2 + typescript: 5.9.3 + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + transitivePeerDependencies: + - '@opentelemetry/exporter-trace-otlp-http' + - eslint + - oxlint-tsgolint + - supports-color + react-dom@19.2.4(react@19.2.4): dependencies: react: 19.2.4 scheduler: 0.27.0 + react-grab@0.1.44(react@19.2.4): + dependencies: + '@react-grab/cli': 0.1.44 + bippy: 0.5.41(react@19.2.4) + optionalDependencies: + react: 19.2.4 + react-is@17.0.2: {} react-is@18.3.1: {} @@ -7601,6 +8954,31 @@ snapshots: react-refresh@0.17.0: {} + react-scan@0.5.7(esbuild@0.27.3)(eslint@9.39.3(jiti@2.7.0))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.59.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/types': 7.29.7 + '@preact/signals': 2.9.1(preact@10.29.2) + '@rollup/pluginutils': 5.4.0(rollup@4.59.0) + bippy: 0.5.41(react@19.2.4) + commander: 14.0.3 + picocolors: 1.1.1 + preact: 10.29.2 + prompts: 2.4.2 + react: 19.2.4 + react-doctor: 0.5.2(eslint@9.39.3(jiti@2.7.0)) + react-dom: 19.2.4(react@19.2.4) + react-grab: 0.1.44(react@19.2.4) + optionalDependencies: + esbuild: 0.27.3 + unplugin: 3.0.0 + transitivePeerDependencies: + - '@opentelemetry/exporter-trace-otlp-http' + - eslint + - oxlint-tsgolint + - rollup + - supports-color + react@19.2.4: {} read-pkg-up@10.1.0: @@ -7701,6 +9079,13 @@ snapshots: require-from-string@2.0.2: {} + require-in-the-middle@8.0.1: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + module-details-from-path: 1.0.4 + transitivePeerDependencies: + - supports-color + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -7716,6 +9101,8 @@ snapshots: ret@0.5.0: {} + reusify@1.1.0: {} + rfdc@1.4.1: {} rgb2hex@0.2.5: {} @@ -7753,6 +9140,10 @@ snapshots: run-async@4.0.6: {} + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -7810,6 +9201,8 @@ snapshots: signal-exit@4.1.0: {} + sisteransi@1.0.5: {} + slash@3.0.0: {} slice-ansi@5.0.0: @@ -7870,6 +9263,8 @@ snapshots: std-env@4.1.0: {} + stdin-discarder@0.3.2: {} + stream-buffers@3.0.3: {} streamx@2.25.0: @@ -7901,6 +9296,11 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 + string-width@8.2.1: + dependencies: + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 @@ -7934,6 +9334,12 @@ snapshots: strnum@2.3.0: {} + stubborn-fs@2.0.0: + dependencies: + stubborn-utils: 1.0.2 + + stubborn-utils@1.0.2: {} + style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -7952,6 +9358,8 @@ snapshots: symbol-tree@3.2.4: {} + tagged-tag@1.0.0: {} + tailwindcss@4.2.1: {} tapable@2.3.0: {} @@ -8055,19 +9463,27 @@ snapshots: type-fest@4.41.0: {} - typescript-eslint@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3): + type-fest@5.7.0: dependencies: - '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + tagged-tag: 1.0.0 + + typescript-eslint@8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.3(jiti@2.6.1) + '@typescript-eslint/utils': 8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.3(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color typescript@5.9.3: {} + typescript@6.0.3: {} + + uint8array-extras@1.5.0: {} + undici-types@6.21.0: {} undici@7.24.0: {} @@ -8107,6 +9523,13 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 + unplugin@3.0.0: + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.4 + webpack-virtual-modules: 0.6.2 + optional: true + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -8138,7 +9561,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3): + vite@6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) @@ -8149,15 +9572,15 @@ snapshots: optionalDependencies: '@types/node': 20.19.39 fsevents: 2.3.3 - jiti: 2.6.1 + jiti: 2.7.0 lightningcss: 1.31.1 tsx: 4.21.0 yaml: 2.8.3 - vitest@4.1.8(@types/node@20.19.39)(@vitest/coverage-v8@4.1.8)(jsdom@28.1.0)(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@20.19.39)(@vitest/coverage-v8@4.1.8)(jsdom@28.1.0)(vite@6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 4.1.8(vite@6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -8174,15 +9597,33 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 6.4.2(@types/node@20.19.39)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.4.2(@types/node@20.19.39)(jiti@2.7.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: + '@opentelemetry/api': 1.9.1 '@types/node': 20.19.39 '@vitest/coverage-v8': 4.1.8(vitest@4.1.8) jsdom: 28.1.0 transitivePeerDependencies: - msw + vscode-jsonrpc@8.2.0: {} + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-uri@3.1.0: {} + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -8258,6 +9699,9 @@ snapshots: webidl-conversions@8.0.1: {} + webpack-virtual-modules@0.6.2: + optional: true + whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 @@ -8274,6 +9718,8 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' + when-exit@2.1.5: {} + which@2.0.2: dependencies: isexe: 2.0.0 @@ -8381,6 +9827,12 @@ snapshots: compress-commons: 6.0.2 readable-stream: 4.7.0 + zod-validation-error@4.0.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + + zod@4.4.3: {} + zustand@5.0.11(@types/react@19.2.14)(react@19.2.4): optionalDependencies: '@types/react': 19.2.14 diff --git a/src-tauri/src/chat/chef.rs b/src-tauri/src/chat/chef.rs index 625d6d0e..c7401464 100644 --- a/src-tauri/src/chat/chef.rs +++ b/src-tauri/src/chat/chef.rs @@ -105,6 +105,12 @@ impl ChefSession { } } + /// Tool-based CLI system prompt: no embedded board (the model reads it via the + /// `get_board` MCP tool). Only valid for `ChefMode::Cli`. + pub fn build_system_prompt_tools(&self, workspace: &Workspace, columns: &[Column]) -> String { + context::build_cli_system_prompt_tools(workspace, columns) + } + /// Build the board context JSON for injection into messages. pub fn build_board_context( &self, @@ -169,8 +175,16 @@ impl ChefSession { let system_prompt = self.build_system_prompt(&workspace, &columns, &tasks); self.session.set_system_prompt(system_prompt); - // Augment message with board context - let augmented = self.augment_message(message, &workspace, &columns, &tasks); + // The board lives in the system prompt (rebuilt fresh every turn). Only on + // a `--resume` turn does Claude reuse the ORIGINAL session's system prompt + // (stale board) — so only then do we also prepend the current board to the + // message. On a fresh turn the system prompt already carries it; prepending + // would just double the payload (and grow it toward the argv/E2BIG limit). + let augmented = if self.session.resume_id().is_some() { + self.augment_message(message, &workspace, &columns, &tasks) + } else { + message.to_string() + }; // Delegate to base session self.session.send_message(&augmented, on_event).await @@ -220,6 +234,7 @@ mod tests { system_prompt: String::new(), working_dir: None, effort_level: None, + ..Default::default() } } diff --git a/src-tauri/src/chat/debug.rs b/src-tauri/src/chat/debug.rs new file mode 100644 index 00000000..77ea69a2 --- /dev/null +++ b/src-tauri/src/chat/debug.rs @@ -0,0 +1,92 @@ +//! In-memory recorder for CLI chat turns — a debugging aid that captures exactly +//! what we send to the CLI (command, args incl. system prompt, the stdin prompt) +//! and what comes back (raw stream-json lines). Bounded ring buffer; surfaced in +//! the Settings → Debug tab. Covers the pipe transport (chef + managed agents). + +use std::collections::VecDeque; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Mutex, OnceLock}; + +/// Keep the last N turns. A turn = one CLI spawn + its output. +const MAX_TURNS: usize = 20; +/// Cap output lines per turn so a chatty run can't grow unbounded. +const MAX_LINES_PER_TURN: usize = 1000; + +#[derive(Clone, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DebugTurn { + pub id: u64, + /// Millis since the Unix epoch (stamped at spawn). + pub started_at_ms: u64, + pub command: String, + pub args: Vec, + /// The prompt delivered on stdin (claude `-p`), if any. + pub stdin: Option, + /// Raw stdout lines (the stream-json the CLI emits). + pub output: Vec, + /// Set when output exceeded `MAX_LINES_PER_TURN`. + pub output_truncated: bool, +} + +static LOG: OnceLock>> = OnceLock::new(); +static NEXT_ID: AtomicU64 = AtomicU64::new(1); + +fn log() -> &'static Mutex> { + LOG.get_or_init(|| Mutex::new(VecDeque::new())) +} + +/// Record a spawn; returns the turn id to thread into `record_line`. +pub fn record_spawn(command: &str, args: &[String], stdin: Option<&str>) -> u64 { + let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); + let started_at_ms = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_millis() as u64) + .unwrap_or(0); + let turn = DebugTurn { + id, + started_at_ms, + command: command.to_string(), + args: args.to_vec(), + stdin: stdin.map(str::to_string), + output: Vec::new(), + output_truncated: false, + }; + if let Ok(mut log) = log().lock() { + log.push_back(turn); + while log.len() > MAX_TURNS { + log.pop_front(); + } + } + id +} + +/// Append a raw stdout line to a turn. +pub fn record_line(turn_id: u64, line: &str) { + let line = line.trim_end_matches(['\n', '\r']); + if line.is_empty() { + return; + } + if let Ok(mut log) = log().lock() { + if let Some(turn) = log.iter_mut().find(|t| t.id == turn_id) { + if turn.output.len() < MAX_LINES_PER_TURN { + turn.output.push(line.to_string()); + } else { + turn.output_truncated = true; + } + } + } +} + +/// Newest-first snapshot for the UI. +pub fn snapshot() -> Vec { + log() + .lock() + .map(|log| log.iter().rev().cloned().collect()) + .unwrap_or_default() +} + +pub fn clear() { + if let Ok(mut log) = log().lock() { + log.clear(); + } +} diff --git a/src-tauri/src/chat/mod.rs b/src-tauri/src/chat/mod.rs index 17a0fa22..c7ce6854 100644 --- a/src-tauri/src/chat/mod.rs +++ b/src-tauri/src/chat/mod.rs @@ -14,6 +14,7 @@ pub mod bridge; pub mod chef; +pub mod debug; pub mod events; pub mod gc; pub mod log_retention; diff --git a/src-tauri/src/chat/pipe_transport.rs b/src-tauri/src/chat/pipe_transport.rs index 663dcb31..68535a6d 100644 --- a/src-tauri/src/chat/pipe_transport.rs +++ b/src-tauri/src/chat/pipe_transport.rs @@ -61,8 +61,14 @@ impl ChatTransport for PipeTransport { } } - // No stdin needed - message is in args - cmd.stdin(std::process::Stdio::null()); + // The prompt is delivered on stdin (when present) rather than argv, so a + // large prompt can't exceed Linux's 128 KiB per-arg limit (E2BIG). + let stdin_data = config.stdin_data.clone(); + if stdin_data.is_some() { + cmd.stdin(std::process::Stdio::piped()); + } else { + cmd.stdin(std::process::Stdio::null()); + } cmd.stdout(std::process::Stdio::piped()); cmd.stderr(std::process::Stdio::piped()); @@ -70,6 +76,19 @@ impl ChatTransport for PipeTransport { .spawn() .map_err(|e| format!("Failed to spawn CLI: {}", e))?; + // Stream the prompt to stdin and close it so the CLI sees EOF and starts. + // Done on a task (not inline) so a prompt larger than the pipe buffer + // (~64 KiB) can't deadlock against a child that blocks before draining. + if let Some(prompt) = stdin_data { + if let Some(mut stdin) = child.stdin.take() { + tokio::spawn(async move { + use tokio::io::AsyncWriteExt as _; + let _ = stdin.write_all(prompt.as_bytes()).await; + let _ = stdin.shutdown().await; + }); + } + } + // Spawn stderr reader if let Some(stderr) = child.stderr.take() { spawn_stderr_reader(stderr, config.command.clone()); @@ -85,6 +104,11 @@ impl ChatTransport for PipeTransport { self.alive.store(true, Ordering::SeqCst); self.child = Some(child); + // Debug recorder: capture the exact outgoing invocation (command + args, + // which include --system-prompt + the MCP flags) and the stdin prompt. + let debug_id = + super::debug::record_spawn(&config.command, &config.args, config.stdin_data.as_deref()); + let (event_tx, event_rx) = mpsc::channel::(256); let alive_flag = Arc::clone(&self.alive); @@ -111,6 +135,9 @@ impl ChatTransport for PipeTransport { break; } Ok(Ok(_)) => { + // Capture the raw line for the debug inspector before parsing. + super::debug::record_line(debug_id, &line); + // Skip assistant events to avoid double-counting with streaming deltas let is_assistant_event = line.contains("\"type\":\"assistant\"") || line.contains("\"type\": \"assistant\""); diff --git a/src-tauri/src/chat/registry.rs b/src-tauri/src/chat/registry.rs index 4759dcec..fbab5f90 100644 --- a/src-tauri/src/chat/registry.rs +++ b/src-tauri/src/chat/registry.rs @@ -422,6 +422,7 @@ mod tests { system_prompt: "test".to_string(), working_dir: None, effort_level: None, + ..Default::default() } } diff --git a/src-tauri/src/chat/runtime.rs b/src-tauri/src/chat/runtime.rs index f3fe7513..2a1b20c7 100644 --- a/src-tauri/src/chat/runtime.rs +++ b/src-tauri/src/chat/runtime.rs @@ -236,6 +236,21 @@ impl ClaudeCliAdapter { effort_level: Option<&str>, resume_id: Option<&str>, message: &str, + ) -> Vec { + let mut args = Self::managed_turn_args_stdin(model, system_prompt, effort_level, resume_id); + args.push(message.to_string()); + args + } + + /// Same as [`managed_turn_args`] but WITHOUT the positional message — the + /// prompt is delivered on stdin instead (`claude -p` reads stdin when no + /// positional prompt is given). The pipe transport uses this so a large + /// prompt can't trip Linux's 128 KiB per-argv-string limit (E2BIG). + pub fn managed_turn_args_stdin( + model: &str, + system_prompt: &str, + effort_level: Option<&str>, + resume_id: Option<&str>, ) -> Vec { let mut args = vec!["--print".to_string()]; args.extend(CLAUDE_STREAM_OUTPUT_FLAGS.iter().map(|s| s.to_string())); @@ -256,7 +271,6 @@ impl ClaudeCliAdapter { args.push(id.to_string()); } - args.push(message.to_string()); args } } diff --git a/src-tauri/src/chat/session.rs b/src-tauri/src/chat/session.rs index 449e55b9..7b52f231 100644 --- a/src-tauri/src/chat/session.rs +++ b/src-tauri/src/chat/session.rs @@ -41,13 +41,20 @@ pub enum SessionState { } /// Configuration for creating a session (stored, reused across messages). -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct SessionConfig { pub cli_path: String, pub model: String, pub system_prompt: String, pub working_dir: Option, pub effort_level: Option, + /// Read-only MCP tooling for the chef (claude `--mcp-config` path). When set, + /// the prompt passes `--strict-mcp-config --mcp-config ` and the message + /// drops the embedded board (the model fetches it via `get_board`). `None` for + /// per-task agents. + pub mcp_config_path: Option, + /// Tool names to allow-list (`--allowedTools`) when `mcp_config_path` is set. + pub allowed_tools: Vec, } /// Unified chat session wrapping a transport with lifecycle management. @@ -260,6 +267,7 @@ impl UnifiedChatSession { env_vars: None, cols, rows, + stdin_data: None, }; let mut transport = TmuxTransport::new(&self.session_name); @@ -323,13 +331,15 @@ impl UnifiedChatSession { // -- Internal helpers -- fn build_pipe_spawn_config(&self, message: &str) -> SpawnConfig { - let args = build_pipe_args_for_cli( + let (args, stdin_data) = build_pipe_args_for_cli( &self.config.cli_path, &self.config.model, &self.config.system_prompt, self.config.effort_level.as_deref(), self.resume_id.as_deref(), message, + self.config.mcp_config_path.as_deref(), + &self.config.allowed_tools, ); SpawnConfig { @@ -339,10 +349,15 @@ impl UnifiedChatSession { env_vars: None, cols: 80, rows: 24, + stdin_data, } } } +/// Returns `(argv, stdin_data)`. For claude the message is delivered on stdin +/// (returned as `Some`) and kept off argv so a long conversation can't exceed +/// Linux's 128 KiB per-arg limit (E2BIG). Codex keeps the message on argv. +#[allow(clippy::too_many_arguments)] fn build_pipe_args_for_cli( cli_path: &str, model: &str, @@ -350,17 +365,32 @@ fn build_pipe_args_for_cli( effort_level: Option<&str>, resume_id: Option<&str>, message: &str, -) -> Vec { + mcp_config_path: Option<&str>, + allowed_tools: &[String], +) -> (Vec, Option) { let cli_name = cli_path.rsplit('/').next().unwrap_or(cli_path); match cli_name { - "codex" => CodexCliAdapter::managed_turn_args(model, resume_id, message), - _ => ClaudeCliAdapter::managed_turn_args( - model, - system_prompt, - effort_level, - resume_id, - message, + "codex" => ( + CodexCliAdapter::managed_turn_args(model, resume_id, message), + None, ), + _ => { + let mut args = + ClaudeCliAdapter::managed_turn_args_stdin(model, system_prompt, effort_level, resume_id); + // Chef read-only MCP tooling. `--strict-mcp-config` ignores the user's + // global ~/.claude MCP servers so only this isolated server loads (no + // ToolSearch indirection); `--allowedTools` pre-approves the read tools. + if let Some(cfg) = mcp_config_path { + args.push("--strict-mcp-config".to_string()); + args.push("--mcp-config".to_string()); + args.push(cfg.to_string()); + if !allowed_tools.is_empty() { + args.push("--allowedTools".to_string()); + args.push(allowed_tools.join(",")); + } + } + (args, Some(message.to_string())) + } } } @@ -375,6 +405,7 @@ mod tests { system_prompt: "You are helpful".to_string(), working_dir: None, effort_level: None, + ..Default::default() } } @@ -416,7 +447,55 @@ mod tests { assert!(config.args.contains(&"--print".to_string())); assert!(config.args.contains(&"--resume".to_string())); assert!(config.args.contains(&"resume-abc".to_string())); - assert_eq!(config.args.last().unwrap(), "Hello world"); + // claude: the message is delivered on stdin, NOT argv (avoids E2BIG). + assert_eq!(config.stdin_data.as_deref(), Some("Hello world")); + assert!(!config.args.contains(&"Hello world".to_string())); + } + + #[test] + fn test_build_pipe_spawn_config_with_mcp_tools() { + // Chef tool-based context: claude gets the isolated MCP config + allow-list, + // and the board-bearing prompt is no longer needed on argv. + let mut cfg = test_config(); + cfg.mcp_config_path = Some("/tmp/chef-mcp.json".to_string()); + cfg.allowed_tools = vec![ + "mcp__kaitencode__get_board".to_string(), + "mcp__kaitencode__get_workspaces".to_string(), + ]; + let session = UnifiedChatSession::new(cfg, TransportType::Pipe); + + let config = session.build_pipe_spawn_config("what's in Review?"); + assert!(config.args.contains(&"--strict-mcp-config".to_string())); + let mcp_idx = config.args.iter().position(|a| a == "--mcp-config").unwrap(); + assert_eq!(config.args[mcp_idx + 1], "/tmp/chef-mcp.json"); + let allow_idx = config.args.iter().position(|a| a == "--allowedTools").unwrap(); + assert_eq!( + config.args[allow_idx + 1], + "mcp__kaitencode__get_board,mcp__kaitencode__get_workspaces" + ); + // Prompt still rides stdin. + assert_eq!(config.stdin_data.as_deref(), Some("what's in Review?")); + } + + #[test] + fn test_build_pipe_spawn_config_large_prompt_goes_to_stdin() { + // Regression: a long chef conversation / big board produced a >128 KiB + // argv string and exec() failed with E2BIG. The prompt must ride stdin, + // and every individual argv string must stay under the kernel limit. + const MAX_ARG_STRLEN: usize = 128 * 1024; + let session = UnifiedChatSession::new(test_config(), TransportType::Pipe); + + let huge = "x".repeat(512 * 1024); // 512 KiB — well past the per-arg cap + let config = session.build_pipe_spawn_config(&huge); + + assert_eq!(config.stdin_data.as_deref(), Some(huge.as_str())); + for arg in &config.args { + assert!( + arg.len() < MAX_ARG_STRLEN, + "argv string of {} bytes would trip E2BIG", + arg.len() + ); + } } #[test] diff --git a/src-tauri/src/chat/tmux_transport.rs b/src-tauri/src/chat/tmux_transport.rs index a6664daf..9f0d819e 100644 --- a/src-tauri/src/chat/tmux_transport.rs +++ b/src-tauri/src/chat/tmux_transport.rs @@ -129,6 +129,26 @@ pub fn ensure_tmux_server() -> Result<(), String> { .args(["set-option", "-g", "mouse", "on"]) .output(); + // Color fidelity. Set explicitly (not relying on the user's ~/.tmux.conf or a + // GUI-launch environment) so the hosted agent TUIs render in full color and + // alternate-screen / clear works: + // - default-terminal: what TERM panes see; tmux-256color advertises 256-color + // + smcup/rmcup (alternate screen) + clear capabilities. Only applies to + // sessions created AFTER it's set — hence here, pre-session. + // - terminal-overrides …:RGB: pass 24-bit truecolor through to the attached + // client (xterm.js attaches as xterm-256color; see attach_in_pty). + let _ = Command::new("tmux") + .args(["set-option", "-g", "default-terminal", "tmux-256color"]) + .output(); + let _ = Command::new("tmux") + .args([ + "set-option", + "-ga", + "terminal-overrides", + ",xterm-256color:RGB,*256col*:RGB", + ]) + .output(); + Ok(()) } @@ -361,6 +381,9 @@ impl TmuxTransport { let output = Command::new("tmux") .args(&args) .envs(config.env_vars.as_ref().cloned().unwrap_or_default()) + // Panes inherit TERM from default-terminal (tmux-256color); COLORTERM + // lets truecolor-aware apps inside the pane emit 24-bit color. + .env("COLORTERM", "truecolor") .output() .map_err(|e| format!("Failed to create tmux session: {}", e))?; @@ -380,9 +403,19 @@ impl TmuxTransport { let (pty, pts) = pty_process::blocking::open().map_err(|e| format!("Failed to open PTY: {}", e))?; - // Build tmux attach command + // Build tmux attach command. The attach client renders tmux's output into + // the xterm.js PTY, so ITS environment decides whether tmux emits color + + // clear/alternate-screen sequences. On a GUI launch TERM is unset, which + // makes tmux downgrade to a colorless dumb terminal — hence set it here + // explicitly. `-2` forces tmux to assume 256-color support. let mut cmd = pty_process::blocking::Command::new("tmux"); - cmd = cmd.arg("attach-session").arg("-t").arg(&name); + cmd = cmd + .env("TERM", "xterm-256color") + .env("COLORTERM", "truecolor") + .arg("-2") + .arg("attach-session") + .arg("-t") + .arg(&name); let child = cmd .spawn(pts) diff --git a/src-tauri/src/chat/transport.rs b/src-tauri/src/chat/transport.rs index bffc4d74..082f83a0 100644 --- a/src-tauri/src/chat/transport.rs +++ b/src-tauri/src/chat/transport.rs @@ -38,6 +38,11 @@ pub struct SpawnConfig { pub cols: u16, /// Initial terminal size (PTY only, ignored by pipe) pub rows: u16, + /// Data to write to the child's stdin after spawn (pipe transport only). + /// Used to pass the prompt to `claude -p` via stdin instead of argv, so a + /// long conversation/board can't blow past Linux's 128 KiB per-arg limit + /// (E2BIG). `None` => stdin is null (the default for PTY/interactive). + pub stdin_data: Option, } /// Events sent from a transport to the session layer. diff --git a/src-tauri/src/commands/agent.rs b/src-tauri/src/commands/agent.rs index 003ab5fc..c0673951 100644 --- a/src-tauri/src/commands/agent.rs +++ b/src-tauri/src/commands/agent.rs @@ -224,6 +224,7 @@ pub async fn start_agent( system_prompt: String::new(), working_dir: Some(working_dir.clone()), effort_level: None, + ..Default::default() }; let session = registry @@ -641,6 +642,7 @@ pub async fn send_task_input( system_prompt: String::new(), working_dir: Some(working_dir.clone()), effort_level: effort_level.clone(), + ..Default::default() }; let session = registry @@ -1623,6 +1625,7 @@ pub async fn switch_agent_transport( system_prompt: String::new(), working_dir: Some(validated_working_dir), effort_level: None, + ..Default::default() }; let session = registry @@ -1757,6 +1760,7 @@ pub async fn ensure_pty_session( system_prompt: String::new(), working_dir: Some(working_dir.clone()), effort_level: None, + ..Default::default() }; let session = registry @@ -1875,6 +1879,7 @@ pub async fn ensure_chef_terminal( system_prompt: String::new(), working_dir: Some(repo_path.clone()), effort_level: None, + ..Default::default() }; let session = registry .get_or_create(&key, config, TransportType::Pty) diff --git a/src-tauri/src/commands/orchestrator/stream_api.rs b/src-tauri/src/commands/orchestrator/stream_api.rs index 24114d7d..d988a254 100644 --- a/src-tauri/src/commands/orchestrator/stream_api.rs +++ b/src-tauri/src/commands/orchestrator/stream_api.rs @@ -40,6 +40,7 @@ pub(super) async fn stream_via_api( system_prompt: String::new(), working_dir: None, effort_level: None, + ..Default::default() }, ); diff --git a/src-tauri/src/commands/orchestrator/stream_cli.rs b/src-tauri/src/commands/orchestrator/stream_cli.rs index 5547955c..fa6c353e 100644 --- a/src-tauri/src/commands/orchestrator/stream_cli.rs +++ b/src-tauri/src/commands/orchestrator/stream_cli.rs @@ -32,12 +32,33 @@ pub(super) async fn stream_via_unified_cli( let (full_response, captured_cli_session_id) = { let mut registry = session_registry.lock().await; + // Tool-based board context (opt-in): the chef reads the board via the + // kaitencode-mcp `get_board` tool instead of embedding it in the prompt. + // Falls back to the embedded board if disabled or the binary isn't found. + let (mcp_config_path, allowed_tools) = if crate::config::chef_tools_enabled() { + match resolve_chef_mcp() { + Some((path, tools)) => (Some(path), tools), + None => { + log::warn!( + "KAITENCODE_CHEF_TOOLS is set but kaitencode-mcp was not found; \ + falling back to the embedded board" + ); + (None, Vec::new()) + } + } + } else { + (None, Vec::new()) + }; + let tools_active = mcp_config_path.is_some(); + let config = SessionConfig { cli_path: cli_path.to_string(), model: model.to_string(), system_prompt: String::new(), working_dir: None, effort_level: None, + mcp_config_path, + allowed_tools, }; let prompt_builder = ChefSession::new_cli(workspace_id.to_string(), config.clone()); @@ -72,9 +93,26 @@ pub(super) async fn stream_via_unified_cli( (workspace, columns, tasks) }; - let system_prompt = prompt_builder.build_system_prompt(&workspace, &columns, &tasks); + let system_prompt = if tools_active { + // No embedded board — the model reads it via get_board. + prompt_builder.build_system_prompt_tools(&workspace, &columns) + } else { + prompt_builder.build_system_prompt(&workspace, &columns, &tasks) + }; session.set_system_prompt(system_prompt); - let full_message = prompt_builder.augment_message(message, &workspace, &columns, &tasks); + + let full_message = if tools_active { + // Board fetched on demand via the tool — send only the user's text. + message.to_string() + } else if session.resume_id().is_some() { + // The board lives in the system prompt (rebuilt fresh each turn). Only on + // a --resume turn does Claude reuse the original session's stale system + // prompt, so only then prepend the board; otherwise it's pure duplication + // that bloats the prompt toward the argv/E2BIG limit. + prompt_builder.augment_message(message, &workspace, &columns, &tasks) + } else { + message.to_string() + }; let ws_id = workspace_id.to_string(); let chat_session_id = session_id.to_string(); @@ -274,3 +312,45 @@ fn emit_orchestrator_cli_event( | ChatEvent::Unknown => {} } } + +/// Read-only MCP tools the chef may call (board reads only — writes still go +/// through the action-block protocol). +const CHEF_READ_TOOLS: &[&str] = &[ + "get_workspaces", + "get_board", + "get_task", + "list_scripts", + "list_pipeline_templates", + "get_pipeline_template", +]; + +/// Resolve the chef's read-only MCP setup: locate the `kaitencode-mcp` binary, +/// write an isolated `--mcp-config` JSON to the data dir, and return its path plus +/// the allow-listed tool names. `None` if the binary can't be found (caller falls +/// back to the embedded board). +fn resolve_chef_mcp() -> Option<(String, Vec)> { + let mcp_bin = locate_kaitencode_mcp()?; + let cfg = serde_json::json!({ + "mcpServers": { "kaitencode": { "command": mcp_bin } } + }); + let path = crate::db::data_dir().join("chef-mcp.json"); + std::fs::write(&path, serde_json::to_string_pretty(&cfg).ok()?).ok()?; + let allowed = CHEF_READ_TOOLS + .iter() + .map(|t| format!("mcp__kaitencode__{t}")) + .collect(); + Some((path.to_string_lossy().into_owned(), allowed)) +} + +/// Locate the `kaitencode-mcp` binary: PATH first, then a sibling of the running +/// executable (dev: `target/release/kaitencode-mcp`). +fn locate_kaitencode_mcp() -> Option { + if let Some(p) = crate::commands::cli_detect::find_cli("kaitencode-mcp") { + return Some(p); + } + let exe = std::env::current_exe().ok()?; + let sibling = exe.parent()?.join("kaitencode-mcp"); + sibling + .exists() + .then(|| sibling.to_string_lossy().into_owned()) +} diff --git a/src-tauri/src/commands/siege.rs b/src-tauri/src/commands/siege.rs index 34282fb7..c0e82772 100644 --- a/src-tauri/src/commands/siege.rs +++ b/src-tauri/src/commands/siege.rs @@ -336,6 +336,7 @@ pub async fn start_siege( system_prompt: String::new(), working_dir: Some(workspace.repo_path.clone()), effort_level: None, + ..Default::default() }; let session = registry @@ -584,6 +585,7 @@ pub async fn continue_siege( system_prompt: String::new(), working_dir: Some(workspace.repo_path.clone()), effort_level: None, + ..Default::default() }; // Kill existing session for this task if any, then create fresh diff --git a/src-tauri/src/commands/system.rs b/src-tauri/src/commands/system.rs index 809fb052..226033bc 100644 --- a/src-tauri/src/commands/system.rs +++ b/src-tauri/src/commands/system.rs @@ -150,6 +150,17 @@ pub fn check_runtime_prerequisites() -> Vec { ] } +/// Recent CLI chat turns (what we sent + what came back) for the Debug tab. +#[tauri::command] +pub fn get_chat_debug() -> Vec { + crate::chat::debug::snapshot() +} + +#[tauri::command] +pub fn clear_chat_debug() { + crate::chat::debug::clear(); +} + #[cfg(test)] mod tests { use super::*; diff --git a/src-tauri/src/config/mod.rs b/src-tauri/src/config/mod.rs index 63fdfb36..33657935 100644 --- a/src-tauri/src/config/mod.rs +++ b/src-tauri/src/config/mod.rs @@ -51,6 +51,20 @@ fn interactive_mode_env_enabled() -> bool { } } +/// Opt-in flag for the tool-based chef context: the chef reads the board via the +/// kaitencode-mcp `get_board` tool instead of embedding the whole board in the +/// prompt. Env-only for now (`KAITENCODE_CHEF_TOOLS=1`); falls back to the +/// embedded board when off, or when the mcp binary can't be located. +pub fn chef_tools_enabled() -> bool { + match std::env::var("KAITENCODE_CHEF_TOOLS") { + Ok(value) => matches!( + value.trim().to_ascii_lowercase().as_str(), + "1" | "true" | "yes" | "on" + ), + Err(_) => false, + } +} + pub fn env_var(new_key: &str, deprecated_key: &str) -> Result { std::env::var(new_key).or_else(|new_err| match std::env::var(deprecated_key) { Ok(value) => { diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 667ffa02..2ba63ed5 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -325,6 +325,8 @@ pub fn run() { commands::cli_detect::check_cli_update, commands::cli_detect::check_cli_health, commands::system::check_runtime_prerequisites, + commands::system::get_chat_debug, + commands::system::clear_chat_debug, // Checklist commands commands::checklist::create_checklist, commands::checklist::update_checklist, diff --git a/src-tauri/src/llm/context.rs b/src-tauri/src/llm/context.rs index af5eb7ee..a6f29d09 100644 --- a/src-tauri/src/llm/context.rs +++ b/src-tauri/src/llm/context.rs @@ -158,6 +158,46 @@ Current tasks: ) } +/// Tool-based variant of [`build_cli_system_prompt`]: omits the embedded task +/// snapshot. The chef reads the board on demand via the `get_board` MCP tool, so +/// the prompt stays small regardless of board size. Writes still use the action +/// protocol (`CLI_OPERATION_PROMPT`). +pub fn build_cli_system_prompt_tools(workspace: &Workspace, columns: &[Column]) -> String { + let ordered_columns = ordered_columns(columns); + let column_names: Vec<&str> = ordered_columns.iter().map(|c| c.name.as_str()).collect(); + let columns_str = column_names.join(", "); + + format!( + r#"You are the orchestrator for "{workspace_name}", a Kanban board. + +Columns: {columns} + +This board's workspace id is `{workspace_id}`. You do NOT have the task list in +this prompt — read it with the `get_board` tool (pass `workspace_id`). Call +`get_board` whenever you need current tasks, never assume board state, and call it +again after you change something to confirm the result. `get_task` and +`get_workspaces` are also available. + +{board_schema} + +{operation_guidance} + +## Style +- Be concise. Short answers. +- No emojis. +- Use markdown for formatting (bold, lists, code). +- Ask clarifying questions if the request is ambiguous. + +{cli_operation_prompt}"#, + workspace_name = workspace.name, + workspace_id = workspace.id, + columns = columns_str, + board_schema = BOARD_SCHEMA_PROMPT, + operation_guidance = OPERATION_GUIDANCE_PROMPT, + cli_operation_prompt = CLI_OPERATION_PROMPT + ) +} + /// Build board context JSON to inject into the conversation pub fn build_board_context( workspace: &Workspace, diff --git a/src/components/panel/agent-panel.test.tsx b/src/components/panel/agent-panel.test.tsx index 2cadcedf..c31462e4 100644 --- a/src/components/panel/agent-panel.test.tsx +++ b/src/components/panel/agent-panel.test.tsx @@ -285,13 +285,14 @@ describe('AgentPanel session controls', () => { it('shows Activity, Terminal, and Changes tabs in headless mode without tab badges', () => { renderPanel() - // Icon-only tabs in the header; their accessible name comes from aria-label. + // Responsive tabs: at regular density labels are visible text (the accessible + // name comes from the label, not aria-label). const tabs = [ screen.getByRole('button', { name: 'Activity' }), screen.getByRole('button', { name: 'Terminal' }), screen.getByRole('button', { name: 'Changes' }), ] - expect(tabs.map((tab) => tab.getAttribute('aria-label'))).toEqual(['Activity', 'Terminal', 'Changes']) + expect(tabs.map((tab) => tab.textContent)).toEqual(['Activity', 'Terminal', 'Changes']) expect(screen.queryByRole('button', { name: 'Context' })).not.toBeInTheDocument() expect(screen.queryByText('live')).not.toBeInTheDocument() expect(screen.queryByText(/\+\d+/)).not.toBeInTheDocument() diff --git a/src/components/panel/agent-panel.tsx b/src/components/panel/agent-panel.tsx index 009d2827..48f23a5e 100644 --- a/src/components/panel/agent-panel.tsx +++ b/src/components/panel/agent-panel.tsx @@ -9,6 +9,12 @@ import { holdTask, killTaskSession } from '@/lib/ipc/agent' import { agentRestart, interactiveModeDevFlag } from '@/lib/ipc/agent-interactive' import { setTaskRuntimeModeOverride } from '@/lib/ipc/task' import { PanelTabs, type PanelTab } from '@/components/shared/panel-tabs' +import { + useElementWidth, + panelDensity, + PanelDensityContext, + usePanelDensity, +} from '@/hooks/use-element-width' import { ActivityIcon, TerminalIcon, ChangesIcon, FilesIcon } from '@/components/shared/tab-icons' import { WorkspaceFilesView } from './workspace-files-view' import { signalPtyInterrupt } from '@/lib/ipc/terminal' @@ -207,34 +213,18 @@ function HeadlessPanel({ task, onClose }: AgentPanelProps) { - - - { void handleKill() }} - /> - + { void handleHoldToggle() }} + killBusy={killBusy} + onKill={() => { void handleKill() }} + /> } viewSlot={ - - - { void handleKill() }} - /> - + { void handleHoldToggle() }} + killBusy={killBusy} + onKill={() => { void handleKill() }} + /> } /> @@ -525,10 +499,17 @@ function PanelHeader({ viewSlot, errorSlot, }: PanelHeaderProps) { + // Measure the controls row so tabs + right-side controls collapse in step + // (regular → icons → overflow) instead of overlapping at narrow panel widths. + const [rowRef, rowWidth] = useElementWidth() + const density = panelDensity(rowWidth) return ( +
- {/* Controls row: close + runtime/session controls. */} -
+ {/* Controls row: close + runtime/session controls. Fixed height so the + header never changes vertical size between label and icon states — + all controls are h-7 and vertically centered within it. */} +
{onClose && (
}
+ ) } @@ -582,7 +564,18 @@ const RUNTIME_CHOICES: { value: RuntimeChoice; label: string }[] = [ { value: 'interactive', label: 'Interactive (live TUI)' }, ] -function RuntimeModeToggle({ task }: { task: Task }) { +type RuntimeVariant = 'button' | 'compact' | 'menu' + +function RuntimeModeToggle({ + task, + variant = 'button', + onAfterSelect, +}: { + task: Task + /** `button` = labelled (regular), `compact` = icon-only, `menu` = flat list inside the overflow menu (mini). */ + variant?: RuntimeVariant + onAfterSelect?: () => void +}) { const [open, setOpen] = useState(false) const [busy, setBusy] = useState(false) const [interactiveAllowed, setInteractiveAllowed] = useState(true) @@ -605,6 +598,7 @@ function RuntimeModeToggle({ task }: { task: Task }) { const handleSelect = async (choice: RuntimeChoice) => { setOpen(false) + onAfterSelect?.() if (choice === current) return setBusy(true) try { @@ -621,6 +615,42 @@ function RuntimeModeToggle({ task }: { task: Task }) { } } + const choiceButtons = RUNTIME_CHOICES.map((choice) => { + const disabled = choice.value === 'interactive' && !interactiveAllowed + return ( + + ) + }) + + // Mini: render choices directly inside the overflow menu (no own dropdown). + if (variant === 'menu') { + return ( +
+
+ Runtime +
+ {choiceButtons} +
+ ) + } + + const iconOnly = variant === 'compact' return (
{open && (
- {RUNTIME_CHOICES.map((choice) => { - const disabled = choice.value === 'interactive' && !interactiveAllowed - return ( - - ) - })} + {choiceButtons}
)}
) } +function RuntimeIcon() { + return ( + + ) +} + +// Right-side header controls (runtime · hold · overflow), responsive to the +// density reported by PanelHeader: regular = labelled inline, compact = icons +// inline, mini = everything folded into the overflow (⋯) menu so nothing +// overlaps at narrow panel widths. +function AgentHeaderControls({ + task, + holdBusy, + onHoldToggle, + killBusy, + onKill, +}: { + task: Task + holdBusy: boolean + onHoldToggle: () => void + killBusy: boolean + onKill: () => void +}) { + const density = usePanelDensity() + + const killItem = (close: () => void) => ( + + ) + + if (density === 'mini') { + return ( + + {(close) => ( + <> + +
+ { close(); onHoldToggle() }} + variant="menu" + /> +
+ {killItem(close)} + + )} + + ) + } + + const compact = density === 'compact' + return ( + <> + + + {(close) => killItem(close)} + + ) +} + +type HoldVariant = 'full' | 'compact' | 'menu' + +function HoldButton({ + held, + busy, + onToggle, + variant, +}: { + held: boolean + busy: boolean + onToggle: () => void + variant: HoldVariant +}) { + const title = held ? 'Release auto-advance hold' : 'Hold auto-advance for this task' + if (variant === 'menu') { + return ( + + ) + } + const compact = variant === 'compact' + return ( + + ) +} + const AGENT_PANEL_TABS: readonly PanelTab[] = [ { value: 'activity', label: 'Activity', icon: , testId: 'agent-panel-tab-transcript' }, { value: 'terminal', label: 'Terminal', icon: , testId: 'agent-panel-tab-terminal' }, @@ -684,14 +830,16 @@ function HoldIcon() { ) } +// Generic ⋯ overflow menu shell. Children receive a `close` callback so items +// can dismiss the menu on activation. The agent header composes its contents +// (kill always; runtime + hold folded in at `mini` density). function PanelOverflowMenu({ - killBusy, - onKill, + children, }: { - killBusy: boolean - onKill: () => void + children: (close: () => void) => ReactNode }) { const [open, setOpen] = useState(false) + const close = useCallback(() => { setOpen(false) }, []) return (
@@ -701,7 +849,7 @@ function PanelOverflowMenu({ aria-expanded={open} aria-label="Agent actions" title="Agent actions" - className="flex h-7 w-7 items-center justify-center rounded-md border border-border-default bg-surface text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary" + className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md border border-border-default bg-surface text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary" style={{ cursor: 'pointer' }} > {open && ( -
- +
+ {children(close)}
)}
diff --git a/src/components/panel/agent-transcript.tsx b/src/components/panel/agent-transcript.tsx index 1285e483..2c96c5c5 100644 --- a/src/components/panel/agent-transcript.tsx +++ b/src/components/panel/agent-transcript.tsx @@ -1,7 +1,7 @@ -import { memo, useEffect, useMemo, useRef, useState, type ReactNode } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { AnimatePresence, motion } from 'motion/react' -import ReactMarkdown from 'react-markdown' import type { AgentTranscriptEvent } from '@/types/events' +import { ChatMarkdown, ChatEntry, ChatDivider } from './chat-flat' type QueuedMessage = { id: string @@ -76,7 +76,7 @@ export function AgentTranscript({
{isEmpty ? ( @@ -498,28 +498,35 @@ function runStatusDetail(items: RenderItem[]): string | undefined { function TranscriptItem({ item, isLatest }: { item: RenderItem; isLatest: boolean }) { switch (item.kind) { case 'meta': - return + return ( + + {item.label}{item.detail ? ` · ${item.detail}` : ''} + + ) case 'user': return ( - - - - + + + ) case 'assistant': return ( - - + {item.source === 'agent_output' ? - : } - + : } + ) case 'thinking': return ( - + - + ) case 'tool': return @@ -527,58 +534,21 @@ function TranscriptItem({ item, isLatest }: { item: RenderItem; isLatest: boolea return case 'end': return ( - - - + ) } } -function EntryShell({ - children, - isLatest, - border, -}: { - children: ReactNode - isLatest: boolean - border: string -}) { - return ( - - {children} - - ) -} - -function MetaEntry({ item, isLatest }: { item: Extract; isLatest: boolean }) { - return ( - -
- - {item.label}{item.detail ? ` · ${item.detail}` : ''} - -
- - ) -} - function ToolEntry({ item, isLatest }: { item: Extract; isLatest: boolean }) { const detail = item.detail?.trim() return ( - + - + ) } function CommandEntry({ item, isLatest }: { item: Extract; isLatest: boolean }) { const detail = joinDetails(item.content, item.detail)?.trim() return ( - + - + ) } @@ -656,7 +626,8 @@ function AgentOutputBlock({ content, storageKeyPrefix }: { content: string; stor const sections = useMemo(() => parseAgentOutputSections(cleanTmuxTailForTranscript(content)), [content]) return ( -
+ // Raw tmux tail — keep monospace so terminal alignment survives. +
{sections.map((section, index) => ( + return case 'thinking': return (
@@ -745,7 +716,7 @@ function ActionDisclosure({ exit={{ height: 0, opacity: 0 }} className="max-h-[40vh] overflow-auto break-words border-t border-border-default/70 px-3 py-2" > - + )} @@ -826,11 +797,15 @@ function PendingSemanticRunEntry({ return () => { clearInterval(interval) } }, [startTime]) + const detail = `starting${elapsed > 0 ? ` · ${String(elapsed)}s` : ''}${queueCount > 0 ? ` · ${String(queueCount)} queued` : ''}` return ( - -
- 0 ? ` · ${String(elapsed)}s` : ''}${queueCount > 0 ? ` · ${String(queueCount)} queued` : ''}`} /> - {onCancel && ( + Cancel - )} -
+ ) : null + } + >
waiting for transcript events...
-
+ ) } function QueuedEntry({ content }: { content: string }) { return ( - - - - + + + ) } -function EntryMeta({ - label, - tone, - timestamp, - detail, -}: { - label: string - tone: 'user' | 'agent' | 'running' | 'queued' | 'error' - timestamp?: string - detail?: string -}) { - const toneClass = { - user: 'text-accent', - agent: 'text-text-secondary', - running: 'text-running', - queued: 'text-accent/70', - error: 'text-error', - }[tone] - - return ( -
- {label} - {detail && {detail}} - {timestamp && ( - - )} -
- ) -} - -const MarkdownBlock = memo(function MarkdownBlock({ - content, - muted = false, -}: { - content: string - muted?: boolean -}) { - return ( -
-

{children}

, - h1: ({ children }) =>

{children}

, - h2: ({ children }) =>

{children}

, - h3: ({ children }) =>

{children}

, - ul: ({ children }) =>
    {children}
, - ol: ({ children }) =>
    {children}
, - li: ({ children }) =>
  • {children}
  • , - code: ({ children }) => ( - - {children} - - ), - pre: ({ children }) => ( -
    -              {children}
    -            
    - ), - blockquote: ({ children }) => ( -
    - {children} -
    - ), - a: ({ children, href }) => ( - - {children} - - ), - }} - > - {content} -
    -
    - ) -}) - function parseMetadata(metadataJson: string | null): Record { if (!metadataJson) return {} try { diff --git a/src/components/panel/chat-flat.tsx b/src/components/panel/chat-flat.tsx new file mode 100644 index 00000000..9857e676 --- /dev/null +++ b/src/components/panel/chat-flat.tsx @@ -0,0 +1,163 @@ +/** + * Shared flat-document chat primitives used by BOTH the per-task agent transcript + * and the chef/orchestrator chat — a single reading experience, no bubbles. + * + * A message is a left-border entry (accent tone), a small role label, and a + * full-width markdown body. This is the agent-transcript visual language lifted + * into one place so the chef chat can drop its bubbles and match it. + */ + +import { memo, type ReactNode } from 'react' +import { motion } from 'motion/react' +import ReactMarkdown from 'react-markdown' +import remarkGfm from 'remark-gfm' + +export type ChatTone = 'user' | 'agent' | 'running' | 'queued' | 'warning' | 'error' | 'system' + +const TONE_BORDER: Record = { + user: 'border-accent/50', + agent: 'border-border-default', + running: 'border-running/50', + queued: 'border-accent/30', + warning: 'border-warning/50', + error: 'border-error/50', + system: 'border-border-default', +} + +const TONE_LABEL: Record = { + user: 'text-accent', + agent: 'text-text-secondary', + running: 'text-running', + queued: 'text-accent/70', + warning: 'text-warning', + error: 'text-error', + system: 'text-text-secondary', +} + +function formatTime(timestamp?: string): string | null { + if (!timestamp) return null + const d = new Date(timestamp) + if (Number.isNaN(d.getTime())) return null + return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) +} + +/** + * One flat-document entry: left accent border + optional role label/detail/time, + * then arbitrary body (markdown, tool disclosures, streaming status, …). + */ +export function ChatEntry({ + tone, + label, + detail, + timestamp, + isLatest = false, + headerRight, + children, +}: { + tone: ChatTone + label?: string + detail?: string + timestamp?: string + isLatest?: boolean + /** Optional control rendered at the far right of the header row (e.g. Cancel). */ + headerRight?: ReactNode + children?: ReactNode +}) { + const time = formatTime(timestamp) + const hasHeader = Boolean(label || detail || time || headerRight) + return ( + + {hasHeader && ( +
    + {label && {label}} + {detail && {detail}} + {time && } + {headerRight && {headerRight}} +
    + )} + {children} +
    + ) +} + +/** Centered divider used for system notices (model switches, etc.). */ +export function ChatDivider({ children, isLatest = false }: { children: ReactNode; isLatest?: boolean }) { + return ( + +
    + {children} +
    + + ) +} + +/** + * The single markdown renderer for all chat surfaces. GFM (tables, strikethrough, + * task lists) on everywhere; newline-preserving paragraphs so agent output keeps + * its shape. + */ +export const ChatMarkdown = memo(function ChatMarkdown({ + content, + muted = false, +}: { + content: string + muted?: boolean +}) { + return ( +
    +

    {children}

    , + h1: ({ children }) =>

    {children}

    , + h2: ({ children }) =>

    {children}

    , + h3: ({ children }) =>

    {children}

    , + ul: ({ children }) =>
      {children}
    , + ol: ({ children }) =>
      {children}
    , + li: ({ children }) =>
  • {children}
  • , + strong: ({ children }) => {children}, + em: ({ children }) => {children}, + hr: () =>
    , + table: ({ children }) => ( +
    + {children}
    +
    + ), + th: ({ children }) => {children}, + td: ({ children }) => {children}, + code: ({ className, children }) => { + const isInline = !className + return isInline ? ( + {children} + ) : ( + {children} + ) + }, + pre: ({ children }) => ( +
    +              {children}
    +            
    + ), + blockquote: ({ children }) => ( +
    {children}
    + ), + a: ({ href, children }) => ( + + {children} + + ), + }} + > + {content} +
    +
    + ) +}) diff --git a/src/components/panel/chat-history.tsx b/src/components/panel/chat-history.tsx index d5b7cc61..e47271e3 100644 --- a/src/components/panel/chat-history.tsx +++ b/src/components/panel/chat-history.tsx @@ -1,9 +1,8 @@ import { useState, useEffect, useRef, memo } from 'react' -import { motion, AnimatePresence } from 'motion/react' -import ReactMarkdown from 'react-markdown' -import remarkGfm from 'remark-gfm' +import { motion } from 'motion/react' import type { ChatMessage } from '@/lib/ipc' import { ToolCallItem, type ToolCallData } from './shared' +import { ChatEntry, ChatDivider, ChatMarkdown } from './chat-flat' type QueuedMessage = { id: string @@ -17,9 +16,10 @@ type ChatHistoryProps = { processingStartTime?: number | null thinkingContent?: string toolCalls?: ToolCallData[] - onCancel?: () => void queuedMessages?: QueuedMessage[] emptyState?: React.ReactNode + /** Role label for assistant turns (the chef chat shows "chef"). */ + assistantLabel?: string } export function ChatHistory({ @@ -29,9 +29,9 @@ export function ChatHistory({ processingStartTime = null, thinkingContent = '', toolCalls = [], - onCancel, queuedMessages = [], emptyState, + assistantLabel = 'chef', }: ChatHistoryProps) { const scrollRef = useRef(null) @@ -74,39 +74,43 @@ export function ChatHistory({ const isProcessing = processingStartTime !== null return ( -
    +
    {messages.map((msg, index) => ( - + ))} {isProcessing && ( - )} - {/* Queued messages shown as pending */} {queuedMessages.map((queued) => ( - + ))}
    ) } -type MessageBubbleProps = { +type MessageEntryProps = { message: ChatMessage + assistantLabel: string isLatest: boolean } -// Parse action blocks from message content and extract display text + actions -function parseMessageContent(content: string): { displayText: string; actions: Array<{ action: string; title?: string; column?: string; task_id?: string }> } { - const actions: Array<{ action: string; title?: string; column?: string; task_id?: string }> = [] - let displayText = content +type ParsedAction = { action: string; title?: string; column?: string; task_id?: string } - // Find and extract all ```action blocks +// Parse ```action blocks from message content; return display text + actions. +function parseMessageContent(content: string): { displayText: string; actions: ParsedAction[] } { + const actions: ParsedAction[] = [] const actionBlockRegex = /```action\s*\n?([\s\S]*?)```/g let match @@ -116,21 +120,18 @@ function parseMessageContent(content: string): { displayText: string; actions: A if (!captured) continue const parsed: unknown = JSON.parse(captured.trim()) if (Array.isArray(parsed)) { - actions.push(...(parsed as Array<{ action: string; title?: string; column?: string; task_id?: string }>)) + actions.push(...(parsed as ParsedAction[])) } } catch { // Invalid JSON, ignore } } - // Remove action blocks from display text - displayText = content.replace(actionBlockRegex, '').trim() - + const displayText = content.replace(actionBlockRegex, '').trim() return { displayText, actions } } -// Format action for display -function formatAction(action: { action: string; title?: string; column?: string; task_id?: string }): string { +function formatAction(action: ParsedAction): string { switch (action.action) { case 'create_task': return `Created "${action.title ?? 'task'}"${action.column ? ` in ${action.column}` : ''}` @@ -145,129 +146,63 @@ function formatAction(action: { action: string; title?: string; column?: string; } } -function MessageBubble({ message, isLatest }: MessageBubbleProps) { - const isUser = message.role === 'user' - const isSystem = message.role === 'system' - - if (isSystem) { - const isModelSwitch = message.content.startsWith('Switched to ') - return ( - -
    - - {message.content} - -
    - - ) +// Memoized: while a response streams, the parent re-renders on every token but +// the settled history messages don't change — skip re-rendering them. +const MessageEntry = memo(function MessageEntry({ message, assistantLabel, isLatest }: MessageEntryProps) { + if (message.role === 'system') { + return {message.content} } - // Parse action blocks for assistant messages - const { displayText, actions } = isUser ? { displayText: message.content, actions: [] } : parseMessageContent(message.content) + const isUser = message.role === 'user' + const { displayText, actions } = isUser + ? { displayText: message.content, actions: [] as ParsedAction[] } + : parseMessageContent(message.content) return ( - -
    - {/* Show executed actions with checkmarks */} - {actions.length > 0 && ( -
    - {actions.map((action, idx) => ( -
    - - - - {formatAction(action)} -
    - ))} -
    - )} - {displayText && } -
    -
    + {actions.length > 0 && } + {displayText && } + ) -} +}) -// Memoized markdown renderer for chat messages -const MarkdownContent = memo(function MarkdownContent({ content }: { content: string }) { +function ActionList({ actions }: { actions: ParsedAction[] }) { return ( -
    -

    {children}

    , - table: ({ children }) => ( -
    - {children}
    -
    - ), - th: ({ children }) => {children}, - td: ({ children }) => {children}, - hr: () =>
    , - strong: ({ children }) => {children}, - em: ({ children }) => {children}, - ul: ({ children }) =>
      {children}
    , - ol: ({ children }) =>
      {children}
    , - li: ({ children }) =>
  • {children}
  • , - h1: ({ children }) =>

    {children}

    , - h2: ({ children }) =>

    {children}

    , - h3: ({ children }) =>

    {children}

    , - code: ({ className, children }) => { - const isInline = !className - return isInline ? ( - {children} - ) : ( - {children} - ) - }, - pre: ({ children }) => ( -
    {children}
    - ), - a: ({ href, children }) => ( - - {children} - - ), - blockquote: ({ children }) => ( -
    - {children} -
    - ), - }} - > - {content} -
    +
    + {actions.map((action, idx) => ( +
    + + + + {formatAction(action)} +
    + ))}
    ) -}) +} -type StreamingBubbleProps = { +type StreamingEntryProps = { content: string startTime?: number | null thinkingContent?: string toolCalls?: ToolCallData[] - onCancel?: () => void queueCount?: number + assistantLabel: string } -function StreamingBubble({ content, startTime, thinkingContent = '', toolCalls = [], onCancel, queueCount = 0 }: StreamingBubbleProps) { +function StreamingEntry({ + content, + startTime, + thinkingContent = '', + toolCalls = [], + queueCount = 0, + assistantLabel, +}: StreamingEntryProps) { const [elapsed, setElapsed] = useState(0) const [isThinkingExpanded, setIsThinkingExpanded] = useState(false) @@ -276,35 +211,34 @@ function StreamingBubble({ content, startTime, thinkingContent = '', toolCalls = setElapsed(0) return } - - // Set initial elapsed setElapsed(Math.floor((Date.now() - startTime) / 1000)) - const interval = setInterval(() => { setElapsed(Math.floor((Date.now() - startTime) / 1000)) }, 1000) - - return () => { clearInterval(interval); } + return () => { clearInterval(interval) } }, [startTime]) const hasThinking = thinkingContent.length > 0 const hasToolCalls = toolCalls.length > 0 const hasContent = content.length > 0 + const statusLabel = hasContent ? 'Typing' : hasToolCalls ? 'Using tools' : hasThinking ? 'Thinking' : 'Processing' + const detail = [ + statusLabel, + elapsed > 0 ? `${String(elapsed)}s` : null, + queueCount > 0 ? `${String(queueCount)} queued` : null, + ].filter(Boolean).join(' · ') + return ( - -
    - {/* Thinking block - collapsible */} + +
    {hasThinking && (
    - - {isThinkingExpanded && ( - -

    - {thinkingContent} -

    -
    - )} -
    + {isThinkingExpanded && ( +

    {thinkingContent}

    + )}
    )} - {/* Tool calls */} {hasToolCalls && (
    {toolCalls.map((tool) => ( @@ -350,71 +272,45 @@ function StreamingBubble({ content, startTime, thinkingContent = '', toolCalls =
    )} - {/* Streaming content or typing indicator */} {hasContent ? ( - + ) : !hasThinking && !hasToolCalls ? ( ) : null} - - {/* Status line with cancel button */} -
    -

    - {hasContent ? 'Typing' : hasToolCalls ? 'Using tools' : hasThinking ? 'Thinking' : 'Processing'} - {elapsed > 0 ? ` · ${String(elapsed)}s` : ''} - {queueCount > 0 && ( - · {String(queueCount)} queued - )} -

    - {onCancel && ( - - )} -
    - +
    ) } function TypingDots() { return (
    - - - + {[0, 0.2, 0.4].map((delay) => ( + + ))}
    ) } -// Queued message bubble - shows as pending user message -function QueuedBubble({ content }: { content: string }) { +// Pending (not-yet-sent) turn. Visually distinct from sent messages: a muted, +// dashed, indented wrap with a "Queued" pill — so it reads as "waiting", not +// "delivered". +function QueuedEntry({ content }: { content: string }) { return ( - -
    -

    {content}

    -

    Queued

    +
    +
    + + Queued
    - +
    {content}
    +
    ) } diff --git a/src/components/panel/orchestrator-panel.tsx b/src/components/panel/orchestrator-panel.tsx index 17f8be6b..a24d093d 100644 --- a/src/components/panel/orchestrator-panel.tsx +++ b/src/components/panel/orchestrator-panel.tsx @@ -23,6 +23,7 @@ import { killTaskSession } from '@/lib/ipc/agent' import { chefSessionKey } from '@/lib/ipc/terminal' import { WorkspaceFilesView } from './workspace-files-view' import { PanelTabs, type PanelTab } from '@/components/shared/panel-tabs' +import { useElementWidth, panelDensity, PanelDensityContext } from '@/hooks/use-element-width' import { ChatIcon, TerminalIcon, FilesIcon } from '@/components/shared/tab-icons' type OrchestratorView = 'chat' | 'terminal' | 'files' @@ -35,7 +36,7 @@ import { PanelSidebar } from './panel-sidebar' import { PipelineDashboard } from './pipeline-dashboard' import { PipelineV2Dashboard } from './pipeline-v2-dashboard' import { ChatErrorBoundary } from './chat-error-boundary' -import { ErrorBanner, FailedMessageBanner, CliDetectingBanner, ChatInput, type ChatInputMessage, mapToolCalls } from './shared' +import { ErrorBanner, CliDetectingBanner, ChatInput, type ChatInputMessage, mapToolCalls } from './shared' type OrchestratorPanelProps = { workspaceId: string @@ -106,6 +107,11 @@ export function OrchestratorPanel({ workspaceId }: OrchestratorPanelProps) { disabled: isPanelCollapsed, }) + // Measure the header so its tabs drop to icons and the Cmd+J hint hides at + // narrow widths instead of overlapping the centered "Chef" title. + const [headerRef, headerWidth] = useElementWidth() + const headerDensity = panelDensity(headerWidth, { compactBelow: 480 }) + // Local UI state const [sidebarMode, setSidebarMode] = useState<'history' | 'dashboard' | 'v2-dashboard' | null>(null) const [viewMode, setViewMode] = useState('chat') @@ -117,8 +123,9 @@ export function OrchestratorPanel({ workspaceId }: OrchestratorPanelProps) { const [messagesLoading, setMessagesLoading] = useState(false) const [localError, setLocalError] = useState(null) - // Sync hook error to local state (like AgentPanel does) - const error = localError ?? chat.error ?? cliDetectionError + // Single error surface: stream errors, send/spawn failures, and CLI-detection + // all funnel into one banner (no separate retry/dismiss banner). + const error = localError ?? chat.error ?? chat.failedMessage?.error ?? cliDetectionError useEffect(() => { if (chat.error) setLocalError(chat.error) }, [chat.error]) @@ -342,7 +349,9 @@ export function OrchestratorPanel({ workspaceId }: OrchestratorPanelProps) { > {/* Header - clickable to toggle. */} +
    )} - - {isPanelCollapsed ? 'Cmd+J to expand' : 'Cmd+J'} - + {(isPanelCollapsed || headerDensity === 'regular') && ( + + {isPanelCollapsed ? 'Cmd+J to expand' : 'Cmd+J'} + + )}
    + {/* Content - only shown when expanded */} @@ -584,19 +597,12 @@ export function OrchestratorPanel({ workspaceId }: OrchestratorPanelProps) { <> {/* CLI Detection Indicator */} {cliDetecting && } - {/* Error Banner */} - {error && !chat.failedMessage && !cliDetecting && ( + {/* Single error banner (covers stream errors + send/spawn + failures). Dismiss clears every error source. */} + {error && !cliDetecting && ( { setLocalError(null); chat.clearError(); }} - /> - )} - {/* Failed message with retry/dismiss */} - {chat.failedMessage && ( - { void chat.retryFailed() }} - onDismiss={chat.dismissFailed} + onDismiss={() => { setLocalError(null); chat.clearError(); chat.dismissFailed() }} /> )} { void handleCancel() }} queuedMessages={chat.queue} /> = { @@ -85,6 +86,7 @@ type TabId = | 'runtime' | 'updates' | 'advanced' + | 'debug' type TabDef = { id: TabId @@ -132,6 +134,7 @@ const TAB_GROUPS: TabGroup[] = [ tabs: [ { id: 'runtime', label: 'Runtime', hint: 'Global defaults, sessions, GC, limits' }, { id: 'advanced', label: 'Advanced', hint: 'Terminal, panels, performance, git, shortcuts' }, + { id: 'debug', label: 'Debug', hint: 'Inspect CLI prompts, args, and tool calls' }, { id: 'updates', label: 'Updates', hint: 'Check for app updates' }, ], }, @@ -213,6 +216,8 @@ export function SettingsPanel() { return case 'runtime': return + case 'debug': + return case 'updates': return case 'advanced': diff --git a/src/components/settings/tabs/debug-tab.tsx b/src/components/settings/tabs/debug-tab.tsx new file mode 100644 index 00000000..735d1e47 --- /dev/null +++ b/src/components/settings/tabs/debug-tab.tsx @@ -0,0 +1,279 @@ +import { useCallback, useEffect, useState } from 'react' +import { getChatDebug, clearChatDebug, type ChatDebugTurn } from '@/lib/ipc/system' + +// ─── stream-json parsing ──────────────────────────────────────────────────── + +type ParsedEvent = + | { kind: 'text'; text: string } + | { kind: 'tool_use'; name: string; input: unknown } + | { kind: 'tool_result'; text: string } + | { kind: 'result'; text: string; meta: string } + | { kind: 'system'; text: string } + | { kind: 'raw'; text: string } + +function strProp(obj: unknown, key: string): string | null { + if (obj && typeof obj === 'object' && key in obj) { + const v = (obj as Record)[key] + if (typeof v === 'string') return v + } + return null +} + +function asText(content: unknown): string { + if (typeof content === 'string') return content + if (Array.isArray(content)) { + return content.map((b) => strProp(b, 'text') ?? '').join('') + } + return '' +} + +function parseEvents(lines: string[]): ParsedEvent[] { + const out: ParsedEvent[] = [] + for (const line of lines) { + let o: Record + try { + o = JSON.parse(line) as Record + } catch { + out.push({ kind: 'raw', text: line }) + continue + } + const type = o.type + const message = o.message as { content?: unknown[] } | undefined + if (type === 'assistant' && Array.isArray(message?.content)) { + for (const b of message.content as Array>) { + if (b.type === 'text' && typeof b.text === 'string' && b.text.trim()) { + out.push({ kind: 'text', text: b.text }) + } else if (b.type === 'tool_use') { + out.push({ kind: 'tool_use', name: strProp(b, 'name') ?? 'tool', input: b.input }) + } + } + } else if (type === 'user' && Array.isArray(message?.content)) { + for (const b of message.content as Array>) { + if (b.type === 'tool_result') { + out.push({ kind: 'tool_result', text: asText(b.content) }) + } + } + } else if (type === 'result') { + const durMs = o.duration_ms + const costUsd = o.total_cost_usd + const dur = typeof durMs === 'number' ? `${String(Math.round(durMs / 100) / 10)}s` : null + const cost = typeof costUsd === 'number' ? `$${costUsd.toFixed(4)}` : null + out.push({ + kind: 'result', + text: asText(o.result), + meta: [strProp(o, 'subtype'), dur, cost].filter(Boolean).join(' · '), + }) + } else if (type === 'system') { + out.push({ kind: 'system', text: strProp(o, 'subtype') ?? 'system' }) + } + } + return out +} + +// ─── arg parsing ──────────────────────────────────────────────────────────── + +function flagValue(args: string[], flag: string): string | null { + const i = args.indexOf(flag) + return i >= 0 ? (args[i + 1] ?? null) : null +} + +// ─── components ───────────────────────────────────────────────────────────── + +function Field({ label, children }: { label: string; children: React.ReactNode }) { + return ( +
    + {label} + {children} +
    + ) +} + +function Mono({ children }: { children: React.ReactNode }) { + return ( +
    +      {children}
    +    
    + ) +} + +function EventRow({ ev }: { ev: ParsedEvent }) { + switch (ev.kind) { + case 'system': + return
    ⚙ {ev.text}
    + case 'text': + return ( +
    + assistant + {ev.text} +
    + ) + case 'tool_use': + return ( +
    + 🔧 {ev.name} + {JSON.stringify(ev.input)} +
    + ) + case 'tool_result': + return ( +
    + ← result + {ev.text.length > 600 ? `${ev.text.slice(0, 600)}…` : ev.text} +
    + ) + case 'result': + return ( +
    + ✓ result + {ev.meta && {ev.meta}} + {ev.text &&
    {ev.text}
    } +
    + ) + case 'raw': + return
    {ev.text}
    + } +} + +function TurnCard({ turn }: { turn: ChatDebugTurn }) { + const [open, setOpen] = useState(false) + const [showRaw, setShowRaw] = useState(false) + const [showSys, setShowSys] = useState(false) + + const model = flagValue(turn.args, '--model') + const systemPrompt = flagValue(turn.args, '--system-prompt') + const mcpConfig = flagValue(turn.args, '--mcp-config') + const allowedTools = flagValue(turn.args, '--allowedTools') + const resume = flagValue(turn.args, '--resume') + const events = parseEvents(turn.output) + const cli = turn.command.split('/').pop() ?? turn.command + const time = new Date(turn.startedAtMs).toLocaleTimeString() + + return ( +
    + + + {open && ( +
    + {/* SENT */} +
    +
    ↑ Sent
    + + + {turn.command} {turn.args.filter((a) => a !== systemPrompt).join(' ')} + + + {allowedTools && ( + + {allowedTools} + + )} + {systemPrompt && ( + + + {showSys && {systemPrompt}} + + )} + + {turn.stdin ?? '(none — message on argv)'} + +
    + + {/* RECEIVED */} +
    +
    +
    ↓ Received
    + + {turn.outputTruncated && (truncated)} +
    + {turn.output.length === 0 ? ( +
    (no output captured)
    + ) : showRaw ? ( + {turn.output.join('\n')} + ) : ( +
    + {events.map((ev, i) => )} +
    + )} +
    +
    + )} +
    + ) +} + +export function DebugTab() { + const [turns, setTurns] = useState([]) + const [auto, setAuto] = useState(true) + + const refresh = useCallback(() => { + void getChatDebug().then(setTurns).catch(() => { /* ignore */ }) + }, []) + + useEffect(() => { + refresh() + if (!auto) return + const t = setInterval(refresh, 2000) + return () => { clearInterval(t) } + }, [auto, refresh]) + + return ( +
    +
    +

    CLI Chat Inspector

    +

    + The last {20} CLI turns (chef + managed agents) — exactly what we sent (command, args, + system prompt, stdin) and the raw stream-json we got back, with tool calls parsed out. +

    +
    + +
    + + + +
    + +
    + {turns.length === 0 ? ( +
    + No CLI turns yet — send a message in the chef or an agent chat. +
    + ) : ( + turns.map((t) => ) + )} +
    +
    + ) +} diff --git a/src/components/shared/panel-tabs.tsx b/src/components/shared/panel-tabs.tsx index e8a5e32e..553860dd 100644 --- a/src/components/shared/panel-tabs.tsx +++ b/src/components/shared/panel-tabs.tsx @@ -6,6 +6,7 @@ */ import type { ReactNode } from 'react' +import { usePanelDensity } from '@/hooks/use-element-width' export type PanelTab = { value: T @@ -22,6 +23,12 @@ type PanelTabsProps = { className?: string /** Icon-only (label as tooltip) — for tight headers like the agent panel. */ iconOnly?: boolean + /** + * Drop labels (icon-only) automatically when the surrounding `PanelHeader` + * reports a non-`regular` density. Shows labels when there's room, icons when + * tight — without the caller threading width through. + */ + responsive?: boolean 'aria-label'?: string } @@ -31,8 +38,11 @@ export function PanelTabs({ onChange, className = '', iconOnly = false, + responsive = false, 'aria-label': ariaLabel, }: PanelTabsProps) { + const density = usePanelDensity() + const showIconsOnly = iconOnly || (responsive && density !== 'regular') return (
    ({ key={tab.value} type="button" aria-pressed={active} - aria-label={iconOnly ? tab.label : undefined} - title={iconOnly ? tab.label : undefined} + aria-label={showIconsOnly ? tab.label : undefined} + title={showIconsOnly ? tab.label : undefined} data-testid={tab.testId} onClick={() => { onChange(tab.value) }} className={`relative inline-flex min-w-0 items-center gap-1.5 py-1.5 text-[11px] font-medium transition-colors ${ - iconOnly ? 'px-1.5' : 'px-2' + showIconsOnly ? 'px-1.5' : 'px-2' } ${active ? 'text-text-primary' : 'text-text-secondary hover:text-text-primary'}`} style={{ cursor: 'pointer' }} > {tab.icon} - {!iconOnly && {tab.label}} + {!showIconsOnly && {tab.label}} {active && (