Changelog

See what's new in PorkiCoder. Updates, fixes, and new features.

Version 2.0.1 (May 30, 2026) NEW

Refreshed Chat mode look

A visual polish pass on Chat mode. Where Agent mode reads as a live green "execution terminal," Chat mode now has its own calm blue "quiet editor" theme — so a glance at the window tells you which lane you're in. AI replies also pick up a small per-message identity mark, and the Chat/Agent switcher got a cleaner, more cohesive look.

  • Per-mode theming — Chat mode is themed around the app's blue accent (canvas, message framing, code blocks, input, effort bar, and scrollbar), a distinct counterpart to Agent mode's green HUD.
  • Clearer AI identity — Chat-lane responses now lead with a small cyberpig avatar that takes on the active mode's accent, giving each reply an identity without a repeated name label.
  • Restyled mode switcher — The Chat/Agent toggle is now a single, unified raised pill; only the accent (text, border, and icon glow) changes per mode, instead of two mismatched button styles.

Version 2.0.0 (May 30, 2026)

PorkiCoder 2.0 — Agent Everywhere 🚀

This is the big one. Agent Mode is now the default, and for the first time it runs on every model PorkiCoder supports — Claude, Gemini, Kimi, Grok, and the brand-new GPT 5.5. Pick any provider and get the same autonomous, tool-using agent with human-reviewed file writes. We also retired the old HOG and Normie modes in favor of a single, dead-simple Chat lane for quick questions — two modes instead of three, and no more guessing which one to use. Under the hood, 2.0 sheds roughly 3,250 lines of legacy code, making the whole app leaner and easier to evolve.

  • Agent Mode on all five providers — Grok and GPT join Claude, Gemini, and Kimi with native agent loops (Read, Glob, Grep, Bash, WebSearch, WebFetch, sub-Agent). Whatever model you prefer, Agent Mode now just works — with the same accept/reject diff review before anything touches disk.
  • GPT 5.5 — OpenAI's latest, wired in through the Responses API with streaming reasoning, replacing the previous Codex model in the selector.
  • Agent is the default — New conversations start in Agent Mode, so you're set up for real work from the first message.
  • Modes simplified to Chat + Agent — HOG and Normie are gone. Chat is a fast, no-tools Q&A lane (code blocks with a Copy button); Agent handles anything that needs to read or change files. To apply code, switch to Agent and it lands through a reviewed diff.
  • Kimi K2.6 is the new default model — Fast, capable, and a great everyday driver across both modes.
  • A leaner codebase — The entire legacy code-apply pipeline was removed (~3,250 lines), consolidating all file edits onto Agent Mode's reviewed-diff path. Fewer moving parts, fewer edge cases.

Version 1.14.0 (May 30, 2026)

Smarter Agent Mode — Sessions That Survive Restarts, Image Input, Spend Limits, and Claude Task Tracking

1.14.0 is a big upgrade to agent mode. Your agent conversations now persist across restarts, Gemini and Kimi can read image attachments in agent mode, you can set an optional per-run spend ceiling, and Claude agent mode gains task tracking and the ability to ask you a clarifying question mid-run. The activity card also got more useful — long-running commands stream their output live, and parallel tool calls collapse into one tidy card.

  • Agent sessions persist across restarts — Gemini/Kimi conversation history (and Claude's resume link) are now saved to disk, so quitting and relaunching no longer loses your agent thread. Bounded and self-pruning, with each thread schema-versioned so an app update can't replay a stale conversation.
  • Image attachments in agent mode (Gemini & Kimi) — Attach a screenshot or diagram and ask an agent about it, just like in normal chat. (Claude agent mode stays text-only for now and tells you so.)
  • Optional per-run spend limit — Set a dollar ceiling and a runaway agent run hard-stops once it's reached, with a clear "Stopped · budget" note instead of burning unbounded spend. Off by default.
  • Claude agent mode: task tracking + clarifying questions — Claude can now create and update a task list and ask you a question when it hits genuine ambiguity — the same tools Gemini and Kimi already had. Pressing Stop now also dismisses an open question prompt instead of leaving it stranded.
  • Live command output + cleaner parallel tools — A long npm test or build now streams its output into the agent's activity card as it runs, so it never looks frozen. When the agent fires several read-only tools at once, they group under a single collapsible "Running N tools in parallel" card.
  • Heads-up when a folder won't live-update — Opening a system directory (one PorkiCoder can't safely watch) now shows a brief notice, so an unwatched tree doesn't silently look broken.

Version 1.13.2 (May 29, 2026)

Smarter Terminal Tab Naming — Skips Slash Commands and Startup Prompts

When you launch Claude Code in a terminal tab, PorkiCoder auto-names the tab from your first real question. But the meta-input you type before that question was being grabbed as the name instead — a slash command like /effort or /model, or the trust-folder confirmation you press right after launching — leaving tabs labelled "Ultracode", "Unknown Command", or "One". 1.13.2 teaches the auto-namer to skip those and wait for the prompt that actually describes your session.

  • Slash commands no longer hijack the tab name — Lines starting with a slash (/effort, /model, /ultracode, or any unrecognized /command) are skipped as a naming basis — in both normal and full-screen-TUI keypad modes — so your first actual question names the tab.
  • Startup confirmations are ignored — The trust-folder prompt answer and quick y/n/menu selections pressed right after launching an agent no longer become the tab name, so you won't end up with a tab called "One" or "Yes".

Version 1.13.1 (May 28, 2026)

Terminal Tune-Up — Emoji Alignment Returns (Safely), Plus Zoom, Scroll & Readability Fixes

Back in 1.12.0 we rolled back the emoji-width fix to keep terminals opening reliably, and promised a safer version would return. This is it. 1.13.1 brings wide-character and emoji column alignment back — this time guarded so it can never block the terminal from opening — and clears out a batch of long-standing terminal annoyances around zooming, scrolling, and readability, all while keeping the classic matrix green-on-black look intact.

  • Emoji & wide-character alignment is back — safely — Unicode 11 width support returns so emoji, CJK, and other wide glyphs occupy the correct two cells and stop shearing your tables. It's now wrapped so that if the width data ever fails to load, the terminal falls back to normal widths instead of failing to open — the exact problem that forced the 1.12.0 rollback.
  • Zoom no longer double-zooms or breaks rendering — Pressing Cmd +/Cmd -/Cmd 0 in the terminal now resizes only the terminal font. Previously it also scaled the entire app at the same time, which threw off the terminal's character grid and garbled the display.
  • In-terminal pickers are readable again — Tools running inside the terminal (like the Claude Code CLI's @-file picker) were rendering the selected row as white-on-white because the terminal under-reported its color support. PorkiCoder now advertises full 256-color and true-color, so highlighted selections are properly visible.
  • Smoother scrolling — The mouse wheel now reliably reaches the very bottom of the output, and switching between terminal tabs no longer leaves the scroll "stuck" until you press a key.
  • Easier on the eyes — Dim text (prompts, comments, faint git hashes) has been lifted out of near-invisibility and glyphs render a touch crisper — without changing the signature matrix-green color.

Version 1.13.0 (May 28, 2026)

Claude Opus 4.8 — Now With the XHigh Effort Tier

Anthropic shipped Claude Opus 4.8, and PorkiCoder now runs it everywhere Opus 4.7 used to — in both regular chat and Agent mode. Opus 4.8 keeps the same pricing as 4.7 ($5 in / $25 out per million tokens) and the same 1M-token context window, so the upgrade costs nothing extra. Alongside the model swap, this release surfaces Anthropic's new XHigh effort level on the effort bar: a tier that sits between High and Max, tuned as the recommended starting point for long-horizon coding and agentic runs, with Max still reserved for the most demanding frontier problems.

  • Opus 4.8 replaces Opus 4.7 — The model picker now lists Opus 4.8 (claude-opus-4-8), wired end-to-end through chat, Agent mode, pricing, and usage tracking. If you had Opus 4.7 selected, PorkiCoder migrates you to 4.8 automatically — no blank dropdown, nothing to reselect.
  • New XHigh effort tier — The effort bar gains an XHi step between High and Max (the full ladder is Low → Med → High → XHi → Max). XHigh is Anthropic's suggested default for extended coding and agentic sessions, while Max remains the absolute ceiling. The new tier appears only for Opus 4.8 — every other model is unchanged.

Version 1.12.1 (May 27, 2026)

Polished Agent Chat Layout — Bubble Padding, Table Styling, and Button Placement

A handful of chat-panel visual fixes that had piled up since the agent-mode redesign. In Agent mode the AI response is wrapped in a subtle bubble, but its first heading would sit flush against the bubble's left edge with no internal padding — visually cramped. Markdown tables that the model produced rendered without any cell padding or borders, so file paths and descriptions ran into each other. And the delete/copy buttons on your own messages sat at the top-left corner, overlapping the first letter of whatever you'd typed. All three fixed in this patch.

  • Agent-mode AI bubble breathes — Added 20px of interior left padding and 56px right padding to the AI message bubble in Agent mode, so headings and paragraphs no longer touch the bubble border and never slide underneath the absolute-positioned action buttons.
  • Markdown tables render correctly — Tables in AI responses now have proper cell padding, header backgrounds, row separators, rounded outer borders, and wrapped cell content. Long file paths and descriptions wrap inside their cells instead of overflowing the chat panel.
  • Your message buttons moved off the text — The delete (✕) and copy buttons on user message bubbles now sit at the top-right corner with reserved padding, instead of overlapping the first word at top-left.

Version 1.12.0 (May 27, 2026)

Terminals Open Reliably Again — Rolling Back the Emoji-Width Change

1.11.2 introduced a change to make emoji and wide characters line up correctly in terminal tables, and 1.11.3 was a follow-up to fix a startup problem that change caused. After further testing, that whole approach proved too unreliable, so 1.12.0 removes it entirely and returns the terminal to the proven setup from 1.11.1. Terminals now open consistently every time. The trade-off: tables containing emoji may once again drift a column — a cosmetic issue we'll revisit with a safer fix — but a terminal that always opens matters more.

  • Reliable terminal startup — Removed the Unicode 11 width addon (added in 1.11.2) and the proposed-API flag (1.11.3) that together could leave the terminal failing to open. The terminal now initializes exactly as it did in the stable 1.11.1 release.
  • Emoji column-alignment temporarily removed — The emoji/wide-character width tweak is gone for now, so emoji-heavy tables may drift a column as they did before 1.11.2. A safer version will return in a future release.

Version 1.11.3 (May 27, 2026)

The Terminal Opens Again

On 1.11.2 the terminal could fail to open at all — you'd click into a terminal tab and get an empty panel. The cause was the very fix 1.11.2 shipped: switching the terminal to Unicode 11 width tables relies on an xterm capability that has to be explicitly turned on, and it wasn't. Activating the Unicode 11 addon threw an error partway through terminal setup — before the terminal was ever drawn — so the whole panel never appeared. 1.11.3 turns that capability on, so the emoji-alignment fix from 1.11.2 now genuinely works, and wraps the addon step so that even if it ever fails in the future the terminal still opens (just without the width tweak) instead of refusing to start.

  • Terminal reliably opens — Enables the xterm proposed-API flag the Unicode 11 width addon requires, fixing the 1.11.2 regression where the terminal silently failed to initialize.
  • Fail-safe Unicode activation — Activating the width tables can no longer block the terminal from opening; if it ever errors, the terminal falls back to default widths rather than not appearing.

Version 1.11.2 (May 26, 2026)

Emoji and Wide Characters No Longer Garble Tables in the Terminal

If you ran a terminal program that printed emoji — a ✅ or ❌ in a status table, like the ones Claude Code draws — the columns to the right would drift out of alignment: a stray character would bleed into the previous cell and the row would wrap early with a leftover return glyph. The cause: PorkiCoder's terminal measured character widths against Unicode 6 tables from 2010, which count modern emoji as one column wide, while the programs printing them assume two. Every emoji on a line knocked the rest of the row half a cell out of step. 1.11.2 switches the terminal to Unicode 11 width tables, so emoji and other wide characters take the two columns the printing program expects — and tables line up exactly.

  • Unicode 11 width tables — The terminal now activates xterm's Unicode 11 addon, matching the width math modern command-line tools use, so box-drawn tables, status lines, and progress output containing emoji render correctly aligned.

Version 1.11.1 (May 25, 2026)

Terminal No Longer Prints Duplicate Output When You Resize

If you ran an inline terminal app like Claude Code and the terminal's width changed — resizing the window, dragging the chat/sidebar splitter, entering fullscreen — the same block of output could suddenly appear twice, often wrapped at two slightly different widths. The cause: terminal UIs that draw directly into the scroll history (rather than a separate full-screen buffer) re-render their current frame every time the terminal is resized, and the already-printed copy has scrolled into permanent history where it can't be erased. PorkiCoder was sending more resize signals than necessary during a single gesture, so a running app redrew — and re-printed — its output more than once. 1.11.1 collapses each resize gesture into a single settled resize, so the terminal redraws at most once and the duplicate blocks are gone.

  • One resize signal per gesture — A burst of resize events (a window drag, a panel/sidebar animation, the fullscreen transition) now coalesces into a single trailing resize at the final settled size, instead of pushing intermediate widths that each triggered a redraw. The terminal still resizes the moment you stop; it just stops re-emitting the in-between sizes.
  • Idempotent resizes — The main process now skips a terminal resize entirely when the dimensions haven't actually changed, so a redundant redraw signal never reaches a running terminal app — closing the last path that could double-print on a no-op resize.

Version 1.11.0 (May 24, 2026)

PorkiCoder Now Updates Itself — Built-In Auto-Update, Plus a Flush-to-Bottom Terminal Scroll Fix

Until now, upgrading PorkiCoder meant coming back to this page and re-downloading the installer by hand. 1.11.0 ends that: the app now ships a built-in auto-updater that checks for new releases shortly after launch and every few hours, downloads them in the background, and shows a dismissible in-app banner with a Restart button when a new version is ready — no OS popups, no manual re-download. One catch, this release only: because 1.10.2 and earlier shipped without the updater, you'll need to grab 1.11.0 from the download button above this one time. Every release after 1.11.0 updates itself.

  • Background auto-update from the signed release feed — Built on electron-updater reading PorkiCoder's notarized GitHub release feed. The app checks ~10 seconds after launch and then every ~4 hours, downloads available updates in the background, and stages them to apply on restart. Checks never block startup and fail silently when you're offline.
  • In-app update banner — no OS notifications — A subtle, dismissible banner reports "Downloading update v…" with progress, then "Update v… ready" with a Restart button that relaunches straight into the new version. It matches PorkiCoder's existing in-app toasts and never blocks what you're doing.
  • Settings → version display + manual "Check for updates" — Settings now shows the running version and a Check for updates button with inline status — Checking… / You're up to date / Downloading… / Restart to update — for when you don't want to wait for the next scheduled check.
  • Notarized & stapled DMG for clean first installs — The macOS DMG is now notarized by Apple with the ticket stapled, so a fresh install opens without the Gatekeeper "unidentified developer" right-click-to-open workaround.
  • Terminal: mouse-wheel scrolling lands flush at the bottom — Fixed a long-standing bug where wheel-scrolling down in the terminal could stick one row short of the bottom, leaving the last line clipped until you pressed a key. Wheeling down now snaps cleanly to the true bottom; scrolling up through scrollback is untouched.

Version 1.10.2 (May 24, 2026)

Accurate Gemini Usage Accounting — Thinking Tokens Now Counted, Plus a Crash-Hardening Fix and Internal Cleanup

1.10.1 made Gemini 3.1 Pro batteries-included. 1.10.2 is a correctness follow-up. The headline fix: Gemini's "thinking" (reasoning) tokens are now counted in usage and cost. Google's API reports thoughtsTokenCount separately from the visible candidatesTokenCount, and both bill at the output rate — but PorkiCoder was only counting the visible tokens, so any thinking-enabled Gemini call (in-app chat and the consult_gemini tool) under-reported its real token usage, often by 5–10x on reasoning-heavy questions. Your Settings → Usage numbers and the combined Kimi + Gemini consult cap now reflect true spend.

  • Gemini thinking tokens folded into output for billing — Both the in-app Gemini chat path and the MCP consult_gemini tool now add thoughtsTokenCount to the output-token count used for cost. The Settings usage table's "Output" column already documented that it includes thinking/reasoning tokens — now it actually does for Gemini. A defensive canary also logs a warning if Google's prompt + candidates + thoughts = total token invariant ever shifts, so a future API change can't silently break billing again.
  • Main-process crash hardening (EPIPE) — Fixed an "Uncaught Exception: write EPIPE" that could surface if PorkiCoder's stdout/stderr was closed by a downstream reader (e.g. piping dev-mode logs through a tool that exits early). End users running the installed app were never affected — no pipe is involved — but the crash dialog was jarring for contributors. Two boot-time listeners now swallow EPIPE while still surfacing any other stdio error.
  • Internal cleanup — Removed 11 unused electronAPI bridge methods from the preload layer (dead convenience wrappers with zero call sites) and dropped four orphaned Postgres functions from the backend — two left over from the Kimi-only consult era (superseded by the combined-cap RPCs in 1.10.1) and two from an old request-limit subsystem (superseded by the signup trigger and inline updates). No behavior change; just less surface area to carry forward.

Version 1.10.1 (May 24, 2026)

Gemini 3.1 Pro Joins Kimi K2.6 On the House — Free Users Get $5/mo Included Across Both, Premium Bumps to $15/mo Combined

1.10.0 made consult_kimi batteries-included for Premium users — paste no Baseten key, just sign in and Kimi works. The other half of the consult duo, consult_gemini, still required a Google AI Studio key, and free users got a hard 402 the moment they tried the proxy. 1.10.1 finishes the story: Gemini 3.1 Pro now flows through PorkiCoder's own backend the same way Kimi does (no Google signup needed if you're signed into PorkiCoder), and the per-user monthly USD cap is now combined across both providers — $15/mo for Premium, $5/mo for Free. The free-tier gate that 1.10.0 left in place is gone; everyone gets a small included quota that falls back to BYOK when exhausted.

  • New backend route POST /api/consult/gemini — Mirrors the Kimi proxy shape (premium-aware cap, aggregate circuit breaker, rate limit), but adapted for Google's generativelanguage.googleapis.com API. Different upstream auth (URL ?key=… instead of an Authorization header), different body (systemInstruction + contents + generationConfig instead of OpenAI messages), different usage shape (usageMetadata with promptTokenCount / candidatesTokenCount / cachedContentTokenCount). Pricing mirrors the client-side mcp-consult/pricing.js tiered rates — ≤200k input tokens uses low tier ($2/MTok input, $12/MTok output), >200k uses high tier ($4 / $18) — a single call always picks one tier.
  • Per-user $15/mo (Premium) / $5/mo (Free) cap, combined across Kimi + Gemini — The old $15-Kimi-only cap is replaced, not stacked. A user spending $7 on Kimi consults + $4 on Gemini consults sees the next call to either provider hit the cap at $11/$15 — the math is summed across both model_key rollups via the new check_consult_quota RPC. Free users no longer get hard-stopped at the door; they get $5/mo of proxy access and fall back to BYOK after.
  • Shared rate-limit and aggregate-budget state across both proxies — Both routes now import their gate logic from a new src/routes/_consultShared.js module. The 20-req/min/user rate limiter and the $500/mo global circuit-breaker now count both consult proxies together — alternating between providers does not double your headroom.
  • MCP-consult providers/gemini.js ported to the hybrid proxy-first / BYOK-fallback pattern — Same shape providers/kimi.js shipped in 1.10.0. Reads GEMINI_PROXY_URL + PORKICODER_AUTH_TOKEN from the PTY env; on proxy 4xx (cap exhausted, premium required) transparently falls back to direct Google with the user's own GEMINI_API_KEY if one is configured in Settings. On proxy 5xx / network errors, same fallback so a backend hiccup doesn't take consult_gemini offline for BYOK users.
  • PTY env injection adds GEMINI_PROXY_URL alongside the existing KIMI_PROXY_URLsrc/main/index.js's terminal-spawn handler now sets both proxy URLs in the same if (accessToken) branch. One Supabase access token, two providers. Token TTL is still ~1 hour as before — a stale terminal session may need a fresh tab to re-authenticate, at which point both proxies activate again.
  • Settings → Usage table shows friendly names for consult rollups — Added "Kimi K2.6 (consult)" and "Gemini 3.1 Pro (consult)" labels in MODEL_DISPLAY_NAMES. Proxy usage now appears as a labeled row in the monthly usage table alongside main-chat models, instead of showing the raw kimi-consult-proxy / gemini-consult-proxy key.
  • Migration 0010 — combined-quota RPCscheck_consult_quota(user_id, cap_usd) and get_consult_aggregate_spend() widen the model_key filter from 'kimi-consult-proxy' (migration 0009) to IN ('kimi-consult-proxy', 'gemini-consult-proxy'). The old Kimi-only functions are deliberately left in place as rollback shims; a follow-up migration will drop them once 1.10.1 has shipped clean for a release.

