79 KiB
79 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Changed
- Repo flattened — all source code moved from
v2/subdirectory to repo root. 351-commit history squashed by upstream rebuild. Branchhib_changes_v2created from new flatmainwith reconciled docs, CLI tools, and scaffolding. All path references in CLAUDE.md and docs/ updated
Added
- Plugin sandbox Web Worker migration — replaced
new Function()sandbox with dedicated Web Worker per plugin. True process-level isolation — no DOM, no Tauri IPC, no main-thread access. Permission-gated API proxied via postMessage with RPC pattern. 26 tests (MockWorker class in vitest) - seen_messages startup pruning —
pruneSeen()called fire-and-forget in App.svelte onMount. Removes entries older than 7 days (emergency: 3 days at 200k rows) - Aider autonomous mode toggle — per-project
autonomousModesetting ('restricted'|'autonomous') gates shell command execution in Aider sidecar. Default restricted. SettingsTab toggle - SPKI certificate pinning (TOFU) —
remote_probe_spkiTauri command +probe_spki_hash()extracts relay TLS certificate SPKI hash.remote_add_pin/remote_remove_pincommands. In-memory pin store in RemoteManager - Per-message btmsg acknowledgment —
seen_messagestable with session-scoped tracking replaces count-based polling.btmsg_unseen_messages,btmsg_mark_seen,btmsg_prune_seencommands. ON DELETE CASCADE cleanup - Aider parser test suite — 72 vitest tests for extracted
aider-parser.ts(pure parsing functions). 8 realistic Aider output fixtures. Covers prompt detection, suppression, turn parsing, cost extraction, shell execution, format-drift canaries - Dead code wiring — 4 orphaned Rust functions wired as Tauri commands:
btmsg_get_agent_heartbeats,btmsg_queue_dead_letter,search_index_task,search_index_btmsg
Security
- Plugin sandbox hardened —
new Function()(same-realm, escapable via prototype walking) replaced with Web Worker (separate JS context, no escape vectors). Eliminatesarguments.callee.constructorand({}).constructor.constructorattack paths
Changed
- SidecarManager actor refactor — replaced
Arc<Mutex<HashMap>>with dedicated actor thread viastd::sync::mpscchannel. Eliminates TOCTOU race conditions on session lifecycle. All mutable state owned by single thread - Aider parser extraction — pure functions (
looksLikePrompt,parseTurnOutput,extractSessionCost, etc.) extracted fromaider-runner.tstoaider-parser.tsfor testability. Runner imports from parser module
Fixed
- groups.rs test failure —
test_groups_roundtripmissing 9 Option fields added in P1-P10 (provider, model, use_worktrees, sandbox_enabled, anchor_budget_scale, stall_threshold_min, is_agent, agent_role, system_prompt) - remote_probe_spki tracing skip mismatch —
#[tracing::instrument(skip(state))]referenced non-existent parameter name. Removed unused State parameter
Added
- Comprehensive documentation suite — 4 new docs:
architecture.md(end-to-end system architecture with component hierarchy, data flow, IPC patterns),sidecar.md(multi-provider runner lifecycle, env stripping, NDJSON protocol, build pipeline),orchestration.md(btmsg messaging, bttask kanban, agent roles, wake scheduler, session anchors, health monitoring),production.md(sidecar supervisor, Landlock sandbox, FTS5 search, plugin system, secrets management, notifications, audit logging, error classification, telemetry) - Sidecar crash recovery/supervision —
bterminal-core/src/supervisor.rs: SidecarSupervisor wraps SidecarManager with auto-restart, exponential backoff (1s base, 30s cap, 5 retries), SidecarHealth enum (Healthy/Degraded/Failed), 5min stability window. 17 tests - Notification system — OS desktop notifications via
notify-rust+ in-app NotificationCenter.svelte (bell icon, unread badge, history max 100, 6 notification types). Agent dispatcher emits on complete/error/crash. notifications-bridge.ts adapter - Secrets management —
keyringcrate with linux-native (libsecret). SecretsManager in secrets.rs: store/get/delete/list with__bterminal_keys__metadata tracking. SettingsTab Secrets section. secrets-bridge.ts adapter. No plaintext fallback - Keyboard-first UX — Alt+1-5 project jump, Ctrl+H/L vi-nav, Ctrl+Shift+1-9 tab switch, Ctrl+J terminal toggle, Ctrl+Shift+K focus agent, Ctrl+Shift+F search overlay.
isEditing()guard prevents conflicts. CommandPalette rewritten: 18+ commands, 6 categories, fuzzy filter, arrow nav, keyboard shortcuts overlay - Agent health monitoring — heartbeats table + dead_letter_queue table in btmsg.db. 15s heartbeat polling in ProjectBox. Stale detection (5min threshold). ProjectHeader heart indicator (green/yellow/red). StatusBar health badge
- FTS5 full-text search — rusqlite upgraded to
bundled-full. SearchDb with 3 FTS5 virtual tables (search_messages, search_tasks, search_btmsg). SearchOverlay.svelte: Spotlight-style Ctrl+Shift+F overlay, 300ms debounce, grouped results with FTS5 highlight snippets - Plugin system —
~/.config/bterminal/plugins/with plugin.json manifest. plugins.rs: discovery, path-traversal-safe file reading, permission validation. plugin-host.ts: sandboxednew Function()execution, permission-gated API (palette, btmsg:read, bttask:read, events). plugins.svelte.ts store. SettingsTab plugins section. Example hello plugin - Landlock sandbox —
bterminal-core/src/sandbox.rs: SandboxConfig with RW/RO paths, applied viapre_exec()in sidecar child process. Requires kernel 6.2+ (graceful fallback). Per-project toggle in SettingsTab - Error classification —
error-classifier.ts: classifyApiError() with 6 types (rate_limit, auth, quota, overloaded, network, unknown), actionable messages, retry delays. 20 tests - Audit log — audit_log table in btmsg.db. AuditLogTab.svelte: Manager-only tab, filter by type+agent, 5s auto-refresh. audit-bridge.ts adapter. Events: agent_start/stop/error, task changes, wake events, prompt injection
- Usage meter — UsageMeter.svelte: compact inline cost/token meter with color thresholds (50/75/90%), hover tooltip. Integrated in AgentPane cost bar
- Team agent orchestration — install_cli_tools() copies btmsg/bttask to ~/.local/bin on startup. register_agents_from_groups() with bidirectional contacts. ensure_review_channels_for_group() creates #review-queue/#review-log per group
- Optimistic locking for bttask —
versioncolumn in tasks table.WHERE id=? AND version=?in update_task_status(). Conflict detection in TaskBoardTab. Both Rust + Python CLI updated - Unified test runner —
v2/scripts/test-all.shruns vitest + cargo tests with optional E2E (--e2eflag). npm scripts:test:all,test:all:e2e,test:cargo. Summary output with color-coded pass/fail - Testing gate rule —
.claude/rules/20-testing-gate.mdrequires running full test suite after every major change (new features, refactors touching 3+ files, store/adapter/bridge/backend changes) - E2E test mode infrastructure —
BTERMINAL_TEST=1env var disables file watchers (watcher.rs, fs_watcher.rs), wake scheduler, and allows data/config dir overrides viaBTERMINAL_TEST_DATA_DIR/BTERMINAL_TEST_CONFIG_DIR. Newis_test_modeTauri command bridges test state to frontend - E2E data-testid attributes — Stable test selectors on 7 key Svelte components: AgentPane (agent-pane, data-agent-status, agent-messages, agent-stop, agent-prompt, agent-submit), ProjectBox (project-box, data-project-id, project-tabs, terminal-toggle), StatusBar, AgentSession, GlobalTabBar, CommandPalette, TerminalTabs
- E2E Phase A scenarios — 7 human-authored test scenarios (22 tests) in
agent-scenarios.test.ts: app structural integrity, settings panel, agent pane initial state, terminal tab management, command palette, project focus/tab switching, agent prompt submission (graceful Claude CLI skip) - E2E test fixtures —
tests/e2e/fixtures.ts: creates isolated temp environments with data/config dirs, git repos, and groups.json.createTestFixture(),createMultiProjectFixture(),destroyTestFixture() - E2E results store —
tests/e2e/results-db.ts: JSON-based test run/step tracking (pivoted from better-sqlite3 due to Node 25 native compile failure) - E2E Phase B scenarios — 6 multi-project + LLM-judged test scenarios in
phase-b.test.ts: multi-project grid rendering, independent tab switching, status bar fleet state, LLM-judged agent response quality, LLM-judged code generation, context tab verification - LLM judge helper —
tests/e2e/llm-judge.ts: dual-mode judge (CLI first, API fallback). CLI backend spawnsclaudewith--output-format text(unsets CLAUDECODE). API backend uses raw fetch to Anthropic. Backend selectable viaLLM_JUDGE_BACKENDenv var. Structured verdicts (pass/fail + reasoning + confidence),assertWithJudge()with configurable min confidence threshold - E2E testing documentation —
docs/e2e-testing.md: comprehensive guide covering all 3 pillars (test fixtures, test mode, LLM judge), spec phases A-C, CI integration, WebKit2GTK pitfalls, troubleshooting - E2E CI workflow —
.github/workflows/e2e.yml: 3 jobs (vitest, cargo, e2e), xvfb-run for headless WebKit2GTK, path-filtered triggers on v2 source changes, LLM-judged tests gated onANTHROPIC_API_KEYsecret availability
Fixed
- E2E fixture env propagation —
tauri:options.envdoes not reliably set process-level env vars for Ruststd::env::var(). Addedprocess.envinjection at module scope in wdio.conf.js so fixture groups.json is loaded instead of real user config - LLM judge CLI context pollution — Claude CLI loaded project CLAUDE.md files causing model to refuse JSON output. Fixed by running judge from
cwd: /tmpwith--setting-sources userand--system-promptflags - E2E mocha timeout — Increased global mocha timeout from 60s to 180s. Agent-running tests (B4/B5) need 120s+ for Claude CLI round-trip
- E2E test suite — 27 failures fixed across 3 spec files: bterminal.test.ts (22 — stale v2 CSS selectors, v3 tab order/count, JS-dispatched KeyboardEvent for Ctrl+K, idempotent palette open/close, backdrop click close, scrollIntoView for below-fold settings, scoped theme dropdown selectors), agent-scenarios.test.ts (3 — JS click for settings button, programmatic focus check, graceful 40s agent timeout with skip), phase-b.test.ts (2 — waitUntil for project box render, conditional null handling for burn-rate/cost elements). 82 E2E passing, 0 failing, 4 skipped
- AgentPane.svelte missing closing
>— div tag with data-testid attributes was missing closing angle bracket, causing template parse issues
Changed
- WebDriverIO config — TCP readiness probe replaces blind 2s sleep for tauri-driver startup (200ms interval, 10s deadline). Added BTERMINAL_TEST=1 passthrough in capabilities
Security
claude_read_skillpath traversal: addedcanonicalize()+starts_with()validation to prevent reading arbitrary files via crafted skill paths (commands/claude.rs)- Sidecar env allowlist hardening — added
ANTHROPIC_*to Rust-levelstrip_provider_env_var()as defense-in-depth (Claude CLI uses credentials file, not env for auth). Dual-layer stripping documented: Rust layer (first checkpoint) + JS runner layer (per-provider) - Plugin sandbox hardening — 13 shadowed globals in
new Function()sandbox (window, document, fetch, globalThis, self, XMLHttpRequest, WebSocket, Function, importScripts, require, process, Deno, TAURI, TAURI_INTERNALS).thisbound to undefined via.call(). 35 tests covering all shadows, permissions, and lifecycle. Known escape vectors documented in JSDoc - WAL checkpoint — periodic
PRAGMA wal_checkpoint(TRUNCATE)every 5 minutes on sessions.db + btmsg.db to prevent unbounded WAL growth under sustained multi-agent load. 2 tests - TLS support for bterminal-relay — optional
--tls-certand--tls-keyCLI args. Server wraps TCP streams with native-tls. Client already supportswss://URLs. Generic handler refactor avoids code duplication - Landlock fallback logging — improved warning message with kernel version requirement (6.2+) and documented 3 enforcement states
Fixed
- btmsg.rs column index mismatch —
get_agents()usedSELECT a.*with positional index 7 forstatus, but column 7 is actuallysystem_prompt. Converted all query functions in btmsg.rs and bttask.rs from positional to named column access (row.get("column_name")). Added SQL aliases for JOIN columns - btmsg-bridge.ts camelCase mismatch —
BtmsgAgentandBtmsgMessageTypeScript interfaces used snake_case fields (group_id,unread_count,from_agent) but Rust#[serde(rename_all = "camelCase")]sends camelCase. Fixed interfaces + all consumers (CommsTab.svelte) - GroupAgentsPanel event propagation — toggleAgent button click propagated to parent card click handler (
setActiveProject). Addede.stopPropagation() - ArchitectureTab PlantUML encoding —
rawDeflate()was a no-op,encode64()did hex encoding. Collapsed into singleplantumlEncode()using PlantUML's~hhex encoding - TestingTab Tauri 2.x asset URL — used
asset://localhost/(Tauri 1.x). Fixed toconvertFileSrc()from@tauri-apps/api/core - Reconnect loop race in RemoteManager — orphaned reconnect tasks continued running after
remove_machine()ordisconnect(). Addedcancelled: Arc<AtomicBool>flag toRemoteMachine; set on removal/disconnect, checked each reconnect iteration.connect()resets flag for new connections (remote.rs) - Subagent delegation not triggering — Manager system prompt had no documentation of Agent tool / delegation capability. Added "Multi-Agent Delegation" section with usage examples and guidelines. Also inject
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1env var for Manager agents - Gitignore ignoring source code — root
.gitignoreplugins/rule matchedv2/src/lib/plugins/(source code). Narrowed to/plugins/and/v2/plugins/(runtime dirs only)
Added
- Reviewer agent role — Tier 1 specialist with reviewer workflow in
agent-prompts.ts(8-step process: inbox → review-queue → analyze → verdict → status update → review-log → report). Rustbttask.rsauto-posts to#review-queuebtmsg channel on task→review transition vianotify_review_channel()+ensure_review_channels()(idempotent).reviewQueueDepthinattention-scorer.ts(10pts/task, cap 50).review_queue_count()Rust function + Tauri command +reviewQueueCount()IPC bridge. ProjectBox: 'Tasks' tab for reviewer (reuses TaskBoardTab), 10s review queue polling →setReviewQueueDepth()in health store. 7 new vitest + 4 new cargo tests. 388 vitest + 76 cargo total - Auto-wake Manager scheduler —
wake-scheduler.svelte.ts+wake-scorer.tswith 3 user-selectable strategies: persistent (Manager stays running, resume prompt with fleet context), on-demand (fresh session per wake), smart (threshold-gated on-demand, default). 6 wake signals from tribunal S-3 hybrid: AttentionSpike(1.0), ContextPressureCluster(0.9), BurnRateAnomaly(0.8), TaskQueuePressure(0.7), ReviewBacklog(0.6), PeriodicFloor(0.1). Settings UI: strategy segmented button + threshold slider in Manager agent cards.GroupAgentConfigextended withwakeStrategy+wakeThresholdfields. 24 tests in wake-scorer.test.ts. 381 vitest + 72 cargo total - Dashboard metrics panel —
MetricsPanel.sveltenew ProjectBox tab ('metrics', PERSISTED-LAZY, all projects). Live view: fleet aggregates (running/idle/stalled + burn rate), project health grid (status, burn rate, context %, idle, tokens, cost, turns, model, conflicts, attention), task board summary (5 kanban columns polled every 10s), cross-project attention queue. History view: 5 switchable SVG sparkline charts (cost/tokens/turns/tools/duration) with area fill, stats row (last/avg/max/min), recent sessions table. 25 tests in MetricsPanel.test.ts. 357 vitest + 72 cargo total
Changed
- Branded types for GroupId/AgentId (SOLID Phase 3b) — Extended
types/ids.tswith GroupId and AgentId branded types. Applied to ~40 sites: groups.ts interfaces (ProjectConfig.id, GroupConfig.id, GroupAgentConfig.id, GroupsFile.activeGroupId), btmsg-bridge.ts (5 interfaces, 15 function params), bttask-bridge.ts (Task/TaskComment, 6 params), groups-bridge.ts (AgentMessageRecord, ProjectAgentState, SessionMetric), 3 Svelte components (GroupAgentsPanel, TaskBoardTab, SettingsTab). agentToProject() usesas unknown as ProjectIdcast for domain crossing. 12 tests in ids.test.ts. 332 vitest + 72 cargo total - Branded types for SessionId/ProjectId (SOLID Phase 3) —
types/ids.tswith compile-time branded types (string & { __brand }) and factory functions. Applied to ~140 sites across 11 files: Map/Set keys in conflicts.svelte.ts (4 maps), health.svelte.ts (2 maps), session-persistence.ts (3 maps), function signatures across 6 files, boundary branding at sidecar entry in agent-dispatcher.ts, Svelte component call sites in AgentSession/ProjectBox/ProjectHeader. 293 vitest + 49 cargo total - agent-dispatcher.ts split (SOLID Phase 2) — 496→260 lines. Extracted 4 modules:
utils/worktree-detection.ts(pure function),utils/session-persistence.ts(session maps + persist),utils/auto-anchoring.ts(compaction anchor),utils/subagent-router.ts(spawn + route). Dispatcher is now a thin coordinator - session.rs split (SOLID Phase 2) — 1008-line monolith split into 7 sub-modules under
session/directory: mod.rs (struct + migrate), sessions.rs, layout.rs, settings.rs, ssh.rs, agents.rs, metrics.rs, anchors.rs.pub(in crate::session)conn visibility. 21 new cargo tests - lib.rs command module split — 976-line monolith with 48 Tauri commands split into 11 domain modules under
src-tauri/src/commands/(pty, agent, watcher, session, persistence, knowledge, claude, groups, files, remote, misc). lib.rs reduced to ~170 lines (AppState + setup + handler registration) - Attention scorer extraction —
scoreAttention()pure function extracted from inline health store code toutils/attention-scorer.tswith 14 tests. Priority chain: stalled > error > context critical > file conflict > context high - Shared type guards — deduplicated
str()/num()runtime guards from claude-messages.ts, codex-messages.ts, ollama-messages.ts into sharedutils/type-guards.ts - btmsg/bttask WAL mode — added SQLite WAL journal mode + 5s busy_timeout to both
btmsg.rsandbttask.rsopen_db()for safe concurrent access from Python CLIs + Rust backend
Added
- Regression tests for btmsg/bttask bug fixes — 49 new tests: btmsg.rs (8, in-memory SQLite with named column access regression for status vs system_prompt), bttask.rs (7, named column access + serde camelCase), sidecar strip_provider_env_var (8, all prefix combinations), btmsg-bridge.test.ts (17, camelCase fields + IPC commands), bttask-bridge.test.ts (10, camelCase + IPC), plantuml-encode.test.ts (7, hex encoding algorithm). Total: 327 vitest + 72 cargo
- Configurable stall threshold — per-project range slider (5–60 min, step 5) in SettingsTab.
stallThresholdMininProjectConfig(groups.json),setStallThreshold()API in health store withstallThresholdsMap andDEFAULT_STALL_THRESHOLD_MSfallback. ProjectBox$effectsyncs config → store on mount/change - Memora adapter —
MemoraAdapter(memora-bridge.ts) implementsMemoryAdapterinterface, bridging to Memora's SQLite database (~/.local/share/memora/memories.db) via read-only Rust backend (memora.rs). FTS5 text search, tag filtering viajson_each(). 4 Tauri commands (memora_available, memora_list, memora_search, memora_get). Registered in App.svelte onMount. 16 vitest + 7 cargo tests. MemoriesTab now shows Memora memories on startup - Codex provider runner —
sidecar/codex-runner.tswraps@openai/codex-sdk(dynamic import, graceful failure if not installed). Maps Codex ThreadEvents (agent_message, reasoning, command_execution, file_change, mcp_tool_call, web_search) to common AgentMessage format viacodex-messages.tsadapter. Sandbox/approval mode mapping from BTerminal permission modes. Session resume via thread ID.providers/codex.tsProviderMeta (gpt-5.4 default, hasSandbox, supportsResume). 19 adapter tests - Ollama provider runner —
sidecar/ollama-runner.tsuses direct HTTP tolocalhost:11434/api/chatwith NDJSON streaming (zero external dependencies). Health check before session start. Configurable host/model/num_ctx/think via providerConfig. Supports Qwen3 extended thinking.ollama-messages.tsadapter maps streaming chunks to AgentMessage (text, thinking, cost with token counts).providers/ollama.tsProviderMeta (qwen3:8b default, modelSelection only). 11 adapter tests - All 3 providers registered in App.svelte onMount + message-adapters.ts.
build:sidecarbuilds all 3 runners - S-1 Phase 3: Worktree isolation per project — per-project
useWorktreestoggle in SettingsTab. When enabled, agents run in git worktrees at<repo>/.claude/worktrees/<sessionId>/via SDKextraArgs: { worktree: sessionId }. CWD-based worktree detection in agent-dispatcher (detectWorktreeFromCwd()) matches.claude/,.codex/,.cursor/worktree patterns on init events. Dual detection: CWD-based (primary) + tool_call-based (subagent fallback). 8 files, +125 lines, 7 new tests. 226 vitest + 42 cargo tests - S-2 Session Anchors — preserves important conversation turns through context compaction chains. Auto-anchors first 3 turns with observation masking (reasoning preserved in full per research). Manual pin button on AgentPane text messages. Three anchor types: auto (re-injectable), pinned (display-only), promoted (user-promoted, re-injectable). Re-injection via
system_promptfield. ContextTab anchor section with budget meter bar, per-anchor promote/demote/remove actions. SQLitesession_anchorstable with 5 CRUD commands. 5 new files, 7 modified. 219 vitest + 42 cargo tests - Configurable anchor budget scale —
AnchorBudgetScaletype with 4 presets: Small (2K), Medium (6K, default), Large (12K), Full (20K). Per-project 4-stop range slider in SettingsTab.ProjectConfig.anchorBudgetScalepersisted in groups.json. ContextTab budget meter derives from project setting. agent-dispatcher resolves scale on auto-anchor - Agent provider adapter pattern — full implementation (3 phases complete): core abstraction layer (provider types/registry/capabilities, message adapter registry, 4 file renames), Settings UI (collapsible per-provider config panels, per-project provider dropdown, settings persistence), sidecar routing (provider-based runner selection, env var stripping for CLAUDE*/CODEX*/OLLAMA*). 5 new files, 4 renames, 20+ modified. 6 architecture decisions (PA-1–PA-6). Docs at
docs/provider-adapter/ - PDF viewer in Files tab:
PdfViewer.svelteusing pdfjs-dist (v5.5.207). Canvas-based multi-page rendering, zoom controls (0.5x–3x, 25% steps), HiDPI-aware via devicePixelRatio. Reads PDF viaconvertFileSrc()— no new Rust commands needed - CSV table view in Files tab:
CsvTable.sveltewith RFC 4180 CSV parser (no external dependency). Auto-detects delimiter (comma, semicolon, tab). Sortable columns (numeric-aware), sticky header, row numbers, text truncation at 20rem - FilesTab routing update: Binary+pdf → PdfViewer, Text+csv → CsvTable. Updated file icons (📕 PDF, 📊 CSV)
- S-1 Phase 2: Filesystem write detection — inotify-based real-time file change detection via
ProjectFsWatcher(fs_watcher.rs). Watches project CWDs recursively, filters .git/node_modules/target, debounces 100ms per-file (fs_watcher.rs, lib.rs) - External write conflict detection: timing heuristic (2s grace window) distinguishes agent writes from external edits.
EXTERNAL_SESSION_IDsentinel,recordExternalWrite(),getExternalConflictCount(),FileConflict.isExternalflag (conflicts.svelte.ts) - Separate external write badge (orange ⚡) and agent conflict badge (red ⚠) in ProjectHeader (ProjectHeader.svelte)
externalConflictCountin ProjectHealth interface with attention scoring integration (health.svelte.ts)- Frontend bridge for filesystem watcher:
fsWatchProject(),fsUnwatchProject(),onFsWriteDetected(),fsWatcherStatus()(fs-watcher-bridge.ts) - Inotify watch limit sensing:
FsWatcherStatusreads/proc/sys/fs/inotify/max_user_watches, counts watched directories per project, warns at >75% usage with shell command to increase limit (fs_watcher.rs, lib.rs, ProjectBox.svelte) - Delayed scanning toast: "Scanning project directories…" info toast shown only when inotify status check takes >300ms, auto-dismissed on completion (ProjectBox.svelte)
notify()returns toast ID (was void) to enable dismissing specific toasts viadismissNotification(id)(notifications.svelte.ts)- ProjectBox
$effectstarts/stops fs watcher per project CWD on mount/unmount with toast on new external conflict + inotify capacity check (ProjectBox.svelte) - Collapsible text messages in AgentPane: model responses wrapped in
<details open>(open by default, user-collapsible with first-line preview) (AgentPane.svelte) - Collapsible cost summary in AgentPane:
cost.resultwrapped in<details>(collapsed by default, expandable with 80-char preview) (AgentPane.svelte) - Project max aspect ratio setting:
project_max_aspect(float 0.3–3.0, default 1.0) limits project box width via CSSmax-width: calc(100vh * var(--project-max-aspect))(SettingsTab.svelte, ProjectGrid.svelte, App.svelte) - No-implicit-push rule:
.claude/rules/52-no-implicit-push.md— never push unless user explicitly asks StartupWMClass=bterminalin install-v2.sh .desktop template for GNOME auto-move extension compatibility- MarkdownPane link navigation: relative file links open in Files tab, external URLs open in system browser via
xdg-open, anchor links scroll in-page (MarkdownPane.svelte, ProjectFiles.svelte, lib.rs) open_urlTauri command for opening http/https URLs in system browser (lib.rs)- Tab system overhaul: renamed Claude→Model, Files→Docs, added 3 new tabs (Files, SSH, Memory) with PERSISTED-EAGER/LAZY mount strategies (ProjectBox.svelte)
- FilesTab: VSCode-style directory tree sidebar + tabbed content viewer with shiki syntax highlighting, word wrap, image display via convertFileSrc, 10MB file size gate, collapsible/resizable sidebar, preview vs pinned tabs (FilesTab.svelte)
- CodeEditor: CodeMirror 6 editor component with Catppuccin theme (reads --ctp-* CSS vars), 15 lazy-loaded language modes, auto-close brackets, bracket matching, code folding, line numbers, search, line wrapping, Ctrl+S save binding, blur event (CodeEditor.svelte)
- FilesTab editor mode: files are now editable with dirty dot indicator on tabs, (unsaved) label in path bar, Ctrl+S save, auto-save dirty tabs on close (FilesTab.svelte)
- Rust
write_file_contentcommand: writes content to existing files only — safety check prevents creating new files (lib.rs) - Save-on-blur setting:
files_save_on_blurtoggle in Settings → Defaults → Editor, auto-saves files when editor loses focus (SettingsTab.svelte, FilesTab.svelte) - SshTab: SSH connection CRUD panel with launch-to-terminal button, reuses existing ssh-bridge.ts model (SshTab.svelte)
- MemoriesTab: pluggable knowledge explorer with MemoryAdapter interface, adapter registry, search, tag display, expandable cards (MemoriesTab.svelte, memory-adapter.ts)
- Rust
list_directory_childrencommand: lazy tree expansion, hidden files skipped, dirs-first alphabetical sort (lib.rs) - Rust
read_file_contentcommand: FileContent tagged union (Text/Binary/TooLarge), 30+ language mappings (lib.rs) - Frontend
files-bridge.tsadapter: DirEntry and FileContent TypeScript types + IPC wrappers - ContextTab: LLM context window visualization with stats bar (tokens, cost, turns, duration), segmented token meter (color-coded by message type), file references tree (extracted from tool calls), and collapsible turn breakdown — replaces old ContextPane ctx database viewer (ContextTab.svelte)
- ContextTab AST view: per-turn SVG conversation trees showing hierarchical message flow (Turn → Thinking/Response/Tool Calls → File operations), with bezier edges, color-coded nodes, token counts, and detail tooltips (ContextTab.svelte)
- ContextTab Graph view: bipartite tool→file DAG with tools on left (color-coded by type) and files on right, curved SVG edges showing which tools touched which files, count badges on both sides (ContextTab.svelte)
- Compaction event detection:
compact_boundarySDK messages adapted toCompactionContenttype in sdk-messages.ts, ContextTab shows yellow compaction count pill in stats bar and red boundary nodes in AST view - Project health store: per-project activity state (running/idle/stalled), burn rate ($/hr EMA), context pressure (% of model limit), attention scoring with urgency weights (health.svelte.ts)
- Mission Control status bar: running/idle/stalled agent counts, total $/hr burn rate, "needs attention" dropdown priority queue with click-to-focus cards (StatusBar.svelte)
- ProjectHeader health indicators: color-coded status dot (green=running, orange=stalled), context pressure badge, burn rate badge (ProjectHeader.svelte)
- Session metrics SQLite table: per-project historical metrics with 100-row retention,
session_metric_saveandsession_metrics_loadTauri commands (session.rs, lib.rs) - Session metric persistence on agent completion: records peak tokens, turn count, tool call count, cost, model, status (agent-dispatcher.ts)
- File overlap conflict detection store: per-project tracking of Write/Edit tool file paths across agent sessions, detects when 2+ sessions write same file, SCORE_FILE_CONFLICT=70 attention signal (conflicts.svelte.ts)
- Shared tool-files utility: extractFilePaths() and extractWritePaths() extracted from ContextTab to reusable module (tool-files.ts)
- File conflict indicators: red "⚠ N conflicts" badge in ProjectHeader, conflict count in StatusBar, toast notification on new conflict, conflict cards in attention queue (ProjectHeader.svelte, StatusBar.svelte)
- Health tick auto-stop/auto-start: tick timer self-stops when no running/starting sessions, auto-restarts on recordActivity() (health.svelte.ts)
- Bash write detection in tool-files.ts: BASH_WRITE_PATTERNS regex array covering >, >>, sed -i, tee, cp, mv, chmod/chown — conflict detection now catches shell-based file writes (tool-files.ts)
- Worktree-aware conflict suppression: sessions in different git worktrees don't trigger conflicts, sessionWorktrees tracking map, setSessionWorktree() API, extractWorktreePath() detects Agent/Task isolation:"worktree" and EnterWorktree tool calls (conflicts.svelte.ts, tool-files.ts, agent-dispatcher.ts)
- Acknowledge/dismiss conflicts: acknowledgeConflicts(projectId) suppresses badge until new session writes, acknowledgedFiles state map, auto-clear on new session write to acknowledged file (conflicts.svelte.ts)
- Clickable conflict badge in ProjectHeader: red button with ✕ calls acknowledgeConflicts() on click with stopPropagation, hover darkens background (ProjectHeader.svelte)
useWorktreesoptional boolean field on ProjectConfig for future per-project worktree spawning setting (groups.ts)
Changed
- Anchor observation masking no longer truncates assistant reasoning text (was 500 chars) — reasoning is preserved in full per research consensus (JetBrains NeurIPS 2025, SWE-agent, OpenDev ACC); only tool outputs are compacted (anchor-serializer.ts)
getAnchorSettings()now accepts optionalAnchorBudgetScaleparameter to resolve budget from per-project scale setting (anchors.svelte.ts)- ContextTab now derives anchor budget from
anchorBudgetScaleprop viaANCHOR_BUDGET_SCALE_MAPinstead of hardcodedDEFAULT_ANCHOR_SETTINGS(ContextTab.svelte) - Renamed
sdk-messages.ts→claude-messages.ts,agent-runner.ts→claude-runner.ts,ClaudeSession.svelte→AgentSession.svelte— provider-neutral naming for multi-provider support agent-dispatcher.tsnow usesadaptMessage(provider, event)from message-adapters.ts registry instead of directly callingadaptSDKMessage— enables per-provider message parsing- Rust
AgentQueryOptionsgainedprovider(String, defaults "claude") andprovider_config(serde_json::Value) fields with serde defaults for backward compatibility - Rust
SidecarManager.resolve_sidecar_for_provider(provider)looks for{provider}-runner.mjsinstead of hardcodedclaude-runner.mjs - Rust
strip_provider_env_var()strips CLAUDE*/CODEX*/OLLAMA* env vars (whitelists CLAUDE_CODE_EXPERIMENTAL_*) - SettingsTab: added Providers section with collapsible per-provider config panels (enabled toggle, default model, capabilities display) and per-project provider dropdown
- AgentPane: capability-driven rendering via ProviderCapabilities props (hasProfiles, hasSkills, supportsResume gates)
- AgentPane UI redesign: sans-serif root font (system-ui), tool calls paired with results in collapsible
<details>groups, hook messages collapsed into compact labels, context window usage meter in status strip, cost bar made minimal (no background), session summary with translucent background, two-phase scroll anchoring, tool-aware output truncation (Bash 500/Read 50/Glob 20 lines), colors softened viacolor-mix(), responsive margins via container queries (AgentPane.svelte) - MarkdownPane: added inner scroll wrapper with
container-type: inline-size, responsive padding via shared--bterminal-pane-padding-inlinevariable (MarkdownPane.svelte) - Added
--bterminal-pane-padding-inline: clamp(0.75rem, 3.5cqi, 2rem)shared CSS variable for responsive pane padding (catppuccin.css)
Fixed
- FilesTab invalid HTML nesting: file tab bar used
<button>inside<button>which Svelte/browser rejects — changed outer element to<div role="tab">(FilesTab.svelte) - FilesTab file content not rendering: after inserting a FileTab into the
$statearray, the local plain-object reference lost Svelte 5 proxy reactivity — content mutations were invisible. Fixed by looking up from the reactive array before setting content (FilesTab.svelte) - ClaudeSession type errors: cast
last_session_idto UUID template literal type, add missingtimestampfield (fromcreated_at) to restored AgentMessage records (ClaudeSession.svelte) - Cost bar shows only last turn's cost instead of cumulative session total:
updateAgentCost()changed from assignment to accumulation (+=) so continued sessions properly sum costs across all turns (agents.svelte.ts) - ProjectBox tab switch destroys running agent sessions: changed
{#if activeTab}conditional rendering to CSSstyle:display(flex/none) for all three content panes and terminal section — ClaudeSession now stays mounted across tab switches, preserving session ID, message history, and running agents (ProjectBox.svelte) - Sidecar env var stripping now whitelists
CLAUDE_CODE_EXPERIMENTAL_*vars (both Rust sidecar.rs and JS agent-runner.ts) — previously allCLAUDE*vars were stripped, blocking feature flags like agent teams from reaching the SDK (sidecar.rs, agent-runner.ts) - E2E terminal tab tests: scoped selectors to
.tab-bar .tab-title(was.tab-titlewhich matched project tabs), usedbrowser.execute()for DOM text reads to avoid stale element issues (bterminal.test.ts) - E2E wdio.conf.js: added
wdio:enforceWebDriverClassic: trueto disable BiDi negotiation (wdio v9 injectswebSocketUrl:truewhich tauri-driver rejects), removed unnecessarybrowserName: 'wry', fixed binary path to Cargo workspace target dir (v2/target/debug/notv2/src-tauri/target/debug/) - E2E consolidated to single spec file: Tauri creates one app session per spec file; multiple files caused "invalid session id" on 2nd+ file (wdio.conf.js, bterminal.test.ts)
- E2E WebDriver clicks on Svelte 5 components:
element.click()doesn't reliably trigger onclick handlers inside complex components via WebKit2GTK/tauri-driver; replaced withbrowser.execute()JS-level clicks for .ptab, .dropdown-trigger, .panel-close (bterminal.test.ts) - Removed
tauri-plugin-logentirely —telemetry::init()already registers tracing-subscriber which bridges thelogcrate; adding plugin-log after panics with "attempted to set a logger after the logging system was already initialized" (lib.rs, Cargo.toml)
Changed
- E2E tests expanded from 6 smoke tests to 48 tests across 8 describe blocks: Smoke (6), Workspace & Projects (8), Settings Panel (6), Keyboard Shortcuts (5), Command Palette (5), Terminal Tabs (7), Theme Switching (3), Settings Interaction (8) — all in single bterminal.test.ts file
- wdio.conf.js: added SKIP_BUILD env var to skip cargo tauri build when debug binary already exists
Removed
- Ollama-specific warning toast from AgentPane when injecting anchors — replaced by generic configurable budget scale slider (AgentPane.svelte)
- Unused
notifyimport from AgentPane (AgentPane.svelte) tauri-plugin-logdependency from Cargo.toml — redundant with telemetry::init() tracing-subscriber setup- Individual E2E spec files (smoke.test.ts, keyboard.test.ts, settings.test.ts, workspace.test.ts) — consolidated into bterminal.test.ts
- Workspace teardown race:
switchGroup()now awaitswaitForPendingPersistence()before clearing agent state, preventing data loss when agents complete during group switch (agent-dispatcher.ts, workspace.svelte.ts) - SettingsTab switchGroup click handler made async with await to properly handle the async switchGroup() flow (SettingsTab.svelte)
- Re-entrant sidecar exit handler race condition: added
restartingguard flag preventing double-restart on rapid disconnect/reconnect (agent-dispatcher.ts) - Memory leak:
toolUseToChildPaneandsessionProjectMapmaps now cleared instopAgentDispatcher()(agent-dispatcher.ts) - Listener leak: 5 Tauri event listeners in machines store now tracked via
UnlistenFn[]array withdestroyMachineListeners()cleanup function (machines.svelte.ts) - Fragile abort detection: replaced
errMsg.includes('aborted')withcontroller.signal.abortedfor authoritative abort state check (agent-runner.ts) - Unhandled rejection:
handleMessagemade async with.catch()onrl.on('line')handler preventing sidecar crash on malformed input (agent-runner.ts) - Remote machine
add_machine/list_machines/remove_machineconverted fromtry_lock()(silent failure on contention) to async.lock().await(remote.rs) remove_machinenow abortsWsConnectiontasks before removal, preventing resource leak (remote.rs)save_agent_messageswrapped inunchecked_transaction()for atomic DELETE+INSERT, preventing partial writes on crash (session.rs)- Non-null assertion
msg.event!replaced with safe checkif (msg.event)in agent bridge event handler (agent-bridge.ts) - Runtime type guards (
str(),num()) replace bareascasts on untrusted SDK wire format in sdk-messages.ts - ANTHROPIC_* environment variables now stripped alongside CLAUDE* in sidecar agent-runner.ts
- Frontend persistence timestamps use
Math.floor(Date.now() / 1000)matching Rust seconds convention (agent-dispatcher.ts) - Remote disconnect handler converted from
try_lock()to async.lock().await(remote.rs) save_layoutpane_ids serialization error now propagated instead of silent fallback (session.rs)- ctx.rs Mutex::lock() returns Err instead of panicking on poisoned lock (5 occurrences)
- ctx CLI:
int()limit argument validated with try/except (ctx) - ctx CLI: FTS5 MATCH query wrapped in try/except for syntax errors (ctx)
- File watcher: explicit error for root-level path instead of silent fallback (watcher.rs)
- Agent bridge payload validated before cast to SidecarMessage (agent-bridge.ts)
- Profile.toml and resource_dir failures now log::warn instead of silent empty fallback (lib.rs)
Changed
- All ~100 px layout values converted to rem across 10 components per rule 18: AgentPane, ToastContainer, CommandPalette, SettingsTab, TeamAgentsPanel, AgentCard, StatusBar, AgentTree, TerminalPane, AgentPreviewPane (1rem = 16px base, icon/dot dimensions kept as px)
Added
- E2E testing infrastructure: WebdriverIO v9.24 + tauri-driver setup with
wdio.conf.js(lifecycle hooks for tauri-driver spawn/kill, debug binary build), 6 smoke tests (smoke.test.ts), TypeScript config,test:e2enpm script, 4 new devDeps (@wdio/cli, @wdio/local-runner, @wdio/mocha-framework, @wdio/spec-reporter) waitForPendingPersistence()export in agent-dispatcher.ts: counter-based fence that resolves when all in-flightpersistSessionForProject()calls complete- OpenTelemetry instrumentation:
telemetry.rsmodule with TelemetryGuard (Drop-based shutdown), tracing + optional OTLP/HTTP export to Tempo, controlled byBTERMINAL_OTLP_ENDPOINTenv var (absent = console-only fallback) #[tracing::instrument]on 10 key Tauri commands: pty_spawn, pty_kill, agent_query, agent_stop, agent_restart, remote_connect, remote_disconnect, remote_agent_query, remote_agent_stop, remote_pty_spawnfrontend_logTauri command: routes frontend telemetry events (level + message + context JSON) to Rust tracing layer withsource="frontend"fieldtelemetry-bridge.tsadapter:tel.info/warn/error/debug/trace()convenience wrappers for frontend → Rust tracing bridge via IPC- Agent dispatcher telemetry: structured events for agent_started, agent_stopped, agent_error, sidecar_crashed, and agent_cost (with full metrics: costUsd, tokens, turns, duration)
- Docker Tempo + Grafana stack (
docker/tempo/): Tempo (OTLP gRPC 4317, HTTP 4318, query 3200) + Grafana (port 9715) with auto-provisioned Tempo datasource - 6 new Rust dependencies: tracing 0.1, tracing-subscriber 0.3, opentelemetry 0.28, opentelemetry_sdk 0.28, opentelemetry-otlp 0.28, tracing-opentelemetry 0.29
ctx_register_projectTauri command andctxRegisterProject()bridge function: registers a project in the ctx database viaINSERT OR IGNOREinto sessions table; opens DB read-write briefly then closes- Agent preview terminal (
AgentPreviewPane.svelte): read-only xterm.js terminal that subscribes to agent session messages in real-time; renders Bash commands as cyan❯ command, file operations as yellow[Read/Write/Edit] path, tool results (80-line truncation), text summaries, errors in red, session start/complete with cost; usesdisableStdin: true, Canvas addon, theme hot-swap; spawned via 👁 button in TerminalTabs tab bar (appears when agent session is active); deduplicates — only one preview per session TerminalTab.typeextended with'agent-preview'variant andagentSessionId?: stringfield in workspace storeProjectBoxpassesmainSessionIdtoTerminalTabsfor agent preview tab creation- SettingsTab project settings card redesign: each project rendered as a polished card with icon picker (Svelte state-driven emoji grid popup), inline-editable name input, CWD with left-ellipsis (
direction: rtl), account/profile dropdown (vialistProfiles()from claude-bridge.ts), custom toggle switch (green track/thumb), and subtle remove footer with trash icon - Account/profile dropdown per project in SettingsTab: uses
listProfiles()to fetch Claude profiles, displays display_name + email in dropdown, blue badge styling; falls back to static label when single profile - ProjectHeader profile badge: account name styled as blue pill with translucent background (
color-mix(in srgb, var(--ctp-blue) 10%, transparent)), font-weight 600, expanded max-width to 8rem - Theme integration rule (
.claude/rules/51-theme-integration.md): mandates all colors via--ctp-*CSS custom properties, never hardcode hex/rgb/hsl values - AgentPane VSCode-style prompt: unified input always at bottom with auto-resizing textarea, send icon button (arrow SVG) inside rounded container, welcome state with chat icon when no session
- AgentPane session controls: New Session and Continue buttons shown after session completes, enabling explicit session management
- ClaudeSession
handleNewSession(): resets sessionId for fresh agent sessions, wired viaonExitprop to AgentPane - ContextPane "Initialize Database" button: when ctx database doesn't exist, shows a prominent button to create
~/.claude-context/context.dbwith full schema (sessions, contexts, shared, summaries + FTS5 + sync triggers) directly from the UI; replaces old "run ctx init" hint text; auto-loads data after successful init - Project-level tab bar in ProjectBox: Claude | Files | Context tabs switch the content area between ClaudeSession, ProjectFiles, and ContextPane
- ProjectFiles.svelte: project-scoped markdown file viewer (file picker sidebar + MarkdownPane), accepts cwd/projectName props
- ProjectHeader info bar: CWD path (ellipsized from start via
direction: rtl) + profile name displayed as read-only info alongside project icon/name - Emoji icon picker in SettingsTab: 24 project-relevant emoji in 8-column grid popup, replaces plain text icon input
- Native directory picker for CWD fields: custom
pick_directoryTauri command usingrfdcrate withset_parent(&window)for modal behavior on Linux; browse buttons added to Default CWD, existing project CWD, and Add Project path inputs in SettingsTab rfd = { version = "0.16", default-features = false, features = ["gtk3"] }direct dependency for modal file dialogs (zero extra compile — already built transitively via tauri-plugin-dialog)- CSS relative units rule (
.claude/rules/18-relative-units.md): enforces rem/em for layout CSS, px only for icons/borders/shadows
Changed
- ContextPane redesigned as project-scoped: now receives
projectName+projectCwdprops from ProjectBox; auto-registers project in ctx database on mount (INSERT OR IGNORE); removed project selector list — directly shows context entries, shared context, and session summaries for the current project; empty state showsctx set <project> <key> <value>usage hint; all CSS converted to rem; header shows project name in accent color - Sidebar simplified to Settings-only: removed Sessions, Docs, Context icons from GlobalTabBar (project-specific tabs already in ProjectBox); removed DocsTab/ContextTab imports from App.svelte; removed Alt+1..4 keyboard shortcuts; drawer always renders SettingsTab
- MarkdownPane file switching: replaced onMount-only
watchFile()with reactive$effectthat unwatches previous file and watches new one whenfilePathprop changes; addedhighlighterReadygate to prevent premature watches - MarkdownPane premium typography overhaul: font changed from
var(--ui-font-family)(resolved to JetBrains Mono) to hardcoded'Inter', system-ui, sans-seriffor proper prose rendering; addedtext-rendering: optimizeLegibility,-webkit-font-smoothing: antialiased,font-feature-settings: 'cv01', 'cv02', 'cv03', 'cv04', 'ss01'(Inter alternates); body color softened from--ctp-textto--ctp-subtext1for reduced dark-mode contrast; Tailwind-prose-inspired spacing (1.15-1.75em paragraph/heading margins); heading line-height tightened to 1.2-1.4 with negative letter-spacing on h1/h2; gradient HR (linear-gradientfading to transparent edges); link underlines usetext-decoration-colortransition (30% opacity → full on hover, VitePress pattern); blockquotes now italic with translucent bg; code blocks have insetbox-shadowfor depth; added h5 (uppercase small) and h6 styles; all colors via--ctp-*vars for 17-theme compatibility - ProjectBox terminal area: only visible on Claude tab, now collapsible — collapsed shows a status bar with chevron toggle, "Terminal" label, and tab count badge; expanded shows full 16rem TerminalTabs area. Default: collapsed. Grid rows:
auto auto 1fr auto - SettingsTab project settings: flat row layout replaced with stacked card layout; icon picker rewritten from DOM
classList.toggle('visible')to Svelte$state(iconPickerOpenFor); checkbox replaced with custom toggle switch component - SettingsTab CSS: all remaining px values in project section converted to rem; add-project form uses dashed border container
- AgentPane prompt: replaced separate initial prompt + follow-up input with single unified prompt area; removed
followUpPromptstate,handleSubmitfunction; follow-up handled viaisResumedetection inhandleUnifiedSubmit() - AgentPane CSS: migrated all legacy CSS vars (
--bg-primary,--bg-surface,--text-primary,--text-secondary,--text-muted,--border,--accent,--font-mono,--border-radius) to--ctp-*theme vars + rem units - ContextPane CSS: same legacy-to-theme var migration as AgentPane
- ProjectBox tab CSS: polished with
margin-bottom: -1pxactive tab trick (merges with content),scrollbar-width: none,focus-visibleoutline, hover withvar(--ctp-surface0)background - ProjectBox layout: CSS grid with 4 rows (
auto auto 1fr auto) — header | tab bar | content | terminal; content area switches by tab - AgentPane: removed DIR/ACC toolbar entirely — CWD and profile now passed as props from parent (set in Settings, shown in ProjectHeader); clean chat window with prompt + send button only
- AgentPane prompt area: anchored to bottom (
justify-content: flex-end) instead of vertical center, removedmax-width: 600pxconstraint — uses full panel width - ClaudeSession passes
project.profileto AgentPane for automatic profile resolution - ProjectGrid.svelte CSS converted from px to rem: gap 0.25rem, padding 0.25rem, min-width 30rem
- TerminalTabs.svelte CSS converted from px to rem: tab bar, tabs, close/add buttons, empty state
Removed
- Dead ctx code:
ContextTab.sveltewrapper component,CtxProjectstruct (Rust),list_projects()method,ctx_list_projectsTauri command,ctxListProjects()bridge function,CtxProjectTypeScript interface — all unused after ContextPane project-scoped redesign - Unused Python imports in
ctxCLI:os,datetime/timezonemodules - AgentPane session toolbar (DIR/ACC inputs) — CWD and profile are now props, not interactive inputs
- Nerd Font codepoints for project icons — replaced with emoji (
📁default) for cross-platform compatibility - Nerd Font
font-familydeclarations from ProjectHeader and TerminalTabs - Stub
pick_directoryTauri command (replaced bytauri-plugin-dialogfrontend API)
Fixed
ctx initfails when~/.claude-context/directory doesn't exist:get_db()calledsqlite3.connect()without creating the parent directory; addedDB_PATH.parent.mkdir(parents=True, exist_ok=True)before connect- Terminal tabs cannot be closed and all named "Shell 1":
$state<Map<string, TerminalTab[]>>in workspace store didn't trigger reactive updates for$derivedconsumers whenMap.set()was called; changedprojectTerminalsfromMaptoRecord<string, TerminalTab[]>(plain object property access is Svelte 5's strongest reactivity path) - SettingsTab icon picker not opening: replaced broken DOM
classList.toggle('visible')approach with Svelte$state(iconPickerOpenForkeyed by project ID); icon picker now reliably opens/closes and dismisses on click-outside or Escape - SettingsTab CWD path truncated from right: added
direction: rtl; text-align: left; unicode-bidi: plaintexton CWD input so path shows the end (project directory) instead of the beginning when truncated - Project icons showing "?" — Nerd Font codepoint
\uf120not rendering without font installed; switched to emoji - Native directory picker not opening: added missing
"dialog:default"permission tov2/src-tauri/capabilities/default.json— Tauri's IPC security layer silently blockedinvoke()calls without this capability - Native directory picker not modal on Linux: replaced
@tauri-apps/plugin-dialogopen()with custompick_directoryTauri command usingrfd::AsyncFileDialog::set_parent(&window)— the plugin skipsset_parenton Linux viacfg(any(windows, target_os = "macos"))gate - Native directory picker not dark-themed: set
GTK_THEME=Adwaita:darkviastd::env::set_varat Tauri startup to force dark theme on native GTK dialogs - Sidebar drawer not scaling to content width: removed leftover v2 grid layout on
#appinapp.css(display: grid; grid-template-columns: var(--sidebar-width) 1fr+ media queries) that constrained.app-shellto 260px first column; v3.app-shellmanages its own flexbox layout internally - ContextPane.svelte CSS converted from px to rem: font-size, padding, margin, gap; added
white-space: nowrapon.ctx-header/.ctx-errorfor intrinsic width measurement
Changed
- GlobalTabBar.svelte CSS converted from px to rem: rail width 2.75rem, button 2rem, gap 0.25rem, padding 0.5rem 0.375rem, border-radius 0.375rem; rail-btn color changed from --ctp-overlay1 to --ctp-subtext0 for better contrast
- App.svelte sidebar header CSS converted from px to rem: padding 0.5rem 0.75rem, close button 1.375rem, border-radius 0.25rem
- App.svelte sidebar drawer: JS
$effectmeasures content width viarequestAnimationFrame+querySelectorAllfor nowrap elements, headings, inputs, and tab-specific selectors;panelWidthstate drives inlinestyle:widthonaside.sidebar-panel - Sidebar panel changed from fixed width (28em) to content-driven sizing with
min-width: 16emandmax-width: 50%; each tab component defines its ownmin-width: 22em - Sidebar panel and panel-content overflow changed from
hiddentooverflow-y: autoto allow content to drive parent width - SettingsTab.svelte padding converted from px to rem (0.75rem 1rem)
- DocsTab.svelte converted from px to rem: file-picker 14em, picker-title/file-btn/empty padding in rem
- ContextTab.svelte, DocsTab.svelte, SettingsTab.svelte all now set
min-width: 22emfor content-driven drawer sizing - UI redesigned from top tab bar + right-side settings drawer to VSCode-style left sidebar: vertical icon rail (GlobalTabBar, 2.75rem, 4 SVG icons) + expandable drawer panel (content-driven width) + always-visible main workspace (ProjectGrid)
- GlobalTabBar rewritten from horizontal text tabs + gear icon to vertical icon rail with SVG icons for Sessions, Docs, Context, Settings; Props:
expanded/ontoggle(wassettingsOpen/ontoggleSettings) - Settings is now a regular sidebar tab (not a special right-side drawer);
WorkspaceTabtype:'sessions' | 'docs' | 'context' | 'settings' - App.svelte layout:
.main-rowflex container with icon rail + optional sidebar panel + workspace; state renamedsettingsOpen->drawerOpen - Keyboard shortcuts: Alt+1..4 (switch tabs + open drawer), Ctrl+B (toggle sidebar), Ctrl+, (toggle settings), Escape (close drawer)
- SettingsTab CSS:
height: 100%(wasflex: 1) for sidebar panel context
Added
- SettingsTab split font controls: separate UI font (sans-serif options: System Sans-Serif, Inter, Roboto, Open Sans, Lato, Noto Sans, Source Sans 3, IBM Plex Sans, Ubuntu) and Terminal font (monospace options: JetBrains Mono, Fira Code, Cascadia Code, Source Code Pro, IBM Plex Mono, Hack, Inconsolata, Ubuntu Mono, monospace), each with custom themed dropdown + size stepper (8-24px), font previews in own typeface
--term-font-familyand--term-font-sizeCSS custom properties in catppuccin.css (defaults: JetBrains Mono fallback chain, 13px)- Deep Dark theme group: 6 new themes (Tokyo Night, Gruvbox Dark, Ayu Dark, Poimandres, Vesper, Midnight) — total 17 themes across 3 groups (Catppuccin, Editor, Deep Dark). Midnight is pure OLED black (#000000), Ayu Dark near-black (#0b0e14), Vesper warm dark (#101010)
- Multi-theme system: 7 new editor themes (VSCode Dark+, Atom One Dark, Monokai, Dracula, Nord, Solarized Dark, GitHub Dark) alongside 4 Catppuccin flavors
ThemeIdunion type,ThemePalette(26-color interface),ThemeMeta(id/label/group/isDark),THEME_LISTregistry with group metadata,ALL_THEME_IDSfor validation- Theme store
getCurrentTheme()/setTheme()as primary API; deprecatedgetCurrentFlavor()/setFlavor()wrappers for backwards compat - SettingsTab custom themed dropdown for theme selection: color swatches (base color per theme), 4 accent color dots (red/green/blue/yellow), grouped sections (Catppuccin/Editor/Deep Dark) with styled headers, click-outside and Escape to close
- SettingsTab global settings section: theme selector, UI font dropdown (sans-serif options), Terminal font dropdown (monospace options), each with size stepper (8-24px), default shell input, default CWD input — all custom themed dropdowns (no native
<select>), all persisted via settings-bridge - Typography CSS custom properties (
--ui-font-family,--ui-font-size,--term-font-family,--term-font-size) in catppuccin.css with defaults; consumed by app.css body rule initTheme()now restores 4 saved font settings (ui_font_family, ui_font_size, term_font_family, term_font_size) from SQLite on startup alongside theme restoration- v3 Mission Control (All Phases 1-10 complete): multi-project dashboard with project groups, per-project Claude sessions, team agents panel, terminal tabs, 3 workspace tabs (Sessions/Docs/Context) + settings drawer
- v3 session continuity (P6):
persistSessionForProject()saves agent state + messages to SQLite on session complete;registerSessionProject()maps session to project;ClaudeSession.restoreMessagesFromRecords()restores cached messages on mount - v3 workspace teardown (P7):
clearAllAgentSessions()clears agent sessions on group switch; terminal tabs reset viaswitchGroup() - v3 data model:
groups.rs(Rust structs + load/save~/.config/bterminal/groups.json),groups.ts(TypeScript interfaces),groups-bridge.ts(IPC adapter),--groupCLI argument - v3 workspace store (
workspace.svelte.ts): replaceslayout.svelte.ts, manages groups/activeGroupId/activeTab/focusedProjectId with Svelte 5 runes - v3 SQLite migrations:
agent_messagestable (per-project message persistence),project_agent_statetable (sdkSessionId/cost/status per project),project_idcolumn on sessions - 12 new Workspace components: GlobalTabBar, ProjectGrid, ProjectBox, ProjectHeader, ClaudeSession, TeamAgentsPanel, AgentCard, TerminalTabs, CommandPalette, DocsTab, ContextTab, SettingsTab
- v3 App.svelte full rewrite: GlobalTabBar + tab content area + StatusBar (no sidebar, no TilingGrid)
- 24 new vitest tests for workspace store, 7 new cargo tests for groups (total: 138 vitest + 36 cargo)
- v3 adversarial architecture review: 3 agents (Architect, Devil's Advocate, UX+Performance Specialist), 12 issues identified and resolved
- v3 Mission Control redesign planning: architecture docs (
docs/architecture.md,docs/decisions.md,docs/findings.md), codebase reuse analysis - Claude profile/account switching:
claude_list_profiles()reads~/.config/switcher/profiles/directories withprofile.tomlmetadata (email, subscription_type, display_name); profile selector dropdown in AgentPane toolbar when multiple profiles available; selected profile'sconfig_dirpassed asCLAUDE_CONFIG_DIRenv override to SDK - Skill discovery and autocomplete:
claude_list_skills()reads~/.claude/skills/(directories withSKILL.mdor standalone.mdfiles); type/in agent prompt textarea to trigger autocomplete menu with arrow key navigation, Tab/Enter selection, Escape dismiss;expandSkillPrompt()reads skill content and injects as prompt - New frontend adapter
claude-bridge.ts:ClaudeProfileandClaudeSkillinterfaces,listProfiles(),listSkills(),readSkill()IPC wrappers - AgentPane session toolbar: editable working directory input, profile/account selector (shown when >1 profile), all rendered above prompt form
- Extended
AgentQueryOptionswith 5 new fields across full stack (Rust struct, sidecar JSON, SDK options):setting_sources(defaults to['user', 'project']),system_prompt,model,claude_config_dir,additional_directories - 4 new Tauri commands:
claude_list_profiles,claude_list_skills,claude_read_skill,pick_directory - Claude CLI path auto-detection:
findClaudeCli()in both sidecar runners checks common paths (~/.local/bin/claude, ~/.claude/local/claude, /usr/local/bin/claude, /usr/bin/claude) then falls back towhich/where; resolved path passed to SDK viapathToClaudeCodeExecutableoption - Early error reporting when Claude CLI is not found — sidecar emits
agent_errorimmediately instead of cryptic SDK failure
Changed
- SettingsTab global settings restructured to single-column layout with labels above controls, split into "Appearance" (theme, UI font, terminal font) and "Defaults" (shell, CWD) subsections; all native
<select>replaced with custom themed dropdowns - Font setting keys changed from
font_family/font_sizetoui_font_family/ui_font_size+term_font_family/term_font_size; UI font fallback changed from monospace to sans-serif app.cssbody font-family and font-size now use CSS custom properties (var(--ui-font-family),var(--ui-font-size)) instead of hardcoded values- Theme system generalized from Catppuccin-only to multi-theme: all 17 themes map to same
--ctp-*CSS custom properties (26 vars) — zero component-level changes needed CatppuccinFlavortype deprecated in favor ofThemeId;CatppuccinPalettedeprecated in favor ofThemePalette;FLAVOR_LABELSandALL_FLAVORSdeprecated in favor ofTHEME_LISTandALL_THEME_IDS
Fixed
- SettingsTab theme dropdown sizing: set
min-width: 180pxon trigger container,min-width: 280pxandmax-height: 400pxon dropdown menu,white-space: nowrapon option labels to prevent text truncation - SettingsTab input overflow: added
min-width: 0on.setting-rowto prevent flex children from overflowing container - SettingsTab a11y: project field labels changed from
<div><label>to wrapping<label><span class="field-label">pattern for proper label/input association - SettingsTab CSS: removed unused
.project-field labelselector, simplified input selector to.project-field input:not([type="checkbox"])
Removed
- Dead
update_ssh_session()method from session.rs and its unit test (method was unused after SSH CRUD refactoring) - Stale TilingGrid reference in AgentPane.svelte comment (TilingGrid was deleted in v3 P10)
Changed
- StatusBar rewritten for v3 workspace store: shows active group name, project count, agent count instead of pane counts; version label updated to "BTerminal v3"
- Agent dispatcher subagent routing: project-scoped sessions skip layout pane creation (subagents render in TeamAgentsPanel instead); detached mode still creates layout pane
- AgentPane
cwdprop renamed toinitialCwd— now editable via text input in session toolbar instead of fixed prop
Removed
- Dead v2 components deleted in P10 (~1,836 lines):
TilingGrid.svelte(328),PaneContainer.svelte(113),PaneHeader.svelte(44),SessionList.svelte(374),SshSessionList.svelte(263),SshDialog.svelte(281),SettingsDialog.svelte(433) - Empty component directories removed:
Layout/,Sidebar/,Settings/,SSH/ - Sidecar runners now pass
settingSources(defaults to['user', 'project']),systemPrompt,model, andadditionalDirectoriesto SDKquery()options - Sidecar runners inject
CLAUDE_CONFIG_DIRinto clean env whenclaudeConfigDirprovided in query message (multi-account support)
Fixed
- AgentPane Svelte 5 event modifier syntax:
on:clickchanged toonclick(Svelte 5 requires lowercase event handler attributes, not colon syntax) - CLAUDE* env var stripping now applied at Rust level in SidecarManager (bterminal-core/src/sidecar.rs):
env_clear()+envs(clean_env)strips all CLAUDE-prefixed vars before spawning sidecar process, providing primary defense against nesting detection (JS-side stripping retained as defense-in-depth)
Changed
- Sidecar resolution unified: single pre-built
agent-runner.mjsbundle replaces separateagent-runner-deno.ts+agent-runner.tslookup; same.mjsfile runs under both Deno and Node.js resolve_sidecar_command()in sidecar.rs now checks deno/node availability upfront before searching paths, improved error message with runtime availability note- Removed
agent-runner-deno.tsfrom tauri.conf.json bundled resources (onlydist/agent-runner.mjsshipped)
Added
@anthropic-ai/claude-agent-sdk^0.2.70 npm dependency for sidecar agent session managementbuild:sidecarnpm script for esbuild bundling of agent-runner.ts (SDK bundled in, no external dependency at runtime)permission_modefield in AgentQueryOptions (Rust, TypeScript) — flows from controller through sidecar to SDK, defaults to 'bypassPermissions', supports 'default' mode
Changed
- Sidecar agent runners migrated from raw
claudeCLI spawning (child_process.spawn/Deno.Command) to@anthropic-ai/claude-agent-sdkquery() function — fixes silent hang when CLI spawned with piped stdio (known bug github.com/anthropics/claude-code/issues/6775) - agent-runner.ts: sessions now use
{ query: Query, controller: AbortController }map instead ofChildProcessmap; stop usescontroller.abort()instead ofchild.kill() - agent-runner-deno.ts: sessions now use
AbortControllermap; usesnpm:@anthropic-ai/claude-agent-sdkimport specifier - Deno sidecar permissions expanded: added
--allow-writeand--allow-netflags in sidecar.rs (required by SDK) - CLAUDE* env var stripping now passes clean env via SDK's
envoption in query() instead of filtering process.env before spawn - SDK permissionMode and allowDangerouslySkipPermissions now dynamically set based on permission_mode option (was hardcoded to bypassPermissions)
- build:sidecar esbuild command no longer uses --external for SDK (SDK bundled into output)
Fixed
- AgentPane onDestroy no longer kills running agent sessions on component remount — stopAgent() moved from AgentPane.svelte onDestroy to TilingGrid.svelte onClose handler, ensuring agents only stop on explicit user close action
Previously Added
- Exponential backoff reconnection in RemoteManager: on disconnect, spawns async task with 1s/2s/4s/8s/16s/30s-cap backoff, uses attempt_tcp_probe() (TCP-only, no WS upgrade, 5s timeout, default port 9750), emits remote-machine-reconnecting and remote-machine-reconnect-ready events
- Frontend reconnection listeners: onRemoteMachineReconnecting and onRemoteMachineReconnectReady in remote-bridge.ts; machines store sets status to 'reconnecting' and auto-calls connectMachine() on ready
- Relay command response propagation: bterminal-relay now sends structured responses (pty_created, pong, error) back to client via shared event channel with commandId correlation
- send_error() helper in bterminal-relay for consistent error reporting across all command handlers
- PTY creation confirmation flow: pty_create command returns pty_created event with session ID and commandId; RemoteManager emits remote-pty-created Tauri event
- bterminal-core shared crate with EventSink trait: extracted PtyManager and SidecarManager into reusable crate at v2/bterminal-core/, EventSink trait abstracts event emission for both Tauri and WebSocket contexts
- bterminal-relay WebSocket server binary: standalone Rust binary at v2/bterminal-relay/ with token auth (--port, --token, --insecure CLI flags), rate limiting (10 attempts, 5min lockout), per-connection isolated PTY + sidecar managers
- RemoteManager for multi-machine WebSocket connections: v2/src-tauri/src/remote.rs manages WebSocket client connections to relay instances, 12 new Tauri commands for remote operations, heartbeat ping every 15s
- Remote machine management UI in settings: SettingsDialog "Remote Machines" section for add/remove/connect/disconnect
- Auto-grouping of remote panes in sidebar: remote panes auto-grouped by machine label in SessionList
- remote-bridge.ts adapter for remote machine IPC operations
- machines.svelte.ts store for remote machine state management (Svelte 5 runes)
- Pane.remoteMachineId field in layout store for local vs remote routing
- TauriEventSink (event_sink.rs) implementing EventSink trait for Tauri AppHandle
- Multi-machine support architecture design (
docs/multi-machine.md): WebSocket NDJSON protocol, pre-shared token + TLS auth, autonomous relay model - Subagent cost aggregation: getTotalCost() recursive helper in agents store aggregates cost across parent + all child sessions; total cost displayed in parent pane done-bar when children present
- 10 new subagent routing tests in agent-dispatcher.test.ts: spawn, dedup, child message routing, init/cost forwarding, fallbacks (28 total dispatcher tests, 114 vitest tests overall)
- TAURI_SIGNING_PRIVATE_KEY secret set in GitHub repo for auto-update signing
- Agent teams/subagent support (Phase 7): auto-detects subagent tool calls ('Agent', 'Task', 'dispatch_agent'), spawns child agent panes with parent/child navigation, routes messages via parentId field
- Agent store parent/child hierarchy: AgentSession extended with parentSessionId, parentToolUseId, childSessionIds; findChildByToolUseId() and getChildSessions() query functions
- AgentPane parent link bar: SUB badge with navigate-to-parent button for subagent panes
- AgentPane children bar: clickable chips per child subagent with status-colored indicators (running/done/error)
- SessionList subagent icon: subagent panes show '↳' instead of '*' in sidebar
- Session groups/folders: group_name column in sessions table, setPaneGroup in layout store, collapsible group headers in sidebar with arrow/count, right-click pane to set group
- Auto-update signing key: generated minisign keypair, pubkey configured in tauri.conf.json updater section
- Deno-first sidecar: SidecarCommand struct in sidecar.rs, resolve_sidecar_command() prefers Deno (runs TS directly) with Node.js fallback, both runners bundled via tauri.conf.json resources
- Vitest integration tests: layout.test.ts (30 tests), agent-bridge.test.ts (11 tests), agent-dispatcher.test.ts (28 tests) — total 114 vitest tests passing
- E2E test scaffold: v2/tests/e2e/README.md documenting WebDriver approach
- Terminal copy/paste: Ctrl+Shift+C copies selection, Ctrl+Shift+V pastes from clipboard to PTY (TerminalPane.svelte)
- Terminal theme hot-swap: onThemeChange() callback registry in theme.svelte.ts, open terminals update immediately when flavor changes
- Agent tree node click: clicking a tree node scrolls to the corresponding message in the agent pane (scrollIntoView smooth)
- Agent tree subtree cost: cumulative cost displayed in yellow below each tree node label (subtreeCost utility)
- Agent session resume: follow-up prompt input after session completes or errors, passes resume_session_id to SDK
- Pane drag-resize handles: splitter overlays in TilingGrid with mouse drag, supports 2-col/3-col/2-row layouts with 10-90% ratio clamping
- Auto-update CI workflow: release.yml generates latest.json with version, platform URL, and signature from .sig file; uploads as release artifact
- Deno sidecar proof-of-concept: agent-runner-deno.ts with same NDJSON protocol, compiles to single binary via deno compile
- Vitest test suite: sdk-messages.test.ts (SDK message adapter) and agent-tree.test.ts (tree builder/cost), vite.config.ts test config, npm run test script
- Cargo test suite: session.rs tests (SessionDb CRUD for sessions, SSH sessions, settings, layout) and ctx.rs tests (CtxDb error handling with missing database)
- tempfile dev dependency for Rust test isolation
Fixed
- Sidecar env var leak: both agent-runner.ts and agent-runner-deno.ts now strip ALL
CLAUDE*prefixed env vars before spawning the claude CLI, preventing silent hangs when BTerminal is launched from within a Claude Code terminal session (previously only CLAUDECODE was removed)
Changed
- RemoteManager reconnection probe refactored from attempt_ws_connect() (full WS handshake + auth) to attempt_tcp_probe() (TCP-only connect, no resource allocation on relay)
- bterminal-relay command handlers refactored: all error paths now use send_error() helper instead of log::error!() only; pong response sent via event channel instead of no-op
- RemoteManager disconnect handler: scoped mutex release before event emission to prevent deadlocks; spawns reconnection task
- PtyManager and SidecarManager extracted from src-tauri to bterminal-core shared crate (src-tauri now has thin re-export wrappers)
- Cargo workspace structure at v2/ level: members = [src-tauri, bterminal-core, bterminal-relay], Cargo.lock moved from src-tauri/ to workspace root
- agent-bridge.ts and pty-bridge.ts extended with remote routing (check remoteMachineId, route to remote_* commands)
- Agent dispatcher refactored to split messages: parentId-bearing messages routed to child panes via toolUseToChildPane Map, main session messages stay in parent
- Agent store createAgentSession() now accepts optional parent parameter for registering bidirectional parent/child links
- Agent store removeAgentSession() cleans up parent's childSessionIds on removal
- Sidecar manager refactored from Node.js-only to Deno-first with Node.js fallback (SidecarCommand abstraction)
- Session struct: added group_name field with serde default
- SessionDb: added update_group method, list/save queries updated for group_name column
- SessionList sidebar: uses Svelte 5 snippets for grouped pane rendering with collapsible headers
- Agent tree NODE_H increased from 32 to 40 to accommodate subtree cost display
- release.yml build step now passes TAURI_SIGNING_PRIVATE_KEY and PASSWORD env vars from secrets
- release.yml uploads latest.json alongside .deb and .AppImage artifacts
- vitest ^4.0.18 added as npm dev dependency
Previously Added
- SSH session management: SshSession CRUD in SQLite, SshDialog create/edit modal, SshSessionList grouped by folder with color dots, SSH pane type routing to TerminalPane with shell=/usr/bin/ssh (Phase 5)
- ctx context database integration: read-only CtxDb (Rust, SQLITE_OPEN_READ_ONLY), ContextPane with project selector, tabs for entries/summaries/search, ctx-bridge adapter (Phase 5)
- Catppuccin theme flavors: all 4 palettes (Latte/Frappe/Macchiato/Mocha) selectable via Settings dialog, theme.svelte.ts reactive store with SQLite persistence, TerminalPane theme-aware (Phase 5)
- Detached pane mode: pop-out terminal/agent panes into standalone windows via URL params (?detached=1), detach.ts utility, App.svelte conditional rendering (Phase 5)
- Shiki syntax highlighting: lazy singleton highlighter with catppuccin-mocha theme, 13 preloaded languages, integrated in MarkdownPane and AgentPane text messages (Phase 5)
- Tauri auto-updater plugin: tauri-plugin-updater (Rust + npm) + updater.ts frontend utility (Phase 6)
- Markdown rendering in agent text messages with Shiki code highlighting (Phase 5)
- Build-from-source installer
install-v2.shwith 6-step dependency checking (Node.js 20+, Rust 1.77+, WebKit2GTK, GTK3, and 8 other system libraries), auto-install via apt, binary install to~/.local/bin/bterminal-v2with desktop entry (Phase 6) - Tauri bundle configuration for .deb and AppImage targets with category, descriptions, and deb dependencies (Phase 6)
- GitHub Actions release workflow (
.github/workflows/release.yml): triggered onv*tags, builds on Ubuntu 22.04 with Rust/npm caching, uploads .deb + AppImage as GitHub Release artifacts (Phase 6) - Regenerated application icons from
bterminal.svgas RGBA PNGs (32x32, 128x128, 256x256, 512x512, .ico) (Phase 6) - Agent tree visualization: SVG tree of tool calls with horizontal layout, bezier edges, status-colored nodes (AgentTree.svelte + agent-tree.ts) (Phase 5)
- Global status bar showing terminal/agent pane counts, active agents with pulse animation, total tokens and cost (StatusBar.svelte) (Phase 5)
- Toast notification system with auto-dismiss (4s), max 5 visible, color-coded by type (notifications.svelte.ts + ToastContainer.svelte) (Phase 5)
- Agent dispatcher toast integration: notifications on agent complete, error, and sidecar crash (Phase 5)
- Settings dialog with default shell, working directory, and max panes configuration (SettingsDialog.svelte) (Phase 5)
- Settings persistence: key-value settings table in SQLite, Tauri commands settings_get/set/list, settings-bridge.ts adapter (Phase 5)
- Keyboard shortcuts: Ctrl+W close focused pane, Ctrl+, open settings dialog (Phase 5)
- SQLite session persistence with rusqlite (bundled, WAL mode) — sessions table + layout_state singleton (Phase 4)
- Session CRUD: save, delete, update_title, touch with 7 Tauri commands (Phase 4)
- Layout restore on app startup — panes and preset restored from database (Phase 4)
- File watcher backend using notify crate v6 — watches files, emits Tauri events on change (Phase 4)
- MarkdownPane component with marked.js rendering, Catppuccin-themed styles, and live reload (Phase 4)
- Sidebar "M" button for opening markdown/text files via file picker (Phase 4)
- Session bridge adapter for Tauri IPC (session + layout persistence wrappers) (Phase 4)
- File bridge adapter for Tauri IPC (watch, unwatch, read, onChange wrappers) (Phase 4)
- Sidecar crash detection — dispatcher listens for process exit, marks running sessions as error (Phase 3 polish)
- Sidecar restart UI — "Restart Sidecar" button in AgentPane error bar (Phase 3 polish)
- Auto-scroll lock — disables auto-scroll when user scrolls up, shows "Scroll to bottom" button (Phase 3 polish)
- Agent restart Tauri command (agent_restart) (Phase 3 polish)
- Agent pane with prompt input, structured message rendering, stop button, and cost display (Phase 3)
Fixed
- Svelte 5 rune stores (layout, agents, sessions) renamed from
.tsto.svelte.ts— runes only work in.svelteand.svelte.tsfiles, plain.tscaused "rune_outside_svelte" runtime error (blank screen) - Updated all import paths to use
.sveltesuffix for store modules - Node.js sidecar manager (Rust) for spawning and communicating with agent-runner via stdio NDJSON (Phase 3)
- Agent-runner sidecar: spawns
claudeCLI with--output-format stream-jsonfor structured agent output (Phase 3) - SDK message adapter parsing stream-json into 9 typed message types: init, text, thinking, tool_call, tool_result, status, cost, error, unknown (Phase 3)
- Agent bridge adapter for Tauri IPC (invoke + event listeners) (Phase 3)
- Agent dispatcher routing sidecar events to agent session store (Phase 3)
- Agent session store with message history, cost tracking, and lifecycle management (Phase 3)
- Keyboard shortcut: Ctrl+Shift+N to open new agent pane (Phase 3)
- Sidebar button for creating new agent sessions (Phase 3)
- Rust PTY backend with portable-pty: spawn, write, resize, kill with Tauri event streaming (Phase 2)
- xterm.js terminal pane with Canvas addon, FitAddon, and Catppuccin Mocha theme (Phase 2)
- CSS Grid tiling layout with 5 presets: 1-col, 2-col, 3-col, 2x2, master-stack (Phase 2)
- Layout store with Svelte 5 $state runes and auto-preset selection (Phase 2)
- Sidebar with session list, layout preset selector, and new terminal button (Phase 2)
- Keyboard shortcuts: Ctrl+N new terminal, Ctrl+1-4 focus pane (Phase 2)
- PTY bridge adapter for Tauri IPC (invoke + event listeners) (Phase 2)
- PaneContainer component with header bar, status indicator, and close button (Phase 2)
- Terminal resize handling with ResizeObserver and 100ms debounce (Phase 2)
- v2 project scaffolding: Tauri 2.x + Svelte 5 in
v2/directory (Phase 1) - Rust backend stubs: main.rs, lib.rs, pty.rs, sidecar.rs, watcher.rs, session.rs (Phase 1)
- Svelte frontend with Catppuccin Mocha CSS variables and component structure (Phase 1)
- Node.js sidecar scaffold with NDJSON communication pattern (Phase 1)
- v2 architecture planning: Tauri 2.x + Svelte 5 + Claude Agent SDK via Node.js sidecar
- Research documentation covering Agent SDK, xterm.js performance, Tauri ecosystem, and ultrawide layout patterns
- Phased implementation plan (6 phases, MVP = Phases 1-4)
- Error handling and testing strategy for v2
- Documentation structure in
docs/(task_plan, phases, findings, progress) - 17 operational rules in
.claude/rules/ - TODO.md for tracking active work
.claude/CLAUDE.mdbehavioral guide for Claude sessions- VS Code workspace configuration with Peacock color