Version 1.10.0 (May 23, 2026)

Kimi K2.6 Is On the House — $15/mo of Consults Included With PorkiCoder Premium, No Baseten Signup Needed

The story 1.8.1 → 1.9.8 was supposed to tell was "open PorkiCoder, run claude, Kimi just works." The story it actually told was "open PorkiCoder, run claude, hit a 'KIMI_API_KEY is not set' error, go figure out what Baseten is, sign up, generate a key, paste it into Settings, restart your terminal, then Kimi works." 1.10.0 collapses that whole chain. Premium users ($20/mo) now get $15 of Kimi K2.6 consults every month proxied through PorkiCoder's own Baseten account — zero account creation, zero key handling. The free-tier bring-your-own-Baseten path still works unchanged for users who'd rather pay Baseten directly; the new proxy is purely additive.

  • New backend route POST /api/consult/kimi — Verifies the Supabase JWT, checks user_request_limits.is_premium, atomically checks the per-user monthly USD cap (default $15) via the new check_kimi_proxy_quota RPC against the existing usage_monthly table, then forwards to Baseten using the server's KIMI_API_KEY. Response is the Baseten body verbatim so the MCP-consult provider's parsing is unchanged. Usage is incremented via the existing increment_usage_monthly RPC with model_key='kimi-consult-proxy' so per-model usage continues to roll up correctly.
  • Per-user $15/mo cap + per-minute rate limit + global circuit breaker — Cap enforcement is race-tolerant (overshoot bounded by rate limit × max cost-per-call, ≈$0.13). Rate limit is 20 req/min/user — protects against agent loops without throttling normal use. Global aggregate spend across all users gates on the KIMI_MONTHLY_BUDGET_USD env var (default $500 hard cap, cached 60s to avoid DB churn) — fail-safe in case any single user or batch of users tries to grief the proxy.
  • MCP-consult providers/kimi.js picks proxy vs direct path automatically — When the PTY env carries PORKICODER_AUTH_TOKEN + KIMI_PROXY_URL (set automatically by signing into PorkiCoder), the provider hits the proxy first. On 4xx from the proxy (premium gate failed, cap exhausted, rate limited) it transparently falls back to direct-Baseten if KIMI_API_KEY is also present — so users who pay for both never see an outage. On 5xx / network errors from the proxy, same fallback. When neither path is available, surfaces the proxy's helpful "upgrade or add a Baseten key" message instead of the bare "KIMI_API_KEY not set" error.
  • PTY env injection in src/main/index.js — Signed-in users get their current Supabase access_token forwarded to every spawned terminal as PORKICODER_AUTH_TOKEN, alongside KIMI_PROXY_URL=https://server.porkicoder.com/api/consult/kimi. Token TTL is ~1 hour (Supabase default) — a claude session opened in an old PTY may need a fresh terminal to re-authenticate once the token expires, at which point the provider falls back to direct-Baseten if you have a BYOK key.
  • New GET /api/consult/kimi/quota endpoint — Returns {premium, used_usd, cap_usd, allowed} for the calling user. Wires the data the Settings modal needs to surface a "$X of $15 remaining this month" row once that UI lands in a future release.
  • Migration 0009 — two read-only RPCs against the existing usage_monthly tablecheck_kimi_proxy_quota(user_id, cap_usd) returns the calling user's spend on model_key='kimi-consult-proxy' for the current UTC month. get_kimi_proxy_aggregate_spend() returns the SUM across all users for the global circuit breaker. No new table — reuses the existing rollup so the Settings-modal usage breakdown automatically picks up proxy usage as a new model row.

MCP Consult Trimmed to Kimi + Gemini — Claude/GPT/Grok Removed Because They Were Quietly Causing a Billing Surprise

1.9.8 forwarded ANTHROPIC_API_KEY into every PorkiCoder PTY on the theory that a consult_claude tool would exist. It didn't — only consult_kimi and consult_gemini were ever wired up. Worse, Claude Code silently switches from your OAuth / Max-subscription session to per-token API billing whenever it sees ANTHROPIC_API_KEY in the environment, which meant signed-in PorkiCoder users were unknowingly being billed by-token for Claude Code usage they thought their Max subscription covered. OPENAI_API_KEY and XAI_API_KEY were also being exported for tools that don't exist. 1.10.0 stops forwarding all three and removes the dead consult_grok and consult_gpt5 tool definitions + provider modules + pricing entries entirely.

  • consultKeyMap trimmed to kimi + gemini — Only the env vars that actually feed an enabled MCP tool are forwarded. Net effect: claude sessions launched in a PorkiCoder PTY no longer trigger the "ANTHROPIC_API_KEY detected, switching to API billing" warning.
  • Dead provider modules removedmcp-consult/providers/grok.js and mcp-consult/providers/openai.js deleted; their imports and tool definitions stripped from mcp-consult/index.js; their pricing tables stripped from mcp-consult/pricing.js. The codebase now reflects exactly what the server actually exposes.
  • New ProxyError error type — Lets the MCP-consult tools/consultTool.js error classifier surface the backend's user-facing message verbatim when the proxy explicitly rejects a call (premium-required, cap-exhausted, etc.). Previously these would have been lumped into the generic "Unexpected error" branch with a useless "see logs" remediation.

Version 1.9.8 (May 23, 2026)

MCP Consult Tools Light Up for Gemini, Grok, GPT-5, and Claude Too — Plus the Kimi Settings Field Catches Up With the Baseten Backend

1.8.1 promised "open PorkiCoder, run claude, every consult tool just works" — but for the past few releases that was only true for consult_kimi. The other four (consult_gemini, consult_grok, consult_gpt5, and the implicit Claude path) silently failed with "API_KEY is not set" because PorkiCoder was only forwarding the Kimi key into the terminal environment when it spawned claude. 1.9.8 closes that gap — every key you've already pasted into Settings now flows into every PTY, so every consult tool reaches its provider on first attempt. The same release renames the Settings field "Kimi (Moonshot) API Key" to "Kimi (Baseten) API Key" so the label finally matches the actual upstream after the Baseten swap landed in 1.9.6.

  • Every consult provider key reaches the MCP server, not just Kimisrc/main/index.js's terminal-spawn path used to inject only KIMI_API_KEY into the PTY env. It now iterates over five service→env-var pairs (kimiKIMI_API_KEY, geminiGEMINI_API_KEY, grokXAI_API_KEY, openaiOPENAI_API_KEY, claudeANTHROPIC_API_KEY) and forwards whichever ones the user has saved in PorkiCoder Settings. A failed lookup for any single key logs a warning but doesn't block the spawn — one missing key disables one consult tool, the rest still work. Net effect for users: consult_gemini, consult_grok, consult_gpt5 now answer on first call from inside a freshly-opened PorkiCoder terminal, same as consult_kimi already did.
  • Settings field renamed "Kimi (Moonshot)" → "Kimi (Baseten)" — Kimi K2.6 has been served via Baseten since 1.9.6, but the Settings label and placeholder text still said "Moonshot," and the error log in kimiService.js still printed "MOONSHOT_API_KEY was not provided to the service" — so anyone debugging from logs saw Moonshot and went hunting for a key that no longer maps to anything reachable. The label, placeholder, helper text, error log, and MCP tool description are all updated. One-time action for upgraders: open Settings, paste a Baseten key in the renamed "Kimi (Baseten) API Key" field — a previously-pasted Moonshot key will not authenticate against Baseten's inference endpoint and the consult tool will return a 403 until it's replaced.
  • Vendor label in MCP error text now says "Baseten" — The kimi provider's vendor constant used to be 'Moonshot', which surfaced as "Could not reach Moonshot" in MCP error remediation messages even though the actual failed request went to inference.baseten.co. Switched to 'Baseten' so the remediation text points users at the right vendor dashboard when they hit a key/quota/network issue.

Version 1.9.7 (May 23, 2026)

MCP Consult Swaps Gemini 3.5 Flash for Gemini 3.1 Pro — With a Thinking Knob You Can Dial From Fast-and-Cheap to Deep-and-Patient

1.9.6 added consult_gemini35flash to the MCP consult server so Claude Code could fetch a quick Google second opinion alongside Kimi. In practice the Flash variant was just too shallow for the kinds of design questions you actually want Gemini for — useful as a sanity check, rarely useful as a tiebreaker. 1.9.7 retires Flash from the consult surface entirely and exposes Gemini 3.1 Pro as consult_gemini, with a thinking argument (low / medium / high / max) so the calling agent picks the depth that matches the question — low by default for sub-second drive-bys, high/max when you want it to actually chew on the problem before answering.

  • consult_gemini now routes to Gemini 3.1 Pro with adjustable thinking — Same MCP transport, same calling convention from Claude Code, just a more capable model behind the wheel. Thinking defaults to low (≈token-budget equivalent of "minimal" reasoning) so the common "give me a quick second opinion on this snippet" case still returns in a second or two without burning budget. Bump it to high or max from the agent side when you want Gemini to actually reason about a multi-file refactor, a tricky algorithm, or a "which of these two approaches is less likely to bite me in six months" question — same tool, just deeper.
  • consult_gemini35flash removed from the consult tool surface — The Flash variant added in 1.9.6 is gone (provider file, pricing entry, tool registration) — one consult-Gemini tool, not two confusing siblings. If you had Claude Code subagents wired specifically to consult_gemini35flash, point them at consult_gemini instead and pass thinking: "low" to keep the same latency profile.
  • makeConsultTool factory gains extraInputProperties — Internal cleanup so per-tool schema extensions (like Gemini's thinking argument) don't bleed into every other consult tool's input schema. The Kimi consult tool stays clean, the Gemini one advertises the thinking knob — both share the same factory without surprising each other. No user-visible change unless you're writing your own consult provider against this codebase, in which case the extension point is now actually correct.

Version 1.9.6 (May 23, 2026)

Terminal Wheel Scroll Lands Flush at the Bottom — Plus Kimi Routes Through Baseten for Steady Connections and Tab Naming Returns to Kimi K2.6

Three releases since terminal scroll was last touched and the same intermittent symptom kept resurfacing: slow trackpad-scroll toward the bottom would stop one row short, the last line hidden until you pressed the down arrow. Two prior fixes patched the symptom (snap-to-bottom when within 10 px of the end) but never the cause — xterm's ybase × rowHeight bottom couldn't coincide with the browser's scrollHeight − clientHeight max because the row height (16.8 px at lineHeight: 1.2) didn't divide the viewport evenly. 1.9.6 sets lineHeight: 17/14 so the row is exactly 17 px, aligning xterm's grid with the browser's, and drives follow-state from terminal.onScroll(ydisp) instead of fighting viewport.scrollTop. Same release routes every in-app Kimi call (chat, agent, Cmd+K inline edit, terminal tab naming, MCP consult) through Baseten's OpenAI-compatible endpoint to recover the long-lived complex prompts that Moonshot direct had been dropping mid-request.

  • Terminal wheel scroll lands flush every time — Two prior attempts treated the symptom: 1.8.x called terminal.scrollToBottom() inside the scroll handler (no-op once xterm's ydisp rounded up to ybase), and 1.9.0 replaced it with viewport.scrollTop = scrollHeight − clientHeight guarded by a scrolledDown heuristic (lasted longer but trackpad inertia still settled outside the 10 px snap zone). 1.9.6 fixes the geometry at its source: lineHeight: 17/14 instead of 1.2 makes the rendered row exactly 17 px so xterm's ybase × rowHeight bottom equals the browser's scrollHeight − clientHeight max — they can finally agree. With the grid aligned, follow-state moves to xterm's authoritative onScroll(ydisp) + buffer.active.baseY signal instead of reading the DOM scrollbar, and the snap-back hack is deleted entirely. Scrollback navigation is unaffected; the "↓ New output" badge still appears when output arrives while you're scrolled up; alt-screen apps (vim, htop, less) keep working untouched.
  • Kimi via Baseten for chat, agent, Cmd+K, and MCP — Moonshot's direct endpoint was dropping long-running thinking-enabled requests mid-flight on bursty days — the client received a generic fetch failed with no HTTP response, and previously-successful 60–120 s prompts started returning "Could not reach Moonshot." Same Kimi K2.6 weights are served by Baseten (inference.baseten.co/v1/chat/completions, model moonshotai/Kimi-K2.6, auth scheme Api-Key) with infrastructure tuned for long-lived inference connections. Every internal call site swaps together so chat, the agent dispatch (Cmd+L), Cmd+K inline edit, terminal tab naming (now back on Kimi — see below), and the MCP consult-Kimi tool all hit Baseten through a single shared constant. Your existing "Kimi/Moonshot API key" setting field now expects a Baseten key — paste a new key from your Baseten dashboard once and everything routes through it.
  • Terminal tab naming returns to Kimi K2.6 (via Baseten, thinking-disabled) — 1.9.4 swapped naming from Kimi to Gemini Flash-Lite for cost and latency reasons. Two months in, the trade-off no longer holds: Baseten's serving infra lands Kimi K2.6 in sub-second territory with thinking disabled, the per-label budget is capped at 64 output tokens (sanitize-to-32-chars hard cap on top), and Kimi follows the "label the intent of the command, not the literal binary" guideline better than Flash-Lite for compound commands and agent prompts. BYOK takes precedence as before — if you have a Kimi (Baseten) key set, the client calls it directly. The subsidised proxy at server.porkicoder.com/api/terminal-name still serves Gemini for users without their own key (server-side migration is a separate ticket).
  • API keys can now come from environment variables in dev — Local-dev convenience: getApiKeyForService('kimi') now checks KIMI_API_KEY (and the matching SERVICE_API_KEY for any other provider) before falling back to the encrypted electron-store value. Pasting a key in Settings still works exactly as before; the env var just wins when present so a fresh npm start session can run without re-pasting after a store wipe. Production builds ignore this unless you explicitly launch with an env var set.
  • MCP consult gains Gemini 3.5 Flash + progress callbacks — The MCP consult server (used by Claude Code via stdio) adds consult_gemini35flash alongside consult_kimi for a low-latency Google second opinion, with pricing wired in for usage rollups. The shared consult tool factory also gains a sendProgress passthrough so providers can stream periodic "still thinking…" notifications back to the calling agent during long requests — currently used by the Kimi provider's in-progress timeout/retry work, infrastructure for future providers to opt into.

Version 1.9.5 (May 23, 2026)

Tab Naming Works Out of the Box on Fresh Installs — Backend Proxy Subsidises the Call When You Haven't Plugged In a Gemini Key, Plus macOS Fullscreen Finally Refits the Terminal and 16 Reliability Fixes Land

1.9.4 made terminal naming reliable for people who had already set up a Gemini key in Settings, but new downloaders who hadn't touched Settings yet got nothing — every tab stayed Terminal N because the local naming path needed a key it didn't have. 1.9.5 fixes that by routing keyless users through a small subsidised endpoint on porkicoder.com (Gemini 3.1 Flash-Lite, 200 labels/user/day cap) so the feature works the moment you sign in. Power users with their own key still call Gemini directly — BYOK wins when present, and now falls through to the proxy when the user's key fails (stale, revoked, transient outage). The same release fixes a long-standing UX bug where toggling macOS native fullscreen left the active terminal rendered at the pre-fullscreen width, fixes the popped-out detached terminal window's identical fullscreen miss, and lands 16 correctness bug fixes surfaced by a five-angle code review of the new naming and resize paths.

  • Subsidised backend proxy at server.porkicoder.com/api/terminal-name — Fresh installs without a Gemini key now route through a small Express endpoint on the porkicoder.com backend, authenticated with your existing Supabase session, that calls Gemini Flash-Lite server-side and returns the label. Per-user daily cap of 200 labels (more than enough for normal use — the trivial-command filter and one-shot-per-tab rule keep real usage well below) bounds infrastructure cost; the counter is held in-memory with an upper LRU bound so a high-cardinality day can't blow out container memory. Quota is reserved up-front so concurrent requests respect the cap, but released on every failure path (timeout, Gemini 5xx, empty label) so a flaky-network day can't soft-lock you out before you've actually got a single label. Fetch is locked to redirect: 'error' and refuses non-JSON 2xx bodies so a future CDN/edge interstitial fails loudly instead of masquerading as an empty-label model issue.
  • BYOK still wins, but falls through to the proxy when your key fails — If you've set your own Gemini key in Settings, the client calls Gemini directly first (no proxy round-trip, no quota consumption against your account) — that path is unchanged. New: when your direct call returns gemini-error or timeout (stale/revoked key, personal quota exhausted, Gemini blip), the client now silently retries via the subsidised proxy instead of leaving you stranded on Terminal N. Empty-label and trivial-command skips are still terminal (the proxy would skip those too — no point retrying). The BYOK path also no longer requires you to be signed in to Supabase: the auth gate moved inside suggestName so it only fires for the proxy path, restoring the contract that "BYOK takes precedence" actually means BYOK works standalone.
  • macOS native fullscreen finally refits the terminal — Toggling the green window button (or Cmd+Ctrl+F) used to leave the active terminal rendered at the pre-fullscreen width — text wrapped at the old narrow column with a huge black gap on the right, and new commands kept rendering narrow until you re-toggled. macOS native fullscreen animates over ~700 ms, the renderer's window-resize listener fired off intermediate sizes during the animation, and the 250 ms post-event settle could land mid-animation and miss the final size. The main process now explicitly forwards enter-full-screen/leave-full-screen over a new window:fullscreen-changed IPC channel, and the TerminalManager subscribes and triggers an authoritative refit on every terminal (active or not) after the animation completes. A long-settle pass at ~900 ms also catches the rare reduced-motion / Linux/Windows edge cases where the OS event doesn't fire — gated to the fullscreen path so it doesn't add a spurious third refit to every window-drag.
  • Popped-out detached terminal window gets the same fullscreen fix — Pop the terminal panel out to its own window (Cmd+Shift+D) and macOS-fullscreen that window — same bug used to reproduce there, because the fullscreen event forwarders were only wired on the main window. The detached BrowserWindow now also forwards enter-full-screen/leave-full-screen, and DetachedTerminalManager mirrors the main window's refit handler. Multi-panel detached views refit all panels, not just the active one.
  • Agent-launch deferral is now per-CLI and recognises positional prompts — The "defer naming until you've prompted the agent" path used to mis-treat any flag as one-shot (so claude --dangerously-skip-permissions got labelled "AI Assistant" instead of waiting for your real prompt), and conflated -m across CLIs (Claude uses -m for --model — session config — while aider uses -m for one-shot message). One-shot flags are now a per-agent Map<head, Set<flag>>: claude/gemini/codex get {-p, --print}, aider gets {-m, --message}. Positional-prompt invocations like gemini "summarize package.json" are now recognised as one-shot too via a quoted-string heuristic. Session-config flags (--dangerously-skip-permissions, --model, --continue, --resume) correctly defer to the next prompt instead of producing a useless label.
  • Tab naming re-arms when the first command was skipped, preserves your in-flight keystrokes, and handles app-keypad Enter — Pre-1.9.5, if your first command in a fresh tab was trivial (cd ~, ls, etc.) or hit a transient skip (no key, timeout, Gemini error), the controller latched and the tab stayed Terminal N for the rest of the session. The observer now re-arms when no label committed, so the next real command gets another shot. Re-arm preserves the _cmdLineBuf so chars you typed during the ~800 ms naming round-trip aren't silently discarded. The input observer also recognises SS3 keypad-Enter (\x1bOM) as a real Enter, so TUIs that enable application-keypad mode (Claude Code, possibly aider) don't drop the Enter event when you submit a prompt — fixes the "deferred-Claude tab never gets named" edge case.
  • Per-command IPC storm suppressed with a session-level cooldown cache — Combined with re-arm, a signed-out user or one with no Gemini key would previously send a fresh IPC + auth + network round-trip on every Enter they typed for the entire session. The client now caches a "naming is currently disabled" verdict for 60 s when a skip arrives with a session-level reason (no-session, no-gemini-key, quota, repeated gemini-error) — controller stays re-armed, but _handleFirstCommand short-circuits before the IPC during the cooldown. Recovers gracefully on the first command after the cooldown when auth/quota/Gemini becomes healthy.
  • Smaller correctness fixes from the review pass — Long-settle no longer runs on every window-drag (gated to the fullscreen OS event path); the fullscreen-event unsubscribe is stored on the manager and a double-init guard prevents stacked listeners; manual-rename mid-flight no longer waste a Gemini call (the pending label is now persisted to a short-lived slot so cancelling the rename restores it); proxy fetch sets redirect: 'error' so DNS/load-balancer 3xx changes don't silently surface as model failures.

Version 1.9.4 (May 21, 2026)

Terminal Tab Auto-Naming Switches to Gemini 3.1 Flash-Lite — Sub-Second Labels, Reliable Firing, and No More Stale Names From Past Sessions

1.9.2 shipped Kimi-powered terminal tab auto-naming and 1.9.3 patched the obvious bugs in it, but three problems surfaced once the feature was in regular use: naming fired only sometimes (the "sporadic" feel — type a prompt cleanly and it labelled, lift a finger to use a cursor key and it silently skipped), labels from yesterday's session reappeared on today's tabs and short-circuited any new observation, and Kimi K2.6 was overkill — frontier-tier reasoning for a job that needs 1–3 words. 1.9.4 fixes all three by swapping the engine to Gemini 3.1 Flash-Lite, relaxing the input observer so it stops poisoning itself on every escape sequence, and dropping auto-generated labels from persistent storage so fresh sessions stay fresh.

  • Gemini 3.1 Flash-Lite replaces Kimi K2.6 for terminal naming — Roughly 5× cheaper input/output ($0.25/$1.50 per MTok vs. $0.95/$4.00) and roughly 7× lower TTFT (~0.4s vs. ~2.8s median) for a labelling task that only ever produces 1–3 words. Frontier-tier intelligence was never the bottleneck here — TTFT was — so the swap is a clean win on both axes. Flash-Lite runs with minimal thinking enabled (thinkingLevel: 'minimal', includeThoughts: false) and a tight maxOutputTokens cap so worst-case spend is bounded. Cost rolls up under "Gemini 3.1 Flash Lite" in Settings → Usage as a separate line item from your chat/agent usage.
  • Input observer no longer poisons itself on cursor keys — The renderer's first-command observer used to mark the capture buffer "tainted" the instant any ANSI escape sequence appeared in your input — arrow keys, history recall, Tab completion, Home/End, function keys, you name it. Once tainted, the next Enter reset the buffer silently and waited for the next "clean" cycle, where you typed an entire command without ever moving the cursor. With long prompts to Claude Code or any natural-language input where you fix a typo mid-sentence, that "clean" cycle rarely happened — naming would skip cycle after cycle until you got lucky. The observer now just consumes escape sequences (so embedded \r/\n in OSC/DCS payloads don't fake an Enter) without poisoning the buffer. Trade-off: a recalled-from-history command produces a label based on what you typed, not what the shell actually substituted — but for a 1–3 word tab label the gist is good enough. Empty buffer at Enter still skips fire, so a bare <up-arrow><enter> history recall produces no spurious label.
  • Auto-generated labels stop persisting to electron-store — Pre-1.9.4 every AI-generated label was written to the terminalTabNames preference and reloaded on next launch, which had a nasty side effect: yesterday's "Login Bug" tab came back named "Login Bug" today, and the observer's skipFirstCommandObservation() short-circuit treated it as an "already named" tab — so you could never get a fresh label even if today's tab was for something completely different. 1.9.4 introduces a new preference key terminalTabNamesManual that only stores names you produced explicitly: double-click renames and presets (the "claude" session button). Auto-labels live in memory for the duration of the app run and quietly vanish on quit. The old terminalTabNames key is orphaned in the store — your existing stale entries don't get migrated, they just stop being read.
  • Pricing + display name wired so usage rollup worksmodelPricing.js gained a GEMINI_FLASH_LITE_RATES record (verified against ai.google.dev/gemini-api/docs/pricing on release day) and a 'gemini-flash-lite' key in the MODEL_PRICING map. modelDisplayNames.js gained the matching 'Gemini 3.1 Flash Lite' display entry. geminiService.js gained Flash-Lite as a third model variant alongside 'pro' and 'flash' via a new resolveGeminiModelId() helper, with its own default thinking level ('minimal') and the same "no temperature/topP/topK" generation-config restriction the Flash family uses. Existing Gemini call sites (orchestrator chat path, Cmd+K inline edit) are untouched — they still resolve to 'pro' or 'flash' as before.
  • Per-request maxOutputTokens cap honouredgeminiService.ask/askStream previously hard-coded maxOutputTokens: 65535. For terminal naming that's ~2000× the budget actually needed for a 1–3 word reply. The service now reads options.maxOutputTokens and falls back to the 65k default only when the caller doesn't specify, letting naming cap itself at 128 (enough headroom for the minimal-thinking budget plus the actual label).

Version 1.9.3 (May 20, 2026)

Cmd+Q Just Works Again — Confirm Dialog Fires Only When a Terminal Script Is Actually Running, Standard Quit Otherwise

An earlier release hijacked the red traffic light so closing the window would hide PorkiCoder instead of killing your long-running scripts — useful — but the same intercept was applied to Cmd+Q, the dock Quit item, and the menu's Quit entry. Hitting any of them just hid the window. The only buried escape hatch was a Shift+Cmd+Q "Quit & terminate scripts" menu item that nobody discovered — so users ended up force-quitting through Activity Monitor instead. 1.9.3 restores standard macOS Cmd+Q semantics while preserving the data-loss protection that mattered: the red traffic light still hides, but every quit path now either quits cleanly or surfaces a native confirm dialog when (and only when) there's something to lose.

  • Idle shells don't trigger the dialog — only PTYs running an actual child process do — PorkiCoder opens a default terminal tab at startup, so a naive "any PTY alive → prompt" rule would confirm on every quit and train users to dismiss it reflexively. The new check runs pgrep -P <pty.pid> against every live PTY: zero children means the shell is sitting at a prompt, so the quit is silent. A non-empty result means a foreground command is running and the dialog appears, listing the command names by terminal ("Terminal 2: npm, node — Quitting will terminate them") so you know exactly what's at risk before you confirm.
  • Native macOS confirm dialog with Cancel as default — When scripts are running, dialog.showMessageBox shows a system-modal warning with Cancel (default, also bound to Esc) and Quit. Cancel preserves whatever state the window was in (visible or hidden), no side effects. Quit sets the internal real-quit flag and re-fires app.quit(), which proceeds through the existing teardown path — PTYs killed, in-flight LLM streams aborted, project watcher closed, detached terminal window destroyed.
  • Red traffic light still hides the window — original protection preserved — The mainWindow.on('close') handler is unchanged: clicking the red dot on macOS still calls preventDefault and hides the window with the existing safe-fullscreen exit logic. PTYs and any detached terminal window stay alive; the dock icon brings the window back. Only the quit paths (Cmd+Q, dock-quit, menu-quit) route through the new confirm flow.
  • Dropped the buried Shift+Cmd+Q menu item — The "Quit & terminate scripts" entry was a workaround for the self-inflicted Cmd+Q hijack. With Cmd+Q working correctly again, it served no purpose and was actively misleading (suggested the standard Cmd+Q wouldn't terminate scripts). The application menu is back to a standard macOS app submenu: About, Services, Hide / Hide Others / Show All, Quit.
  • Re-entrancy guarded — rapid Cmd+Q presses can't stack dialogs — Holding Cmd+Q or hitting it from multiple sources in quick succession used to be moot when the handler just hid the window, but with an async dialog in the path a naive implementation would queue duplicate prompts. A quitConfirmInProgress flag short-circuits subsequent calls until the first dialog resolves, so you get exactly one confirm prompt regardless of how many quit signals arrive.

Version 1.9.2 (May 20, 2026)

Terminal Tabs Auto-Name Themselves from Your First Real Command — Kimi K2.6 Picks a 1–3 Word Label in About a Second

Open a new terminal tab and run your first non-trivial command — npm run build, pytest -k login, ssh prod-web-01, tail -f logs/app.log — and a moment later the tab title changes from a generic Terminal 1 to something useful: "Build App", "Run Tests", "SSH Prod", "Tail Logs". Kimi K2.6 with thinking disabled picks the label in roughly a second, the IPC round-trip is fire-and-forget, and there is exactly one naming call per tab — subsequent commands don't re-rename. Trivial heads (cd, ls, pwd, clear, exit, history, echo) skip the call entirely; so do tabs you've already renamed manually or that came pre-named (the Claude Code session preset). No setting to enable, no model to pick — it's on as long as you have a Kimi key in Settings, and silently skipped otherwise. The feature shipped with a 12-bug self-audit pass folded in — every issue surfaced during the review is FIXED in this release.

  • Compound commands count, not just the first word — A naive deny-list on the head token would skip ls -la | grep secret > out.txt, cd ~/proj && npm test, cat foo.log | jq . from naming because the first word is in the trivial list. The classifier now checks for shell operators (| < > ; &) before the head-word lookup — anything that pipes, redirects, chains, or backgrounds is treated as non-trivial regardless of how it starts. cd alone still skips; cd ~/proj && npm test gets named "Run Tests".
  • Manual rename always wins, even mid-flight — Double-click a tab to rename while the Kimi call is in flight and the AI label is dropped on the floor when it returns. The persisted-name write is gated by the same renaming-in-progress check as the visible-title write, so the cancel-rename path can't leave terminalTabNames in storage diverged from what the tab actually shows — there is no scenario where the AI name "resurrects" on next launch because you cancelled the rename you started while Kimi was thinking.
  • Detached terminal windows get auto-named too — Pop the terminal panel out into its own window (Cmd+Shift+D), run your first command, and the tab in the detached window picks up an AI label via a new IPC channel to the main process. When you reattach, the auto-named state migrates back to the main window's tab strip — the in-memory copy of terminalTabNames is refreshed from preference on reattach so Kimi writes from the detached session aren't lost.
  • Cost surfaces in Settings → Usage like every other AI call — Terminal-naming hits the same saveChatInteraction + increment_usage_monthly path that Cmd+K inline edits use, so per-tab labelling cost (input tokens for the system+user prompt, output tokens for the 1–3 word reply) rolls up under "Kimi K2.6 (Min Thinking)" in the Usage panel. A misunderstanding model that decides to write an essay can't burn budget — max_tokens is now caller-configurable per request, and naming caps itself at the smallest value that still lets the model finish a 3-word label, instead of the 32K-token default the chat path uses.
  • 10-second timeout, AbortController-clean shutdown — A hung Kimi call no longer holds the per-tab in-flight slot indefinitely. AbortController aborts the fetch after 10 seconds; on app quit, the controller tears down the in-flight request cleanly instead of leaving the fetch dangling on Electron exit. Other tabs are unaffected — the in-flight lock is keyed per terminal id.
  • Error path distinguished by structure, not by string-matching the reply — Earlier in development the error-detection guard was a regex that flagged any reply starting with "Error " as a failure to swallow. That false-positive-d legitimate labels like "Error Boundary", "Error Handler", "Error Logs" — all plausible names for a developer terminal — and silently kept the default Terminal N. The guard is now if (!result?.usage): the Kimi adapter only populates the usage field on a 200 response with a parsed body, so its absence is the unambiguous error signal and legitimate "Error …" replies pass through. Net effect: any tab whose first command actually relates to error handling now gets a sensible label instead of staying generic.
  • Tighter ANSI-escape skipping in the input observer — The renderer's first-command observer ignores arrow keys, history search, and other interactive shell traffic by recognising ANSI escape sequences. The previous parser only fully consumed CSI (ESC [) and SS3 (ESC O) with a narrow class of parameter bytes; sequences with intermediate bytes, OSC, DCS, or SOS/PM/APC could leak printable garbage into the buffer. Any \x1b now taints the buffer for the rest of the line and the Enter event is skipped — naming never fires on a Ctrl-R search string, an arrow-key edit, or an escape-laden paste. A 4096-char cap caps buffer growth from a malicious paste; the Kimi key is never accidentally exfiltrated via a runaway label.

Version 1.9.1 (May 19, 2026)

Terminal Wheel Scroll Lands Flush at the Bottom — No More Down-Arrow Rescue

Slow trackpad scrolling toward the bottom of the terminal sometimes left the viewport 1–9 px short of true bottom — the last row was partially cut off and only a tap of the down-arrow key snapped it flush. 1.7.4 took a swing at this by calling terminal.scrollToBottom() from the scroll handler whenever the viewport entered the 10 px "at bottom" zone, but the swing missed. xterm.js's wheel path rounds scrollTop / rowHeight to compute the new display row, and for any gap smaller than rowHeight / 2 (~8 px at the default font) that round produces ydisp === ybase before our handler runs. scrollToBottom() then resolves to scrollLines(0), which short-circuits inside BufferService without ever touching viewport.scrollTop — so the snap was silently a no-op for the lower half of the gap. 1.9.1 swaps in a direct viewport.scrollTop = scrollHeight - clientHeight assignment, which is the same primitive xterm's own _innerRefresh uses after PTY data arrives (and is incidentally what the down-arrow keystroke was triggering as a side effect).

  • Direction-guarded snap — small wheel-UP from the bottom no longer yanks you back — Setting scrollTop directly is more forceful than the old no-op, so without a guard a 5-pixel upward flick from a flush-bottom state would land at distance = 5, fire the snap, and immediately return to bottom — making it feel like the wheel was scrolling into a wall. The handler now tracks the previous scrollTop in a closure variable and only snaps when the current value is greater than the last — i.e., when the user is actively heading downward. Wheel-up from bottom proceeds freely; wheel-down to bottom snaps flush.
  • Works on macOS trackpad inertia and slow mouse-wheel finishes alike — Two scroll patterns reliably reproduced the residual gap before: a slow trackpad glide where the tail of inertia emitted sub-pixel deltaY values rounded down by the browser, and a slow mouse-wheel finish where the final tick advanced scrollTop by less than the canvas row height. Both now land flush. Fast flicks and shift+wheel (the fast-scroll modifier) were never affected and remain unchanged.
  • Detached terminal windows and multi-tab terminals both get the fix — The scroll-listener attachment runs the same code path during initialize() and attachToExisting(), so the detached-terminal window inherits the new behaviour, and each tab in a multi-tab terminal has its own controller with its own lastScrollTop closure — no cross-tab interference.

Version 1.9.0 (May 19, 2026)

Gemini Flash Is Now Gemini 3.5 Flash — Better Reasoning at the Same Sub-Second Latency

Google made gemini-3.5-flash generally available on May 19, 2026, and PorkiCoder picks it up in this release. The model retains Flash's defining trait — sub-second first-token latency that makes Cmd+K inline edits feel instant — while delivering noticeably stronger coding, tool use, and long-horizon reasoning than the 3 Flash Preview it replaces. Everywhere "Gemini Flash" appears in PorkiCoder — chat dropdown, agent-mode model picker, Cmd+K fallback, and the apply-code semantic-diff fixer — you're now talking to the 3.5 GA model. No setting to flip, no migration, no flag.

  • Drop-in across every Gemini Flash call site — Four code paths route to Flash: regular chat (geminiService), agent mode tool-use loop (geminiAgentOrchestrator), Cmd+K inline-edit fallback when no Moonshot key is configured (inlineEditService), and the apply-code semantic-diff LLM fallback (applyModelService). All four now hit gemini-3.5-flash directly. The internal gemini-flash model key is unchanged, so per-mode model preferences saved in 1.8.x carry forward without resetting.
  • Sampling knobs dropped per Google's 3.x migration guidetemperature, topP, and topK are no longer sent on Flash requests. Google's guidance for the Gemini 3.x family is that reasoning is tuned for the built-in defaults and these knobs make things worse, not better. Pro (still 3.1 Pro Preview) keeps them. The conditional is on the resolved model id, not the variant string, so future Pro upgrades that join the no-knobs club need only a flag flip.
  • Effort bar unchanged — still Min / Low / Med / High — Gemini 3.5 Flash exposes the same four thinking levels as 3 Flash Preview did, with the same default of medium. The Min / Low / Med / High effort selector in the chat header keeps working exactly as before; Min stays the right pick for Cmd+K inline edits where first-token latency dominates and Max remains hidden because Flash never supported it.
  • Pricing updated to 3.5 Flash's $1.50 / $9.00 / $0.15 per MTok — A roughly 3× bump on both input and output relative to 3 Flash Preview ($0.50 / $3.00), with cached input at $0.15. The Settings → Usage panel reflects the new rates immediately for any Gemini Flash usage after upgrading; historical 1.8.x rows continue to display at the rates that were in effect when they were billed.
  • Display name reads "Gemini 3.5 Flash" everywhere — Chat header dropdown, agent-mode selector, chat-history sidebar, and every per-call usage row in Settings now say "Gemini 3.5 Flash" (with the same Min / Low / Med / High suffix variants). The chat-history fallback map was updated alongside the canonical display-name table so older rehydrated rows render with the new label too.

Version 1.8.1 (May 19, 2026)

MCP Consult Tools Now Install Themselves — Zero Setup, Just Launch PorkiCoder and Talk to Kimi from Claude Code

1.8.0 shipped consult_kimi / consult_grok / consult_gemini / consult_gpt5 as an MCP server, but actually using them required two manual steps most users never made it through: running claude mcp add porkicoder-consult … in the right shell with the right path, and remembering to export KIMI_API_KEY=… in every terminal that hosted a Claude Code session. 1.8.1 closes both gaps. Open PorkiCoder, open a terminal, run claude — Claude already sees the consult tools and already has your Kimi key. Nothing to configure.

  • Auto-registration into ~/.claude.json on every launch — On app.whenReady PorkiCoder writes a porkicoder-consult entry under mcpServers pointing at the bundled mcp-consult/index.js with ELECTRON_RUN_AS_NODE=1 set so the same Electron binary runs the MCP server as Node 20. Subsequent launches no-op when the entry already matches; dev↔packaged switches and node_modules reinstalls rewrite the entry to the current binary so claude mcp list never reports a stale path.
  • KIMI_API_KEY injected into every PorkiCoder terminal — The terminal:create handler reads the encrypted key from safeStorage and threads it into the spawned PTY's environment. Two-hop env flow does the rest: terminal env → Claude Code CLI inherits it → MCP server subprocess spawned by Claude Code inherits it too. Set the key once in Settings, never touch a shell rc-file again. The other three providers (Grok / Gemini / GPT-5) still need their env vars set manually until a future release ports them.
  • Concurrency-safe writes to ~/.claude.json — The Claude Code CLI rewrites the same file after every turn, without a lock. A naive read-modify-rename would silently clobber a CLI write that landed between our read and our rename — losing whatever turn the CLI just persisted. The new flow snapshots the file's inode+mtime+size at read time from a single fd, re-stats just before the atomic rename, and retries up to three times on mismatch. Per-pid .tmp filenames mean two simultaneous PorkiCoder launches can't truncate each other's in-progress writes either.
  • Non-destructive on every malformed-config path — If ~/.claude.json is unparseable JSON, has mcpServers set to an array or string, or is anything other than a plain object, PorkiCoder now logs and skips the registration instead of rewriting the file with a default-empty {mcpServers: {}}. Any custom hand-edits or third-party MCP entries in unusual shapes are preserved untouched.
  • Indent + trailing-newline preserved when rewriting — The writer sniffs the existing file's indent (tab vs. 2-space vs. 4-space) and trailing-newline style and round-trips whatever it found, so updating our entry doesn't produce a noisy whitespace diff against whatever format the CLI's own writer uses.

Kimi Consults No Longer Return Empty Answers

The MCP consult tool capped Kimi's output at max_tokens: 4000. Kimi K2.6 counts thinking tokens against that budget — so a question that triggered a few thousand tokens of reasoning would burn the entire budget reasoning and emit zero visible content. The tool surfaced only the cost footer (— Kimi K2.6 (… in, 4000 out, $…)) with an empty answer above it, leaving Claude Code with nothing actionable. Bumped to 32768 — Moonshot's documented default and the highest doc-blessed cap — so even thinking-heavy questions have ample room to both reason and answer.

API Keys Survive Transient Decrypt Failures

Two key-lookup paths (chat orchestrator and Cmd+K inline-edit service) used to store.delete() the encrypted key whenever safeStorage.decryptString threw — on the theory that an undecryptable key was a corrupted key. That's wrong in the common cases: a Keychain reissue, a login migration, copying the store across machines, or running PorkiCoder under a different macOS user account all produce decrypt failures with a perfectly intact ciphertext. The new behaviour is to log the failure, return null, and leave the stored value in place — the user can re-enter the key in Settings to overwrite it whenever convenient, instead of silently re-entering it from scratch after every machine migration. Bug surfaced because 1.8.0's KIMI_API_KEY injection now runs that decrypt on every terminal-create.

Smaller fixes

  • Decrypted Kimi key cached in-process — Per-terminal-create file I/O + decrypt is now a single hit, invalidated when the user saves a new key in Settings. Negligible for occasional terminal opens; matters if a future flow spawns terminals in a loop (multi-tab restore, test harnesses).
  • Dev-mode process.execPath staleness diagnosable from logs — When a registered entry points at an electron binary that no longer exists on disk (after a node_modules wipe or an Electron version bump), startup logs the prior path and the new one, so claude mcp list's "failed to start" line is no longer mysterious.
  • Registration completion now logged with a restart hint — Because the read-write happens fire-and-forget from app.whenReady, a terminal opened in the first few hundred milliseconds may launch a claude session before the entry lands. The post-write log now explicitly warns that any in-flight session won't see the entry until restarted — so the gap is diagnosable rather than mysterious.
  • Disabled provider blocks switched from /* */ to if (false) { … } — The three commented-out provider registrations in mcp-consult/index.js are now syntax-checked by node --check instead of hidden behind comment delimiters, so a typo while the block is "off" still trips the syntax checker.
  • Bundled mcp-consult/.git/** excluded from packaged builds — Forward-looking: if a future restructure makes mcp-consult/ a vendored repo or submodule, its .git/ won't bloat the DMG.

Version 1.8.0 (May 19, 2026)

Cmd+K Inline Edit — Select Code, Describe the Change, Accept the Diff

Select a block of code in the editor, press Cmd+K, and a floating prompt anchors itself to the selection. Type the change you want — "convert this to async/await", "extract the validation into a helper", "add JSDoc" — hit Enter, and the replacement streams in in place from Kimi K2.6. The selection greys out while tokens land character-by-character; Esc mid-stream restores the original; Cmd+Z after the edit completes reverts the whole thing in one keystroke. Same flow that makes Cursor's Cmd+K sticky, now in PorkiCoder — and intentionally on a different model from your chat dropdown so latency stays sub-second on the first token.

  • Streaming live-replace via Kimi K2.6 with thinking off — Inline-edit needs first-token latency on the order of a second, not the multi-second budget of a thinking model. The Kimi adapter gained an SSE streaming path (OpenAI-compatible delta format), and inline edits route to a dedicated kimi-fast variant that forces thinking: 'disabled' without disturbing the chat-mode Kimi which keeps thinking on. The chat selector and Cmd+K's model are independent — picking Opus in the dropdown does not slow down your inline edits.
  • Fallback chain when no Moonshot key is configured — If you only have a Gemini key, Cmd+K silently falls back to Gemini Flash at minimal thinking. If you have neither, you get a one-time toast linking to Settings rather than a silent no-op. The fallback explicitly does not pick up whichever chat model is selected — Codex / Opus / Sonnet / Grok are all wrong for this latency profile and the feature would feel sluggish on them.
  • Two review modes — live replace and diff modal — Default is live replace: chunks land directly in the buffer as a single merged undo unit. For larger edits, switch Settings → Cmd+K Inline Edit → "Diff modal" and completions open in the same Monaco side-by-side diff editor used by the apply-code flow, with explicit Accept / Reject. There is also a size cap (default 80 lines) that auto-switches to diff modal when you select a big block, so live-replace never nukes a wall of code invisibly.
  • Settings → Cmd+K Inline Edit — A new section in the Settings modal lets you pick the default inline-edit model (Kimi K2.6 / Gemini Flash / Claude Haiku 4.5 / Claude Sonnet 4.6), choose live vs diff review mode, and set the live-replace line cap. The model picker also lives inside the Cmd+K overlay itself as a small badge — click to override the default per-edit.
  • Cost rolls up alongside chat costs in the monthly usage view — Every Cmd+K invocation reuses the same saveChatInteraction persistence path as chat messages, so the token counts and USD are visible in Settings → Usage this month under the resolved inline-edit model name. No separate tab, no hidden spend.
  • Sanity-strip on model output — Even with a strict "no preamble, no fences" system prompt, models occasionally wrap output in markdown code fences. The renderer post-strips them before applying — if Kimi drifts and returns ```javascript ... ``` the inserted code is still clean.

MCP Consult Tools — Claude Code Can Ask Kimi, Grok, Gemini, or GPT-5 for a Second Opinion Mid-Task

A new standalone MCP server ships under mcp-consult/ in the porkr1 repo. Wire it into Claude Code with one claude mcp add command and Claude gets four new tools — consult_kimi, consult_grok, consult_gemini, consult_gpt5. When Claude is uncertain, has burned three turns on the same debugging path, or wants a perspective from a different model lineage, it calls the tool, gets the other model's answer back as a tool result with a cost footer, and folds that perspective into its next turn. No UI, no copy-paste, no user intervention — and you watch the exchange happen in the Claude Code TUI's normal tool-result rendering.

  • One tool per provider, with descriptions tuned for autonomous use — Each tool description gives Claude both a vibe ("Kimi is fast and unbiased by your training", "Grok has live web access and tends toward direct, less-hedged reasoning", "Gemini's 1M-token window excels at long-context", "GPT-5 is strong on algorithmic correctness") and explicit trigger conditions. Claude picks the right one based on the question — you don't have to instruct it.
  • Standalone — works without PorkiCoder running — The MCP server is a separate Node ESM project. It does not depend on the Electron app being open, and it does not import any PorkiCoder code. Install it on any machine you run Claude Code on. Future-proof — could ship as @porkicoder/mcp-consult on npm later for users who don't even use PorkiCoder.
  • Tool results include a cost footer — Every successful consult returns the answer plus a one-line tail: — Kimi K2.6 (834 in, 312 out, $0.0021). Claude can reason about cost when deciding whether to consult again on a follow-up, and you can spot-check spend at a glance without opening any dashboard.
  • Usage logged to ~/.porkicoder-mcp/usage.jsonl — One JSON line per call: timestamp, tool, model, tokens, USD cost, duration, and a 120-char preview of the question (full content is never persisted). tail -f the file during a Claude Code session to see exactly when Claude reaches for a second opinion. The PorkiCoder Settings → Usage panel will surface this alongside chat costs in a future release.
  • API keys via environment variables — missing keys silently disable just that one tool — Set KIMI_API_KEY, GROK_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY in the shell that launches Claude Code. If you only have a Kimi key, the other three tools return a helpful "set the X env var" message when Claude tries to call them, while consult_kimi works fine. No all-or-nothing wiring.
  • Clean stdio — no log pollution of the JSON-RPC stream — All diagnostic logging routes through stderr or the usage-log file. Stdout carries only well-formed MCP JSON-RPC frames, so the Claude Code transport never sees a malformed message.

Small fixes

  • Scrollbar-corner white square is gone — Wherever horizontal and vertical scrollbars met (most visible in ChatView), WebKit was rendering its default opaque corner instead of the dark themed scrollbar track. A global ::-webkit-scrollbar-corner rule now matches the rest of the scrollbar theming.

Version 1.7.4 (May 18, 2026)

Terminal Text No Longer Tears Mid-Word During Resize

When the terminal resized — window drag, font-size change, pane-split, cold start while the custom font was still loading — running tools that draw with absolute cursor positioning (Claude CLI being the most visible offender) could emit a stream of bytes that arrived in xterm at the new column count while the PTY was still echoing against the old one. The result was words sliced apart and stitched back together at the wrong column — em<unrelated>bellish-style garbling that looked like a corrupted stream but was actually a tiny ordering bug between two perfectly healthy components. This release fixes the ordering and several smaller contributors in the same call path.

  • Resize is now PTY-first instead of xterm-first_safeFit() was rewritten to be async: it computes the target geometry via proposeDimensions() without applying, awaits the IPC that delivers SIGWINCH to the child process while xterm is still at the old size, and only then calls terminal.resize(cols, rows). The child's first byte at the new width now lands in an xterm that already agrees on the new width — no more cross-talk between the two sides during the transition.
  • Concurrent resizes can no longer race — A monotonic _fitSeq counter is captured before the await and re-checked after; if a newer fit arrived during the IPC, the older fit drops its xterm-apply rather than stomping the newer geometry. So rapid window drags, font-size flurries, and pane-split + drag combos converge to the latest size cleanly instead of oscillating.
  • Post-spawn and restart paths now await the same flow_performInit and _performRestart previously kicked off a fire-and-forget _pushPtySize() after spawning; that helper was deleted entirely, and both paths now await this._safeFit() so the very first byte from the child is sized correctly. The old optimistic _lastPushed set-before-IPC-resolves trap is also gone.
  • Cold-start font drift no longer mis-sizes the terminal — When the renderer mounts before the configured terminal font has finished loading, xterm measures cell width against the fallback font; once the real font lands, every column is suddenly off by a fraction of a pixel and existing output sits a column too far left or right. A document.fonts.ready safety net was added to _performInit that invalidates the size cache and re-fits through the new PTY-first flow once the configured font is actually available — so the first paint and every paint after it line up.
  • Font-size changes preserve scroll position correctly through the new flow_setFontSize used to snapshot wasFollowing / scrollTop, then call the old sync fit, which applied xterm-side before the PTY had updated. The snapshot is now threaded into _safeFit({...}) so the restore runs after xterm.resize in the new ordering, keeping "stick to bottom" and mid-buffer reading position intact across font changes.

Version 1.7.3 (May 3, 2026)

Cmd+Q and the Red-X No Longer Kill Your Running Scripts on macOS

The 1.7.1 / 1.7.2 fixes closed the detached terminal window's red-X path, but the same scripts could still die from the main IDE's quit paths — Cmd+Q, the dock's Quit menu, the app menu's Quit, and (the symptom that named this release) the main IDE's red-X while in macOS native fullscreen. All of those routed through before-quit, which killed every PTY unconditionally before the close interception could even run. This release closes the entire family of paths in one pass.

  • Cmd+Q, dock→Quit, and app menu→Quit are now intercepted on macOS — they hide the window instead of murdering your scripts — Previously, before-quit killed every PTY in the map before any window's close handler could preventDefault(), so the user saw "I pressed Cmd+Q, my long-running build died, and the IDE didn't even close — what?". before-quit now preventDefault()s on macOS unless an explicit "real quit" path is in flight, and hides the main window instead. PTYs keep running in the background, ready for you to bring the IDE back via the dock.
  • New "Quit & terminate scripts" menu item (Shift+Cmd+Q) for when you actually want to fully quit — Since plain Cmd+Q now hides instead of quitting, there's a new explicit menu item that sets the real-quit flag and tears down PTYs the way the old before-quit did. Use it when you genuinely want everything dead. The standard Cmd+Q stays bound to the OS-conventional "close window, keep app in dock" behaviour.
  • Red-X on the main IDE while in macOS native fullscreen no longer strands you on an empty Space — macOS native fullscreen runs each window in its own Space; calling .hide() on a fullscreen window leaves that Space visible (dark, empty, just the menubar) because the exit-fullscreen animation hasn't run. The hide path now exits fullscreen first (via setFullScreen(false)), waits for leave-full-screen, and only then calls .hide() — so the IDE properly returns to the dock instead of leaving you stuck staring at an empty desktop.
  • Clicking the dock icon now reliably brings the IDE back — After a hide, the activate handler used to be a no-op because the window still existed (just hidden), so the dock click went nowhere. Users gave up and force-quit, which routed back through before-quit and killed the very PTYs we just spent the close handshake protecting. The handler now explicitly .show()s the hidden main window when you click the dock icon, closing the loop.
  • PTY routing during reattach now points back at the main window immediately — During detached→main reattach, there was a small window where PTY data could arrive while the main renderer was still wiring up — and because terminalWindowMap still pointed at the (about-to-die) detached window, that data was silently dropped. Routing is now flipped to mainWindow at the start of the reattach handshake, so no bytes get lost in transit.
  • Linux/Windows still cleanly close-and-kill on red-X — The hide-on-close behaviour is macOS-only (it matches macOS's window-vs-app convention). On Linux and Windows, closing the main window remains a real close — and the closed handler now correctly tears down PTYs owned by the main window while leaving any detached-window PTYs alive, so non-macOS users get the cleanup they expect without losing detached-window state.

Version 1.7.2 (May 3, 2026)

Detached Terminal — Close Handshake Hardened on macOS

A follow-up to the 1.7.1 detached-terminal fix. The reattach-on-close path was still vulnerable to a residual race on macOS where a second close signal could arrive mid-handshake and tear the window down before the renderer finished moving PTYs back to the main IDE — which, in the worst case, orphaned the PTYs and killed the running scripts. This release closes that window.

  • Every close signal during reattach is now blocked, not just the second one — Previously the close handler used if (winRef._reattaching) return, which let any close event after the first one fall through to the default and destroy the window. macOS will sometimes deliver a second close signal before the async reattach handshake finishes (closing via the menu while the red-X is also processed; back-to-back keyboard shortcuts; some accessibility paths). The handler now event.preventDefault()s every close while _reattaching is set, so the destruction can only happen on the explicit code path that owns the timing.
  • Window destruction switched from close() to destroy() after the handshake — Calling winRef.close() at the end of the reattach re-fired the same close event handler we'd just spent the whole function preparing to exit cleanly — a self-recursive landmine. Replaced with winRef.destroy(), which tears down the window without re-triggering the close listener.
  • Reattach failures no longer leave a stranded window — Added a .catch() on the reattach promise. If the buffer-serialize, IPC send, or restore-and-focus step throws, we now still clear the detachedTerminalWindow reference and destroy the window instead of leaving a half-dead popup with no listeners attached.

Version 1.7.1 (May 3, 2026)

Detached Terminal — Stray Red-X No Longer Kills Running Scripts

A focused fix on the popped-out terminal window. Clicking the macOS red traffic light on the detached terminal used to interrupt anything actively running inside it — long scripts, in-progress builds, agent shells. The intent was always "reattach to the IDE," but the implementation lost the handshake under multiple race conditions. Now the close button reliably brings your terminals — and whatever is running in them — back into the main IDE window with no operation interrupted.

  • Red-X on the detached terminal window now reattaches instead of killing — The close handler was rewritten to perform the reattach handshake before the renderer is torn down: PTY output is transit-buffered immediately, the main IDE is brought to front (restored from minimize, focused), the visible scrollback is captured, and only after terminal:reattached is sent does the detached window actually destroy. Previously, the buffer-serialize ran first and the reattach signal fired only after the renderer was already dead — leaving a window where PTY data going to a destroyed webContents was silently dropped, which produced the "looks like the script died" symptom.
  • Closing the main IDE while a terminal is detached no longer murders the running scriptmainWindow.on('closed') previously killed every PTY unconditionally, including ones currently routed to the still-alive detached window. So if you closed the main IDE while a long script was running in the popped-out terminal — even briefly, even by accident — the script died instantly. The closed handler now skips PTYs that are owned by a still-alive detached window; they keep running and stay yours to reattach later.
  • The reattach is now visible — The main IDE is restored from minimize, shown, and focused as part of the close handshake, so you can see your terminals reappear instead of wondering whether they vanished. No more "did the script die or did the window just hide somewhere" guessing.

Version 1.7.0 (May 3, 2026)

Custom Domains — Bring Your Own, Reconcile, and Import

A round of work on the custom-domains pipeline so the database stops drifting from reality. If you bought a domain through PorkiCoder and then changed something at the registrar — or owned a domain before you ever opened the app — your custom_domains table now reflects that. Plus two terminal/UI fixes that have been quietly annoying for a while.

  • Import an externally-owned domain — If you already own yourname.dev at Namecheap, Cloudflare, GoDaddy, or anywhere else, you can now point it at a PorkiCoder-published site without buying a second copy. The Custom Domains panel exposes an "Import existing domain" flow that verifies you control the domain (DNS challenge), wires it to the chosen site, and creates the custom_domains row — same downstream behavior as a domain you bought through the app.
  • Restore custom_domains rows for domains you already own at the registrar — If you bought a domain through PorkiCoder before the custom_domains table existed (or the row was lost in some past schema churn), the app now reconciles registrar ownership against the local DB on launch and recreates the missing rows automatically. No more "I own this, why doesn't PorkiCoder know?" — the next time you open the panel, every domain you own at the registrar shows up.
  • Deleting a site cascades to its custom domains — Previously, deleting a published site left orphaned custom_domains rows pointing at a site that no longer existed; the next deploy attempt with the same domain would hit a unique-constraint violation. Site deletion now cascades: every custom_domains row attached to that site is removed in the same transaction. Re-attaching the domain to a new site Just Works.
  • Terminal text reflows correctly when you widen the window — Resizing from narrow to wide used to occasionally "stick" — the running shell kept rendering at the old narrow column count even though xterm had visually expanded, so new output wrapped at the wrong width and TUI programs (claude, vim, htop) drew their UI to the stale size. Two independent root causes fixed: (1) a guard that silently swallowed the PTY-resize step when xterm and the PTY momentarily disagreed about readiness — that resize is now idempotent and ungated, so the PTY always learns the current size; (2) the debounced fit now schedules through requestAnimationFrame plus a 250 ms settle-pass to catch sub-pixel under-reports from ResizeObserver. The targeted repro (run a shell, exit, resize wider, restart shell) now renders at the wide width every time.
  • Zen-mode indicator no longer covers the terminal's fullscreen controls — The "Zen Mode Active — Press Esc to exit" badge used to live as a fixed top-right element with z-index: 100000, which painted directly on top of the terminal panel's clear/fullscreen-toggle/detach/close buttons when both fullscreen and zen mode were on. The badge is now a real top-center button: clickable to exit zen mode (mouse-only path), out of the way of every other floating UI in either layout. And the Esc handler now runs in capture phase with stopImmediatePropagation, so pressing Esc to leave zen mode no longer bleeds through to xterm and cancels whatever Claude Code is running in the terminal.

Version 1.6.0 (April 20, 2026)

Kimi K2.6 — Now Available in Agent Mode

Moonshot's Kimi K2.6 lands in Agent Mode with thinking cranked to maximum — and as a bonus, the terminal color bug it one-shotted in testing ships in the same release. If you've been waiting for a non-Anthropic option that can actually drive the full autonomous Read/Glob/Grep/Bash/Write tool loop, it's here.

  • Kimi K2.6 available in Agent Mode — Pick Kimi from the model selector with Agent Mode on and you get the same Read, Glob, Grep, Bash, WebSearch, WebFetch, and sub-Agent tool loop that's always run on Claude, now driven by kimi-k2-thinking-turbo. Human-in-the-loop diff review, progress cards, and session resume all work identically. The model handles multi-step planning and tool chaining well enough that it can be a real daily driver, not just a "try it once" curiosity.
  • Kimi upgraded to K2.6 everywhere, with thinking maxed out — The default Kimi endpoint is now kimi-k2-thinking-turbo across chat, HOG Mode, Normie Mode, and the apply-diff path, with the provider's highest thinking effort tier selected by default. Summarized thinking streams live in the UI, same as Claude and Gemini.
  • Round-trip reasoning_content on tool-call assistant messages — Previously, when Kimi emitted a tool call with attached reasoning, the reasoning was dropped on the next turn's history replay — which confused the model about its own prior thought process and led to tool-call loops. We now round-trip reasoning_content back on tool-call assistant messages so the chain-of-thought stays continuous across tool hops.
  • Terminal color / contrast fix (one-shot by Kimi K2.6) — Meta-validation: the first non-trivial bug fix we threw at the new K2.6-in-Agent-Mode was a terminal color regression, and it landed the correct fix on the first attempt, no follow-ups. Shipping that fix in this release alongside the model that wrote it.

Version 1.5.15 (April 17, 2026)

Sidebar & ConfirmModal — Reliability Pass

Twenty-six bugs swept out of the project sidebar and the confirm/alert modal — the kind of issues that rarely crash but quietly corrupt state, leak memory, or silently drop your work. Most were found in a code-review pass and fixed as a batch; none of them were on fire, all of them should have been.

  • Filenames with <, &, or > no longer break the tree — Searching a tree that contained a file like foo<bar>.js used to corrupt the filename on the next clear-search pass: an innerHTML = textContent round-trip re-interpreted the brackets as HTML, so the sidebar rendered foo with the rest silently swallowed. The cleanup paths now restore from a data-name attribute stored at node creation, and highlight insertion escapes through a proper HTML-escape helper.
  • Search stops returning hits for files that no longer exist — Deleting a directory used to leave every file inside it in the search index, because the delete handler only purged the directory's own (absent) entry. Added a prefix-delete that walks every indexed file under the path. The same leak pattern in window.projectFileMap (breaking @filename autocomplete after a delete) is fixed alongside.
  • Rapid filesystem bursts no longer pile up duplicate work — A Set keyed by object identity meant two chokidar events for the same path produced two separate entries, and the sidebar then did two rounds of DOM removes, search-index updates, and ancestor-walk consolidation per path. Converted to a Map keyed by type:path so repeat events for the same path collapse in place. Matters most under agent-driven batch writes where dozens of events fire within a frame.
  • IPC listeners stop firing after app teardown — Three of five IPC cleanup thunks (directory change, file-changed-for-search, build-activity-ended) were being discarded, so after the controller tore down those handlers kept firing on a destroyed state — mutating a cleared pending-changes map, calling index methods on a nulled worker, scheduling refreshes against a detached tree. All five thunks are now captured and called in destroy(), and every handler body short-circuits on the teardown signal.
  • User edits during a build no longer vanish for up to a second — When a build finished, the build-activity-ended handler would pendingChanges.clear() and cancel the debounced flush before scheduling a 1-second safety refresh. Any real user edits, file deletions, agent writes, or rename events that happened during the build window were silently dropped — the tree stayed stale for a full second. The clear-and-cancel is gone; queued events now flush normally within 10 ms.
  • Stop auto-opening every file the agent creates — After an agent batch-write, the sidebar used to click open every new file that matched the editor-open allowlist, thrashing the editor as each click overwrote the last. User context-menu creates piggybacked on the same path and opened files the user hadn't asked to open. Now the sidebar just pulses + scrolls new entries into view; you can click to open if you want to.
  • Search auto-expansions don't permanently reveal directories — When a search auto-expanded directories to show matches, those expansions were indistinguishable from user-driven expansions — so if saveExpandedState ran mid-search (common: any delete during an active search triggers a refresh), the search-only expansions got persisted and those directories stayed permanently open across sessions. A new .search-expanded class marker separates the two so persistence skips search-only expansions; explicit user toggles upgrade search-only expansions into real ones.
  • .env, Dockerfile, Makefile, .gitignore get syntax highlighting — All of these opened as plaintext because the extension map had no entry for dotfile-derived extensions or no-extension filenames. Added a filename-first lookup (Dockerfile → dockerfile, Makefile → makefile) and a prefix rule so .env.local, .env.production, etc. all resolve to shell highlighting. .editorconfig maps to ini.
  • Native confirm() and alert() replaced with the styled modal — The delete flow and eight create/rename error paths were using browser confirm()/alert() which block the renderer event loop and look out of place against the rest of the app. All nine sites now route through the existing ConfirmModal component (a new showAlert variant handles single-button dismissals).
  • ConfirmModal race conditions closed — Seven subtle bugs around modal lifecycle: a stale 200 ms teardown timer could destroy a follow-up alert, the duplicate-modal guard silently dropped both callbacks (callers waiting on either would hang), consecutive showAlert calls dropped the prior onClose, a reentrant showAlert from inside an onClose destroyed the new modal, the duplicate-guard's synchronous onCancel was a reentrancy risk. All fixed; the modal now composes safely under collision, reentry, and rapid-fire scenarios.
  • Worker response race after project refresh eliminated — The search worker used to apply index updates from the old project against the new tree when a refresh swapped the data mid-flight — so searches returned a mix of real and ghost results until the next restart. The worker now stamps each response with a generation token; the sidebar bumps the generation on refresh and drops mismatched responses.
  • Long lines no longer hide matches past column 500 — The search worker used to truncate every indexed line to 500 characters, so a match living at column 600 was invisible, and the content preview in the sidebar was the cut-off line with highlights sometimes landing mid-word. Replaced per-line truncation with a 200 KB aggregate budget — full lines go into the index, CSS truncates the visual preview with ellipsis, and match positions stay correct.
  • And fourteen more — timer leaks on destroy, context-menu listener leaks, a crash on indexFile after worker teardown, filenames with special characters breaking querySelector lookups, stale decoration-tracking entries across deletes, children: undefined directories crashing the render, batched-create diff-removal path not pruning search/file/git state, descendant expandedState orphans, null-project-load leaving stale timers and decoration sets, detached <li> refs pinned across project refresh, and several others along the same lines.

Version 1.5.14 (April 17, 2026)

Chat — UI Polish Pass

Twelve improvements across the main chat surface — the streaming path, the scroll behavior, and the small affordances you use without thinking. Eight features plus four follow-up fixes uncovered in review.

  • Streamed messages finalize in place — no flash, no re-render — When a response finished streaming, the streaming element used to be removed and a fresh displayMessage node rebuilt in its place, causing a visible flash on long replies. Now the streamed element stays put: the blink cursor is dropped, the delete and copy buttons are attached, and the markdown is re-parsed once into the existing DOM. And because the in-place path had drifted from displayMessage's output (thinking block at the top, wrong classes, plain-text reasoning), the finalized DOM is now identical to what you get when reloading the same message from history — reasoning at the bottom, .ai-thinking / .thinking-content classes, markdown parsed through marked. No more "this looked different during streaming vs. after reload."
  • Auto-scroll pauses when you scroll up during streaming — Scroll up mid-stream to re-read something and the chat now stays put instead of yanking you back down on every chunk. A pinnedToBottom flag flips off when you're more than 40px from the bottom; when you scroll back within 40px, it re-pins automatically and subsequent chunks snap the view down again. Three hardcoded scroll sites now route through a single maybeAutoScroll() helper.
  • Blinking cursor in the thinking preview — While the model was reasoning, the live preview element could sit silent for a second at a time — long enough to make the UI look frozen. A subtle blinking cursor now lives inside .streaming-thinking-preview via a CSS ::after, reusing the existing blink-cursor keyframes. As soon as the first text chunk arrives the preview collapses into the <details> summary and the cursor disappears cleanly. Purely additive CSS.
  • "Reasoning… 0s" label visible from the first frame — The cached thinking header used to be created empty and only populated by the next rAF tick or the 1s interval, so if the model emitted no thinking chunks for the first second the header was blank. Now the label is written at creation time so it's visible immediately; the timer and rAF updates keep refreshing the elapsed seconds.
  • Agent summary headers update from pending → done — After the agent resolves, the in-chat summary strings ("Applying files…", "Running commands…") now rewrite themselves to reflect the final counts and outcome: "Applied N file(s)" / "Failed to apply N file(s)"; "Ran N command(s)" / "Failed to run N command(s)". New -done/-error CSS classes tint the headers green or red so the result reads at a glance.
  • Only newly-arrived messages animate — Opening a 50-message conversation used to stutter-cascade as every message played its messageFadeIn. The animation is now gated on a .message.new class that displayMessage only adds for live arrivals; history-path callers pass an isHistory flag and skip the class. Loading a long thread feels instant.
  • History loads and Refresh Chat land you at the bottom, always — The new pinnedToBottom flag is module-scoped, so scrolling up in chat A and then opening chat B (or hitting Refresh Chat) used to leave the new view scrolled to the top — every auto-scroll in the load path was a no-op. A new resetScrollPin() helper runs at every clear-and-reload site so fresh conversations always open at the latest message.
  • Delete and copy buttons are discoverable without hover — Both .message-delete-button and .message-copy-button sat at opacity: 0 when the message wasn't hovered — invisible unless you happened to mouse over the bubble. They now rest at opacity: 0.35 with real colors, brighten to 1 on message hover, and pick up their accent shade on button hover with a smooth transition. Disabled and waiting-state overrides still take precedence.
  • Thinking DOM stops rebuilding every animation frame — The rAF tick that updated the elapsed-time header used to innerHTML = '' and recreate the header and preview elements from scratch every frame. Both are now pre-created once per stream and cached in locals; the tick just writes textContent. One less source of layout thrash during a long reasoning chain.
  • Filename highlighter short-circuits when there's no @highlightFilenamesInInput ran a regex + DOM mutation on every keystroke, even when the input had no mentions. Typing a paragraph-length message could cause caret jitter. A cheap indexOf('@') === -1 && !messageInput.querySelector('.filename-tag') guard now returns early before any regex or DOM work — the hot path on non-mention typing is effectively free.

Version 1.5.13 (April 17, 2026)

Terminal — Claude Code @-picker Selection Visible Again

Follow-up to 1.5.12's WCAG-AA contrast bump. That fix solved one TUI bug and quietly broke another — Claude Code's @-file picker.

  • Selected row is once again clearly distinguishable from the rest — Claude Code's @-picker doesn't paint a background for the highlighted row; it dims every other row using SGR \x1b[2m (faint). xterm.js implements faint by blending the foreground toward the background, then lifts the result back up if it falls below the configured minimumContrastRatio. At 4.5 (WCAG AA, set in 1.5.12 to fix a separate white-on-white rendering bug) the lift fully cancelled the dim, so every row in the picker rendered identically and the selection was visually invisible. Lowered to 2 — still corrects degenerate same-fg-as-bg collisions, but lets dim text actually look dim. Selected vs non-selected rows now read at a glance.

Version 1.5.12 (April 17, 2026)

Terminal — Scroll & Follow-State Reliability Pass

An audit sweep through the terminal scrolling subsystem to close the class of stale-paint and follow-state bugs that 1.5.11's tab-switch fix pointed at but didn't fully cover. Thirteen fixes, most of them invisible until you hit them — and then jarring every time.

  • Mouse wheel no longer goes dead after fullscreen, multi-panel toggle, or window bring-back — Four code paths transitioned a terminal container from hidden/resized → visible without routing through the fit+refresh+snap path introduced in 1.5.11. Entering/exiting fullscreen, flipping multi-panel on or off, and returning from a minimise/Spaces switch would all leave the active terminal mid-scrollback with a dead scroll wheel until a keystroke forced a redraw. All four now call the same onShow() that fixed tab switching.
  • "↓ New output" badge and auto-follow reset correctly after clear and restart — If you scrolled up to read an error, then cleared the terminal or restarted the process, the badge used to stay pinned over an empty terminal and subsequent output wouldn't auto-follow — because clear()/reset() shrink content but don't move scrollTop, so no scroll event fires and follow-state never resets. Extracted a shared _resetFollowState helper and called it from both paths so they can't drift.
  • Auto-follow no longer silently turns off during window resize or font-zoom — Transient scrollHeight/clientHeight measurements during a fit (scrollHeight shrinking before scrollTop catches up) could flicker the "at bottom" check to false and flip you into manual-scroll mode with no scroll action on your part. Ctrl+=/Ctrl+-/Ctrl+0 zoom hit it worst because a font-size change is a much bigger resize than a window drag. Follow-state is now snapshotted before the fit and re-asserted after.
  • Inactive tabs can't erase your scroll-up state — The viewport scroll listener treated display: none containers (which report 0 - 0 - 0 < 10) as "at bottom" and reset follow-state to true. Switching to another tab could quietly wipe out your deliberate scroll-up on a background terminal. The handler now early-returns on zero-size viewports.
  • Reattach from a detached window lands you at the live prompt — Three bugs in the reattach path: the tab could open mid-scrollback, the rolling line buffer was never updated from the restored xterm buffer (so @terminal mentions returned empty until new output arrived), and transit-data replay bypassed the scroll-badge + inactive-tab-indicator path that live output goes through. All three fixed — reattached tabs always snap to the prompt, @terminal mentions work immediately, and transit-data replay behaves like live output.
  • Process-exit banner is visible when you're scrolled up — The ── Process exited (code N). Press any key to restart ── line wrote directly to xterm, bypassing the scroll-badge path. If you were scrolled up reading the error that caused the exit (common — that's usually why the shell exited), you'd see no indication the process had ended or that a key would restart it, and a harmless click quietly spawned a new shell you hadn't asked for. The badge now appears in that case so there's an obvious visual cue.

Version 1.5.11 (April 16, 2026)

Terminal — UX Polish Pass

Three rough edges in the multi-terminal experience that we shipped in 1.5.10. A tightening pass.

  • Active-terminal ring no longer flashes and disappears — In 1.5.10 the colored ring used an inset box-shadow on the terminal container, but xterm's canvas paints on top of that and covered the ring almost immediately after it appeared. Re-implemented as a ::after pseudo-element overlay with pointer-events: none so it sits above xterm's content (clicks still reach the terminal). The ring now persists the entire time a tab is active instead of vanishing after the first paint.
  • No ring when only one terminal is open — A single session doesn't need a disambiguation cue. The ring now only renders when there are two or more terminals, so a solo terminal has a clean frame. Rematerializes the moment you open a second tab and disappears when you close back to one.
  • Active tab row stays calm and uniform — Tried a per-session coloring on the active tab text/glow; it read as busy across three tabs and didn't hold up for non-green tab colors (see the color-theory discussion in the commit trail). Reverted to a neutral VS Code / iTerm-style active tab: white text, subtle neutral fill, no glow. Color signaling lives in two places now where it carries real information: the always-on left strip on each tab (per-session identifier, visible on active AND inactive tabs) and the panel ring (active session).

Zsh %-Marker Finally Gone

  • Fixed a long-standing bug where zsh's reverse-video % would appear in terminals — Zsh's PROMPT_SP option prints a bold % plus a padded CR whenever the previous command's output didn't end in a newline, so the next prompt doesn't collide with partial output. The renderer was already setting PROMPT_EOL_MARK='' on the terminal's env to suppress the symbol — but the main-process PTY handler was building its spawn env from scratch using only process.env and silently dropping options.env. The override never reached zsh. Fixed by merging options.env into the spawn env so the existing renderer-side override actually takes effect. Classic renderer ↔ main handshake mismatch that had been hiding in plain sight.

Version 1.5.10 (April 16, 2026)

Terminal — Never Close the Wrong Tab Again

When you're juggling multiple terminal sessions and go to kill the one that's finished work, it's easy to hit the × on the wrong tab. Each tab already had its own color on its left edge — now the active terminal body wears that same color as a persistent ring, so the tab → panel mapping is unmistakable at a glance.

  • Active terminal shows its tab color as a persistent inset ring — In single-panel mode the colored ring used to only appear in multi-panel view, which meant when one terminal was visible there was no visual link between the highlighted tab and the session you were looking at. Now the active .terminal-instance always carries a 2px inset box-shadow in var(--tab-color), so switching tabs visibly repaints the whole panel frame in that session's color.
  • Zero layout shift — Implemented as an inset box-shadow rather than a real border, so activating a tab doesn't nudge the xterm content by 2px. Multi-panel's existing colored border still wins via CSS specificity when you're in grid view.

Version 1.5.9 (April 16, 2026)

UI Polish — Consistent Emerald Theme

Two small visual inconsistencies that only show up once you're using the app daily. Now fixed so the emerald accent is consistent everywhere.

  • Advanced-mode (gear) toggle now matches the emerald theme in Agent Mode — In Agent Mode everything else adopts the green accent, but the gear toggle was still lighting up blue because it had its own --accent-primary active style and no agent-mode override. Added it to the same green-override block the web-search toggle already uses; the gear is now emerald-on-emerald like the rest of the header.
  • Glassmorphism emerald scrollbar in the live preview — The scrollbar inside the previewed site was whatever boring grey Chromium gave it by default. The preview webview now has CSS injected on every dom-ready (survives navigations/reloads) — translucent blurred black track with an emerald gradient thumb that glows brighter on hover. Matches the app's glass aesthetic.

Version 1.5.8 (April 16, 2026)

Claude Opus 4.7

  • Migrated Opus 4.6 → Opus 4.7 across Agent Mode, chat, and the apply-diff path. Anthropic's latest and most capable Opus is now the one you get everywhere "Opus" was an option — sharper reasoning on long contexts and better tool-use. (Gemini 3.1 Pro remains the default model; pick Opus 4.7 from the model selector when you want it.)
  • Thinking panel stays live — Opus 4.7 hides raw thinking by default; we explicitly request thinking.display = "summarized" so the thinking stream keeps rendering in the UI the way you're used to.
  • Adaptive settings adjusted — Opus 4.7 no longer accepts temperature / top_p / top_k; dropped those from the adaptive request path so requests don't fail under the new API contract.

Terminal — Rock-Solid Lifecycle

A full reliability pass on the terminal controller to close every race and leak we could find. If you ever saw a terminal freeze, spawn in the wrong folder, or leak a PTY when you closed a tab fast — this is the release where that stops.

  • No more crashes when closing a terminal mid-startupdestroy() and initialize()/restart() used to guard their own re-entrance but never coordinate with each other. A tab-close during init could null out the terminal mid-flight and the init continuation would then dereference it and crash. Destroy now flips isDestroying first and awaits any in-flight init or restart; all async paths re-check isDestroying after every await and bail cleanly, closing any PTY they'd already spawned so nothing leaks.
  • Restart spawns the shell in the right directory after project switchattachToExisting was unconditionally overwriting the stored cwd with the currently-open project, so if you switched projects and then hit restart on an older tab, the new shell would come up in the wrong folder. The original cwd is now preserved.
  • Readiness / cleanup gaps in attach + init paths closed — split the shared init promise into separate init/attach slots so cross-method calls fail loudly instead of silently dropping arguments; flip isTerminalReady true before wiring xterm listeners so events never fire while the tab still reads "not ready"; add a destroy re-check after the initial resize so a racing close can't re-flip isInitialized back on; publish to this.terminals only after attach succeeds so a failed attach is never briefly visible; and make sure partial setup is torn down (ResizeObserver, scroll badge, window listeners) in the destroy-during-init bail path.
  • Early bail on already-destroying terminals_performInit, _performAttachToExisting, and _performRestart now short-circuit at entry and after the project-path lookup when destroy has already fired, avoiding a full xterm setup + createTerminal IPC spawn that would immediately have to be torn down.
  • Detached terminal window: activity dot stops pulsing forever — Ported the active → waiting → idle timer chain from the main window into the detached window, so a background-output tab in a detached window now correctly decays from green pulse to amber to idle.
  • Misc regressions swept up — reattach now only iterates terminals that actually attached successfully, the per-tab activity map is fully cleared on detach, command-launched terminals log init failures instead of swallowing them, and backend-id lookups use strict equality.

Agent Mode — Longer Autonomous Runs

  • Claude Agent tool-turn ceiling raised 25 → 100 — Matches what Gemini Agent got in 1.5.6. Complex multi-step work (broad refactors, migrations, multi-file investigations) no longer hits an artificial "agent stopped after 25 turns" ceiling in the middle of a run.

Gemini Agent & Advanced Mode Fixes

  • Advanced-mode toggle no longer gets clobbered mid-boot — If you clicked the Advanced Mode toggle while the chat was still finishing its async init, a stale .then() would land after your click and silently flip the internal flag back to its stored preference. The CSS class stayed on so the UI looked advanced, but Cmd+M and Cmd+1–9 would start behaving as if Advanced Mode were off. Removed the racy continuation; the inline callback now owns the state.
  • TaskUpdate: no more literal "null" rendering in TaskList — Gemini's schema declares task subject/description as strings but can actually send null. The update path was using !== undefined, so a null would pass the write check and corrupt the task — rendering as the literal word "null" in TaskList output. Tightened to typeof === 'string'.

Live Preview Button — Easier to Find

  • Play-triangle icon + one-time coachmark — The Live Preview toggle used to hide among a row of near-identical icon-only chat-header buttons. Swapped in a filled play triangle (▶), added a first-time pulsing cyan tooltip the first time you open an HTML file ("Click here to see your site live."), and rewrote the tooltip copy to plain language ("Preview site — see your HTML/site live").

Version 1.5.7 (April 7, 2026)

Custom Domain Reliability Fixes

  • Domain transfer to another PorkiCoder user actually works now — The transfer feature was silently broken since launch: supabase-js's listUsers wrapper drops the filter parameter on the floor (only forwards page + per_page to GoTrue), so the recipient lookup was returning the same wrong user every time and failing the exact-match check for everyone except whichever account happened to be at the top of the database. Replaced with a paginated lookup that exact-matches in JS. Transfer now also requires the recipient to have published a site first and the sender to enter that subdomain in the dialog, so the recipient inherits a domain pointing at content they actually control — instead of one still pointing at the sender's site (which the sender could keep editing, and which would tear down the recipient's domain if the sender ever deleted that site).
  • Custom domain teardown no longer leaks AWS infrastructure on site delete — Deleting a published site that had a linked custom domain disabled the CloudFront distribution but never actually deleted it, leaving the disabled distribution and its SSL certificate orphaned. The site-delete handler now hands the row off to the same DB-backed reaper the standalone domain delete already used.
  • Domain purchase no longer takes payment without provisioning — The Stripe checkout session is now created after the database row is claimed. If the row claim fails (transient DB error, or a unique-constraint race against another user buying the same domain), the user gets a clean 409 instead of a Stripe charge with no tracking row. Stripe failures after the claim roll back the row so the domain becomes purchasable again.
  • "Change site" no longer reports false failures on cache-invalidation hiccups — When CloudFront's cache invalidation API hit a transient error during a change-site operation, the whole operation was reported as failed even though the origin update had already succeeded. The invalidation is now best-effort with a warning log; the origin change is consistent and edge caches refresh naturally as their TTL expires.
  • Cleaner SIGTERM/SIGINT handling — Container shutdown no longer crashes with a stale cfCleanupTimer ReferenceError. Replaced with a single gracefulShutdown handler that drains in-flight HTTP requests and force-exits after 8 seconds if server.close() hangs.

Version 1.5.6 (April 6, 2026)

New Features

  • Autopilot Mode for Agent Edits — New header toggle that lets Agent Mode auto-apply file writes and edits without showing the diff preview modal. Per-edit progress still surfaces in the activity toast (with a ⚡ icon to distinguish autopilot writes from reviewed ones), so you stay informed without being interrupted. State persists across restarts. Works for both Claude Agent SDK and Gemini Agent.
  • Gemini Agent: AskUserQuestion tool — Gemini agent can now pause mid-task to ask a clarifying question via a modal, instead of guessing on long autonomous runs. Mirrors the Claude Agent SDK's AskUserQuestion behavior.
  • Gemini Agent: environment context injection — Each run now injects cwd, platform, shell, OS version, git branch + status, model identity, knowledge cutoff, and today's date into the system prompt. Prevents wrong-OS suggestions and stops the agent from stepping on uncommitted work.
  • Gemini Agent: longer autonomous runs — Raised MAX_AGENT_TURNS from 25 → 200 and MAX_SUB_AGENT_TURNS from 10 → 50, matching Claude Code's effectively-unlimited default. Fixes premature "agent quit after 25 turns" cutoffs on legitimate multi-step work.
  • Background SSL provisioning for custom domains — The domain purchase modal no longer blocks for the 5–60 minute SSL/CDN steps. After payment + the registrar/DNS handshake, it flips to a "submitted" state with a TLD-aware ETA and hands the domain off to a background watcher. Live status flows through Settings → Custom Domains, with toasts on completion and a boot-time catch-up that surfaces transitions that happened while the app was closed.

Fixes & Improvements

  • Friendlier "awaiting payment" UI — The domain purchase modal no longer shows a raw payment_pending code chip while waiting for Stripe confirmation. Replaced with a soft blue info pill containing a spinner and a human label ("Waiting for payment", "Registering domain", etc) that updates as the backend status advances.
  • Faster stuck-row recovery — The Retry button on Settings → Custom Domains now appears after only 3 minutes of in-progress staleness (down from 60 minutes), and explicitly says it's safe to click. The visual "failed" treatment is unchanged at 60 minutes — Retry is now a softer manual nudge for Stripe webhook hiccups.
  • Clearer payment-wait copy — The awaiting-payment screen now correctly explains that we're waiting on the backend to confirm Stripe (not on the user to pay), and always exposes a "Continue in background" escape hatch.
  • Subscription status auth fix — Fixed a pre-existing 401 on the subscription-status fetch by adding the Bearer token header — backend now requires auth on that endpoint.

Version 1.5.5 (April 6, 2026)

Fixes

  • Fixed "Request validation failed" error — All LLM and Agent Mode requests were failing because the Electron client was not sending the Supabase auth token to the server's validation endpoint. Both validateRequest and getSubscriptionStatus API calls now include the Authorization: Bearer header.
  • Better error messages for validation failures — Validation errors now surface the actual failure reason (timeout, auth issue, network error) instead of a generic "Request validation failed" message.

Version 1.5.4 (April 6, 2026)

New Features

  • Retry domain provisioning — If domain setup gets stuck after payment, you can now retry provisioning directly from the app. Added IPC plumbing between the Electron client and backend for manual retry triggers.

Security

  • Production error messages sanitized — API error responses no longer leak internal details (DB column names, AWS errors, Porkbun internals) in production.
  • Security headers on user-content subdomain — Added HSTS, Content-Security-Policy with frame-ancestors 'none', and changed X-Frame-Options to DENY on the wildcard subdomain block, preventing cross-subdomain iframe attacks.
  • Auth client privilege reduction — Auth verification client now uses the anon key instead of the service role key.

Fixes & Improvements

  • Orphaned CloudFront distributions tracked and retried — Background distribution deletions are now tracked in-memory with a periodic retry scanner every 15 minutes. Timeout increased from 10 to 15 minutes.
  • Domain renewal scanner runs more frequently — Increased from once per day to every 6 hours, with escalating urgency levels (critical/urgent/warning) based on days until expiry.
  • Stripe webhook uses shared orchestrator — Domain provisioning triggered by Stripe webhooks now uses the singleton DomainOrchestrator, fixing missing SSE progress events and a broken concurrency guard.
  • Partial upload rollback — When a deploy fails due to too many upload errors, successfully-uploaded files are now rolled back instead of left orphaned in S3.
  • Partial upload threshold fixed — Exactly 10% file failure now correctly triggers the failure path (was off-by-one).
  • Domain info status field fixed — Removed a nonsensical TLD fallback that could return "com" as a domain status.
  • changeSite ownership check — The orchestrator now verifies subdomain ownership defensively, not just the route handler.
  • deploy_log cleanup activated — The pg_cron schedule for hourly cleanup of old deploy log entries is now active.

Version 1.5.3 (April 4, 2026)

Security

  • Shell injection fix in Grep tool — Switched from shell execution to direct process spawn, eliminating a potential shell injection vector in Agent Mode's Grep tool.

Gemini Agent Mode Improvements

  • 3-layer tool result size management — New graduated truncation system prevents context window overflow during long Gemini agent sessions. Results are progressively compressed across three layers before hitting the model's limits.
  • Claude Code-like features — Gemini agent orchestrator gains WebFetch tool support, sub-agent spawning, and improved concurrent tool execution, bringing it closer to parity with Claude Agent Mode.

Fixes & Improvements

  • Fixed false "Agent could not run" error — Resolved a startup error that incorrectly reported Gemini agent sessions as failed.
  • Broader overflow detection — Layer 3 overflow detection now catches Gemini HTTP 400 errors in addition to existing overflow signals.
  • Fixed response accumulation bug — Intermediate model text no longer leaks into the final response during agent loops.
  • Race condition fix — User messages are now removed by ID instead of pop(), preventing a race condition when messages arrive mid-removal.
  • Thought structure preserved during compaction — Aggressive context compaction no longer corrupts thought block structure.
  • Sub-agent truncation — Tool result truncation layers now correctly apply inside sub-agent loops.
  • Streaming suppressed during compaction retry — Prevents partial/duplicate content from reaching the UI during context compaction retries.
  • capAggregateResultSize fixed — The function now actually shrinks oversized results to the target size instead of silently passing them through.
  • WebFetch hardened — Body streaming now enforces size limits and a full-lifecycle timeout, preventing runaway fetches.
  • Gemini agent error metadata — All error and abort return paths now include proper agentMeta, fixing missing progress indicators on failures.

Version 1.5.2 (April 4, 2026)

Fixes & Improvements

  • Custom domain shown after publish — Users with a custom domain linked to their site now see the custom domain URL (e.g. shamikashabnam.com) on the publish success screen instead of the .porkicoder.com subdomain.
  • "Get Custom Domain" button hidden when not needed — The button no longer appears after every publish if the user already has an active custom domain for that site.

Version 1.5.1 (April 4, 2026)

New Features

  • Custom Domain Management in Settings — View all your custom domains with status badges (active, provisioning, renewal due), linked subdomains, and expiry dates directly in Settings.
  • Change Site — Re-point any active custom domain to a different published site via a dropdown, right from Settings.
  • Internal Domain Transfer — Transfer domain ownership to another PorkiCoder user by email. Two-step inline confirmation with real-time status. No premium requirement for the recipient.
  • External Transfer Support — Dedicated "Transfer Out" action with direct support contact for transferring domains to external registrars.

Fixes & Improvements

  • Fixed Change Site dropdown not working after domain transfer — recipient can now re-link the domain to their own site.
  • Fixed CloudFront 504 errors on pre-migration distributions by ensuring http-only protocol for S3 origins.
  • Domain card layout now stacks vertically, preventing text wrapping issues with long subdomain names.
  • Published site storage migrated from Supabase Storage to S3 on the backend — eliminates Content-Type and CSP header workarounds.

Version 1.4.1 (April 2, 2026)

Fixes

  • Missing dependency fix — Added missing glob package that caused a crash on launch ("Cannot find module 'glob'") in the Gemini Agent orchestrator.

Version 1.4.0 (April 2, 2026)

New Features

  • Gemini Agent Mode — Gemini models now have a full Claude Code-style agent loop built from scratch. Concurrent tool execution, WebFetch tool support, and Google Search grounding let Gemini autonomously read, search, run commands, and browse the web inside your project — just like Claude Agent Mode.
  • Publish-to-Web — Premium users can now publish any project as a live site at <name>.porkicoder.com. One-click deploy directly from the IDE with automatic subdomain provisioning.

Fixes & Improvements

  • Removed terminal session grid view overlay for a cleaner terminal experience.
  • Cleaned up unused dependencies for a smaller install footprint.

Version 1.3.4 (March 25, 2026)

Fixes & Improvements

  • Zero-Loss Terminal Detach & Reattach — PTY output is now buffered in the main process during detach/reattach transitions and replayed once the receiving window is ready. No terminal content is lost during window transfers, even with rapid detach→reattach cycles.
  • Stale Mouse Mode Reset on Process Exit — When a process that enabled mouse tracking (vim, htop, less) exits or is killed, terminal modes are now explicitly reset. Previously, clicking in a terminal with stale mouse tracking could trigger an accidental restart that wiped the scrollback buffer.
  • Buffer serialization timeout increased from 3s to 10s, preventing content loss when reattaching terminals with large scrollback.
  • Exit events during transitions are now captured and delivered alongside buffered data, so terminals that die mid-transfer show "press any key to restart" instead of appearing stuck.
  • Fall back to original main-window buffer when the detached window closes before initializing its controllers.
  • removeTerminal() now properly awaits destroy() for clean PTY shutdown before DOM removal.

Version 1.3.3 (March 24, 2026)

New Features

  • Drag & Drop Files / Images into Terminal — Drop any file or image directly onto the terminal and its shell-escaped path is typed at the cursor. Pairs naturally with Claude Code running in the terminal — drag a screenshot or design file and it lands ready to reference.

Version 1.3.2 (March 24, 2026)

New Features

  • Multi-Session Terminal Management — Run up to 6 concurrent Claude Code sessions in dedicated terminal tabs. Each tab gets activity indicators (pulsing green dot for live output, amber when idle), an auto-assigned cyberpunk color palette, and persistent tab names (double-click to rename). Navigate with Cmd+1-9 or cycle with Cmd+Shift+[ / Cmd+Shift+]. Quick-launch a Claude Code session with Cmd+Shift+C. A session grid view overlay shows all terminals at a glance.
  • Multi-Panel Split View — Toggle a CSS grid layout that shows all open terminals simultaneously in up to 3 columns. The active terminal is highlighted with its tab color. Click any panel to focus it. Works in both the main window and detached terminal windows.
  • Terminal-to-AI-Chat Bridge — A "Send to Chat" button captures the active terminal's recent output (or current selection) and pre-fills it as a code block in the chat input. Type @terminal in the chat for autocomplete that injects the terminal buffer as inline context to the LLM. Powered by a 200-line ANSI-stripped rolling buffer.

Fixes & Improvements

  • Kill orphaned PTY processes on app quit to prevent zombie processes.
  • Reentry guard for toggleDetach() preventing concurrent detach/reattach operations.
  • Fix attachToExisting() concurrent init race and missing error handling.
  • Await async destroy before window.close() in detached terminal for clean shutdown.
  • Close grid modal before detach to prevent listener leaks and stale DOM.
  • Wire onInactiveOutput in detached window so tab activity dots show correctly.
  • Validate DOM before toggling multi-panel state, preventing null reference errors.
  • Set _cwd in attachToExisting() for correct restart directory.
  • Cap _lineResidue at 10KB to prevent memory growth from oversized lines.
  • Improved error handling in addClaudeSession() with graceful fallback.
  • Keyboard shortcuts now use event.code for consistent behavior across keyboard layouts.
  • Balanced multi-panel grid layout (2×2 for 4 terminals instead of 4×1).
  • Track restart-on-keypress disposable for proper cleanup.
  • Guard auto-scroll setTimeout against early destroy.
  • Prevent visibility/resize listener accumulation on repeated panel toggles.
  • Fix keyboard shortcuts using event.code for bracket keys across layouts.

Version 1.3.1 (March 24, 2026)

New Features

  • Multi-Screen Preview Navigation — Preview mode now discovers all HTML files in your project. Use arrow keys (or the nav buttons in the preview header) to navigate between screens. The header shows the current filename and position (e.g. admin.html 2/3).
  • Preview Any HTML File — Live preview no longer requires an index.html. Any .html file in your project is directly accessible; if no index.html exists, the first HTML file found is served automatically.
  • Svelte & Vue Frontend Detection — Projects with .svelte or .vue entry files are surfaced in the preview navigator. Selecting one shows a clear build-step guide (npm install && npm run dev) instead of a blank page.

Version 1.3.0 (March 18, 2026)

New Features

  • Project Instructions (porki.md) — Drop a porki.md file in your project root and PorkiCoder's agent will load it as persistent project-level instructions, giving the AI full context on your codebase conventions, architecture, and preferences.
  • Copy Button on Chat Messages — Every AI response now has a one-click copy icon so you can grab code or text without selecting.
  • Element Attachments in Chat — You can now attach elements (images, files) directly into the chat for richer context when prompting the AI.
  • Preview Error Auto-Send — When your live preview hits an error, PorkiCoder automatically sends it to the AI so it can diagnose and fix it without you copy-pasting stack traces.
  • Agent Cancel Button — The stop button now properly cancels agent mode with a clean abort mechanism and double-send guard.
  • Auto-Open Agent Projects — When the agent creates a new project, the sidebar automatically opens it as soon as the first file is written — no more manual navigation.
  • Terminal Transcript Preview — Full transcript preview is now available in the terminal script picker.

Linux ARM64 Support

  • Full Linux ARM64 build pipeline with auto-launched Docker cross-compilation from macOS.
  • Fixed node-pty binary corruption when building Linux Docker images on macOS.
  • Updated native modules and build scripts for ARM64 compatibility.

Fixes & Improvements

  • Agent SDK no longer crashes when settingSources is empty — prevented an exit code 1 failure.
  • Agent now retries with a fresh session when the SDK session goes stale, and no longer fails silently when Claude Code is unauthenticated.
  • Sidebar no longer opens the parent folder instead of new projects — replaced 60 lines of heuristics with actual file paths.
  • Cancel button no longer nukes the sidebar; Bash file writes are now tracked for auto-open.
  • Executables and compiled binaries are now hidden from the sidebar file tree.
  • Live preview no longer refreshes itself into oblivion when the terminal panel is open.
  • Fixed packaged app not finding npm, which caused live preview to fail on full-stack projects.
  • Upgraded electron-builder to v26 to fix node-gyp failures with Python 3.14.
  • Fixed escaped HTML in stderr output, backtick injection, and sidebar race conditions.
  • Removed 660 lines of terminal transcript context pollution for cleaner agent interactions.
  • Closed isGenerating race window and ensured all AI messages have IDs.

Version 1.2.9 (March 16, 2026)

New Features

  • Live Preview — See your project running directly inside PorkiCoder without leaving the editor. Click the preview button to launch on a local port and start working visually.
  • Smart Project Detection — PorkiCoder automatically detects your project type and picks the best preview strategy: Vite projects get native HMR, framework projects (Next.js, Nuxt, CRA) run npm run dev with auto port detection, and static sites get a built-in HTTP server with WebSocket live reload.
  • Auto-reload — File changes trigger instant browser refresh, debounced to handle rapid multi-file saves from the AI agent.
  • Framework-aware — Detects package managers (npm, yarn, pnpm, bun) and respects each framework's conventions.
  • Graceful fallback — If Vite or a dev command fails, PorkiCoder falls back to static serving automatically.
  • Zero config — No setup required. Open a project, hit preview, and it works.

Version 1.2.8 (March 13, 2026)

New Features

  • Claude Agent SDK Integration — Agent Mode now runs on the official @anthropic-ai/claude-agent-sdk, enabling autonomous multi-step tool use directly inside PorkiCoder.
  • Human-in-the-loop Diff Review — Agent file writes now route through a blocking diff review bridge. Every edit is shown in a Monaco Diff Editor modal for you to accept or reject before it touches your codebase.
  • Full Tool Suite in Agent Mode — Agent can now use Read, Glob, Grep, Bash, WebSearch, WebFetch, and sub-Agent tools autonomously to understand and modify your project.
  • Resumable Agent Sessions — Agent conversations are now persisted per thread, so you can pick up complex multi-step tasks exactly where you left off.
  • Adaptive Thinking with Streaming — Extended thinking is enabled in Agent Mode with thinking deltas streamed live to the UI, so you can follow the agent's reasoning in real time.
  • Agent Progress Cards — New toast-style progress cards show planning, turn-start, tool-start, thinking, and generation-complete events as the agent works.
  • Added Kimi model support — Kimi (Moonshot AI) joins Claude, GPT, Gemini, and Grok as a supported provider.

Fixes & Improvements

  • Agent Mode now serializes diff reviews — only one review shown at a time with a 5-minute timeout to prevent stalls.
  • Improved chat history management with a 40-message sliding window for better context handling.
  • EventManager cleanup on app exit to prevent memory leaks from stale listeners.

Version 1.2.7 (March 10, 2026)

New Features

  • Thinking tokens are now stripped from model responses, reducing cost and improving output accuracy.
  • Web search is now available on all models — including OpenAI, Gemini, and Anthropic. No longer limited to specific models.
  • Effort controls for all models — Low, Medium, High, and Max settings are now available across every model. Current effort level is shown as green bars in chatview, and can be adjusted with ⌘ + ↑ / ⌘ + ↓.

Version 1.2.6 (March 4, 2026)

New Features

  • Introducing Agent Mode — capable of running shell commands directly in chatview.
  • Max, High, Medium, Low Effort controls added for Claude Opus 4.6 and Sonnet 4.6.
  • Better guardrails for less error prone code application.
  • Added Codex 5.3 Medium and Codex 5.3 High models.

Fixes & Improvements

  • Overall UI improvements for Chatview and Autocomplete file tagging mechanism.

Version 1.2.5 (February 26, 2026)

New Features

  • New file creation can now happen from chatview.

Fixes & Improvements

  • Code block generation made more robust in chatview making allowances for LLM generated errors.

Version 1.2.4 (February 24, 2026)

New Features

  • Added git status indicator for files in the side panel.
  • Added auto-apply option for multi-file code application in one-go.
  • Code blocks now show as collapsible/expandable blocks for easier scrolling and navigation.
  • Diff preview has been fully refurbished — now displays line-by-line stats for lines added and removed, alongside a completely redesigned, more polished interface.

Version 1.2.3 (February 22, 2026)

New Models

  • Added Claude Opus 4.6 and Claude Opus 4.6 Max — Anthropic's most capable models yet.
  • Added Claude Sonnet 4.6 for fast, balanced code generation.
  • Added Gemini 3.1 Pro and Gemini 3.1 Flash from Google DeepMind.

Fixes & Improvements

  • Polished overall UI with a range of visual consistency improvements.
  • Resolved terminal bugs causing unexpected output and instability.
  • Fixed sidebar panel issues including incorrect file tree states and refresh glitches.

Version 1.2.21 (January 16, 2026)

Fixes & Improvements

  • Fixed terminal glitches for a smoother command-line experience.
  • Resolved app slowdown when opening projects containing Python virtual environments.

Version 1.2.2 (January 3, 2026)

New Features

  • Added "Open in Finder" option to sidebar context menu for quick file access.

Fixes & Improvements

  • Improved UI behavior on shrinking screens for better responsive layout.
  • Resolved issues with selection of multiple files with the same name.
  • Fixed filepath extraction for new files.
  • Fixed issues with new file creation via chatview.
  • Other minor quality of life improvements relating to UI resizing.

Version 1.2.1 (December 6, 2025)

New Features

  • Added Gemini 3 Pro and Opus 4.5 models.
  • Added Zen Mode for full screen code viewing.

Fixes & Improvements

  • Squashed bugs with terminal and sidebar panel.
  • Improved coding for Normie Mode patches.
  • Fixed HTML rendering in chatview.
  • Can now view .ejs files in sidebar search.

Version 1.2.0 (October 28, 2025)

New Features

  • Added GPT-5 High and GPT-5 Medium to the list of available models, expanding your options for code generation and analysis.

Fixes & Improvements

  • Real-time Sidebar Sync: Resolved an issue where the sidebar file tree did not update in real-time to reflect file changes, ensuring your project view is always current.
  • Editor Display Fix: Fixed a bug where the editor would not display code modifications made by Claude models until the file was reopened. Changes are now reflected instantly.
  • Terminal Display Correction: Corrected a minor display issue with a trailing '%' symbol appearing in the integrated terminal output on certain platforms.