# Changelog

All notable changes to Warm will be documented in this file.

The format is based on Keep a Changelog, and this project follows Semantic
Versioning.

## [Unreleased]

## [0.5.11] - 2026-09-26

### Changed

- The system prompt is sent as cache-tiered sections: static prose and the skills/project tier each close a prompt-cache breakpoint, the environment block and any per-turn text (slash-command prompts, goal cycles, subagent roles) follow unmarked, and the four-breakpoint budget goes to the system tiers plus the two newest messages instead of the tool list.
- Local session events (model switches, slash commands, MCP notices) now travel on the next user message as a hidden reminder instead of being appended to the system prompt, so the cached prefix stays byte-identical while they keep arriving. The transcript still shows only what was typed.
- Retried turns keep their cache markers; only one-shot calls (compaction summaries, schema repair) send none.
- Interactive start waits up to three seconds for MCP servers before the first request so the tool list is stable from turn one; the system prompt is rebuilt only when the skills catalog actually changes.

### Added

- Desktop turns can hand the CLI a per-run MCP server through `WARM_DESKTOP_MCP_URL` and `WARM_DESKTOP_MCP_TOKEN`. It registers as the trusted `warm` server (tools auto-allowed, never persisted, credentials stripped from every child process), is awaited for up to three seconds before the first model call, and adds a pull-request linking contract to the system prompt. See `docs/desktop-mcp.md`.
- Prompt-cache hit rate in the status line (`cache 93%`), `/cost`, the desktop `context_estimate` event (`cacheHitRate`), and `bun run bench:cache <session.json>` for a recorded session.

### Fixed

- A reply cut off inside its thinking no longer breaks the rest of the session: the rolling cache marker skips `thinking` blocks (which the API rejects with a 400) and lands on the reply's last text or tool block instead, or on the previous message when there is none.

## [0.5.10] - 2026-09-12

### Fixed

- Long paths and project names keep CJK characters, combining accents and emoji intact while fitting the terminal's available columns.
- Desktop tool events distinguish cancellation from execution errors without changing protocol 1 or existing result flags. Shell commands, approvals and built-in tools retain their own outcomes when a later tool stops the run.
- Proof storage and isolated worktrees accept alternate directory casing on case-insensitive filesystems without relaxing symlink, containment or identity checks.
- Installation and prompt examples distinguish package-manager launchers from native installs, explain the Node requirement, and show how to supply a project directory or a complete piped review prompt.

### Maintenance

- The standard test runner discovers TSX tests as well as TypeScript tests, without running curated tests twice.
- Release artifact downloads use an immutable action revision, with a regression guard for action pinning and the current-run candidate download contract.

## [0.5.9] - 2026-09-09

### Fixed

- Empty and truncated model replies now get bounded recovery instead of silently completing without an answer. Recovery respects cancellation, turn limits, and the configured output budget.
- Explicit reasoning-off settings now reach default-on reasoning models.
- Long command results preserve the exit status and the end of the output, including failed tests and security audits.
- Capacity retries honor the server's requested wait, explain the delay, and remain cancellable.
- Updated Hono to resolve three dependency security advisories.

## [0.5.8] - 2026-09-09

### Fixed

- Desktop background commands keep running after a response completes and remain available to later turns. Explicit stop, chat removal, and app shutdown still clean them up. Requires Warm Desktop 1.0.17 or later.
- Background commands no longer inherit the foreground two-minute timeout; an explicitly requested timeout still applies.
- Recoverable upstream stream errors use the existing bounded retry loop. Authentication errors remain terminal, and incomplete tool calls are never executed or replayed.

## [0.5.7] - 2026-09-08

### Fixed

- Releases are signed again, so standalone installs update themselves. The
  updater has required a release signature since 0.3.0, but the pipeline never
  wrote one, so every `warm update` on a curl or PowerShell install declined
  the download. From this release each native package carries a signature made
  in the protected publish step; installs made with the installer script pick
  up new versions on their own from here. Anyone on an earlier standalone
  install runs the installer once more to get here.

## [0.5.6] - 2026-09-06

### Changed

- Warm keeps its settings, sessions and login under `~/.warm`. The first run
  of this version moves an existing `~/.keelcode` there and leaves
  `~/.keelcode` behind as a link, so nothing has to be signed in again and a
  PATH entry from the old installer keeps resolving. Both installers do the
  same move and update the PATH line they wrote.
- Environment knobs are documented as `WARM_*` (`WARM_CONFIG_DIR`,
  `WARM_MODEL`, `WARM_AUTO_UPDATE`, `WARM_REGISTRY`, …). The `KEELCODE_*`
  spellings are still read for one more release.
- A repository's settings live in `.warm/`; an existing `.keelcode/`
  directory is still read when no `.warm/` sits beside it.
- The npm launcher file is `bin/warm.js`. The engine executable inside a
  release tarball is still named `keelcode`; the updater and installers now
  accept either name so a later release can rename it without stranding
  installs that update themselves.
- An install under `/usr/lib/warm` or `/usr/bin` is treated as a system
  package: `warm update` names the package manager instead of writing there.
- Requests to the gateway identify themselves with `x-warm-*` headers, and a
  release's signature travels in the manifest as `warmSignature`. Gateways
  and manifests that still use the `keelcode` spellings are understood, so
  nothing changes for an install that updates from an older version.

## [0.5.5] - 2026-09-06

### Changed

- Show free-launch access, family usage windows, and grace-period deadlines without exposing internal allocation sizing.
- Accept the launch metadata used by desktop and Cloud while remaining compatible with older gateways.

## [0.5.4] - 2026-09-05

### Changed

- `warm usage` and `warm merge` speak window budgets in base windows, with the
  merged budget as an addend: `62%` unmerged, `50% +100%` merged once, and the
  span as `5h +5h`. A percentage of the merged limit re-normalised after every
  merge and made a merge look like a couple of points. Same words as the
  desktop's Usage page.

## [0.5.3] - 2026-09-05

### Fixed

- A model your plan cannot call no longer reads as a spent weekly window.
  On Free, nine of the thirteen models carry no budget, and picking one
  used to answer "Weekly usage limit reached — resets in 7 days" while
  `/usage` showed every meter full. The gateway now answers by name
  ("Kimi K3 is not included in the Free plan — Free includes …"), Warm
  shows it as a plan message rather than a limit, nothing counts down,
  and the note points at `/models` and the plans page.
- `/usage` and `warm usage` name the models your plan does not include
  under the meters ("Not included in Free: …"), and the heading reads
  "Usage windows (% left)" so a full bar is read as budget left, not spent.

## [0.5.2] - 2026-09-03

### Added

- `warm usage --recent`: the account's last twenty requests, newest first,
  with the model, how each ended, tokens in and out, and how long it took.
- `warm usage --history` now asks the gateway for the per-model breakdown
  and prints a "By model" tail when it arrives. The JSON series carries the
  same slices per day, and a new `usage_recent` event carries the recent
  list, so the desktop app's Activity page reads both. Gateways from before
  the breakdown are read exactly as before.

## [0.5.1] - 2026-09-03

### Changed

- `warm merge` lists every open window, not only spent ones, with what is
  left and when each ends; a merge is allowed before a window runs dry.
- Window percentages read against the whole window and never pass 100.
  A merged window's caption grows to `10h`; a spent one reads 0% → 50%
  after the merge. The confirmation names the merged window's length.
- Each merge returns 7 days after it was used, and `warm usage` says so.

## [0.5.0] - 2026-09-02

### Added

- Window merges: a spent 5-hour window no longer means waiting. `warm
  merge <model>` (or `/merge` in a session) fuses the next window into
  the current one — another 5 hours and another full budget on the
  spot. Plans carry a weekly allowance; `warm usage` shows what's left,
  and hitting a window mid-session offers the merge in the same breath
  as the refusal, whenever one would actually help.

### Changed

- The meters' heading reads "Usage windows".

## [0.4.3] - 2026-09-02

### Added

- `warm usage --history`: the account's daily token spend — every
  machine, one line per recorded UTC day, no dollars. The desktop app
  reads the same series for its usage charts.

### Changed

- The `/usage` meters' heading reads "Usage windows"; the message shown
  when a window is spent says usage window too.

## [0.4.2] - 2026-09-02

### Fixed

- `/usage` works against the pools-off gateway. The service retired the
  daily credit pools and now reports none; the previous parser treated
  that as an unreadable reply. With no pools the display leads with the
  plan name and goes straight to the per-model fair-use windows.

## [0.4.1] - 2026-09-02

### Changed

- `/usage` and `warm usage` now read in percentages. Each model gets one
  line with its 5-hour and 7-day fair-use windows side by side, daily
  credits show percent left, and dollar amounts are gone from the
  display. Windows with no budget on the current plan are hidden.

## [0.4.0] - 2026-09-01

### Removed

- The `keelcode`, `kc`, `kcode`, and `keel` commands. `warm` is the one
  installed command; updating removes the old names from package-manager
  and standalone installs alike. Muscle memory is one line away:
  `warm alias add kc` (or any name you like).

### Changed

- The Windows standalone command is `warm.exe`; the curl/PowerShell
  installers now retire 0.3.x-era names on reinstall.

## [0.3.2] - 2026-09-01

### Changed

- The rename is finished: the startup header, sign-in and credential
  messages, doctor, usage hints, notifications, and every other sentence
  the CLI speaks now say Warm. Paths, env vars, aliases, and the engine
  binary are unchanged.
- `/init` writes WARM.md, and the instruction loader reads it at every
  level — existing KEELCODE.md files keep loading, so no repository's
  guide breaks.

## [0.3.1] - 2026-09-01

### Changed

- The app presents as warm everywhere a person reads it: the startup header,
  --help, command hints, update notices, and the npm page (README, homepage).
  Nothing functional moved — paths, env vars, and the engine binary are
  unchanged, and every existing command keeps working.

### Fixed

- Device login accepts warm.run approval and API URLs, so signing in against
  the new domain works the same as keelcode.ai.

## [0.3.0] - 2026-09-01

### Changed

- The npm package is now `@warm-ai/warm`. The engine, the `keelcode`/`kc`
  aliases, your login, and your settings are unchanged — only the install name
  moved: `bun add --global @warm-ai/warm`. The old `@keelcode-ai/keelcode`
  package stops receiving updates.

### Added

- `warm` is now an installed command, joining `keelcode`, `kc`, `kcode`, and
  `keel` — the name the docs and the warm.run landing page teach.
- `/usage` (and `keelcode usage`) now show the per-model 5-hour and 7-day
  fair-use windows next to the daily credits: remaining of limit, and either
  "resets in 2h10m" or "unused" for a window that has not started.
- Hitting a fair-use window no longer reads like an outage. The 429 becomes a
  clear message naming the window and exactly when it resets, the client does
  not burn transport retries against a refusal that cannot change, and the
  TUI adds a note that other models are unaffected.

- The self-updater now requires an ed25519 release signature over the exact
  binaries it is about to install, verified against a public key baked into the
  client. A compromised registry that serves a malicious tarball with a matching
  checksum is refused, because it cannot forge a signature made by the offline
  release key.
- A `--version` smoke test runs the freshly staged binary before the live
  `keelcode` aliases are re-pointed. A download that stages cleanly but cannot
  execute (wrong microarchitecture, missing library, subtle corruption) is now
  discarded instead of becoming the live command.
- `keelcode update --rollback` returns the install to the previous version still
  on disk and pins auto-update there; `keelcode update --unpin` re-enables
  updates afterwards.

## [0.2.0] - 2026-08-16

### Changed

- The CLI now completes a short client handshake with the Keelcode gateway on
  login and on first use, which links this machine to your account and keeps
  it eligible for API access. Older releases will be asked to update.

### Added

- `keelcode login` reports immediately when a machine has reached its account
  limit, instead of failing later at the first prompt.

## [0.1.19] - 2026-08-16

### Fixed

- `/bypass` (and `--yolo`) is now actually prompt-free. The workspace-escape
  check ran before the bypass check, so any file access outside the project
  directory still prompted in bypass mode. With an approver present
  (interactive TUI, or headless with `--approval-protocol`), bypass now
  crosses the workspace boundary silently. Deny rules stay absolute, and
  approver-less contexts (subagents, plain headless) still fail closed
  outside the workspace.
- Answering "always allow" on an outside-the-workspace prompt now sticks. The
  persisted rule was never consulted on that path, so the same access re-asked
  forever; a matching allow rule now counts as the explicit approval the
  boundary demands, in every permission mode.
- On Windows, rules persisted by "always allow" for file tools are stored with
  `/` separators. The previous escaped-backslash form could never match the
  slash-normalized candidate path, so the rule was dead on arrival.

## [0.1.18] - 2026-08-16

### Fixed

- A model the gateway currently serves is no longer remapped away by the
  retirement table. The Claude aliases withdrawn on 2026-08-01 came back as
  staff-only models, and the old unconditional remap silently sent a staff
  user's `claude-fable-5` or `claude-sonnet-5` run to `gpt-5.6-luna`. The
  remap now consults the live `/v1/models` catalog and only migrates
  selections the gateway no longer answers for; offline, the conservative
  migration still applies.

## [0.1.17] - 2026-08-13

### Changed

- A failed request now shows what the server actually said. Asking for a model
  that does not exist reads `Model not found — Choose a model returned by
  /v1/models.` instead of a paragraph about the endpoint having no Messages API,
  followed by the raw JSON body and a request id.
- `--help` is grouped into ACCOUNT, WORK, EXTEND and MAINTAIN, so the common
  path is visible without reading a flat list of twenty-seven commands.
- A first run is greeted instead of reported as a failure: with no session,
  `keelcode` prints a short welcome and the one command to run. The exit code
  stays non-zero, so scripts and CI still see a failed invocation.
- Signed-out runs report the error code `not_logged_in` rather than the
  catch-all `config`, so `--output-format json` and desktop `stream-json`
  clients can tell "sign in" from "your settings are wrong". This is a
  behavioural change for anything matching on the old code.

### Fixed

- A 404 that is not a wrong base URL is no longer diagnosed as one. The CLI no
  longer tells you to check your connection and sign in again when the real
  problem was the model name.
- Error text on stderr is no longer dimmed. Dimming put the one line you have
  to read at the same weight as tool chatter, and below comfortable contrast on
  some terminal themes.
- An oversized or non-JSON error body is bounded to 300 characters instead of
  filling the terminal, and internal error codes no longer appear in the
  sentence shown to the user.

### Performance

- Subcommands no longer load the interactive TUI. React, Ink, the tool
  registry, MCP, the extensions runtime and the per-command CLIs are now
  imported where they are used, so `keelcode status` starts in about 30 ms
  rather than 80 ms and peaks near 62 MB rather than 100 MB.

## [0.1.16] - 2026-08-07

### Added

- Hosted requests now carry a browser fingerprint and identify the CLI build,
  so the gateway can tell client versions apart when a rollout needs to give
  newer builds different guidance.

### Fixed

- A request rejected because the account needs browser verification now shows
  what to do — the verification URL and the steps — instead of the raw error
  body from the gateway.

## [0.1.15] - 2026-08-06

### Added

- The `/models` picker is now searchable: type to fuzzy-filter the catalog,
  see highlighted matches, and keep using the arrow keys to choose a model.
- `/update` checks for a new version from inside a session and starts the
  install (or names the exact package-manager command, on managed installs).
- `/changelog [version]` shows release notes embedded in every install, and
  the one-time notice after an automatic update now points to it.
- `keelcode doctor` gained an Updates row with the install channel, current
  version, last registry check, pending version, and last update failure.

### Fixed

- A fully spent git-context probe timeout now stops immediately instead of
  being interpreted by Node as no timeout, which could block the TUI.

## [0.1.14] - 2026-08-04

### Added

- Keelcode keeps itself up to date. An interactive session checks the registry
  in the background at most once every six hours; curl-script installs
  download the new version, verify its sha512 against the registry manifest,
  and switch the `~/.keelcode/bin` links to it atomically — the next launch
  runs the new version and says so once. Package-manager installs are never
  written to behind the manager's back: they get a one-line notice on the
  startup screen instead.
- `keelcode update [--check]` — the same update in the foreground, on demand.
  On npm-managed installs it runs the detected package manager (npm, pnpm,
  yarn, or bun) rather than touching `node_modules` itself.
- `autoUpdate` setting (default `true`); `KEELCODE_AUTO_UPDATE=0` and CI
  environments disable the background updater without touching settings.

### Changed

- Startup got measurably lighter: syntax highlighting and the MCP SDK moved
  off the launch path (main chunk 3.0MB → 1.45MB, first paint ~1.0s → ~0.6s,
  idle RSS ~155MB → ~110MB). Sessions that never use an MCP server never load
  the SDK at all.
- Long sessions hold memory instead of growing it: the read-before-write
  cache and the /rewind checkpoint store are now LRU-bounded, losing only
  history depth, never correctness.
- Thinking-phase streaming stopped committing a full UI frame per SSE event,
  and live-tail rendering skips re-highlighting pathological open code blocks
  past 20k characters.
- Dropped unused dependencies (`marked-terminal`, `lodash-es`) and bumped
  four transitive security pins (`fast-uri`, `brace-expansion`, `hono`,
  `ip-address`) past fresh advisories; `bun audit` is clean.

### Fixed

- Tool calls from open models with structurally malformed arguments (a JSON
  array sent as a string, a lone value where a list was wanted, `null` for an
  omitted optional) are repaired at the exact paths schema validation names,
  instead of surfacing as confusing runtime errors inside the tool. Valid
  input is never touched: validation runs first, repair only on failure.
- `/usage` reported "Keelcode usage is temporarily unavailable" against a
  healthy endpoint. The hosted response schemas rejected unknown keys, so when
  the gateway added a `model_quotas` field every installed CLI refused to read
  an otherwise valid `200`. Response schemas now ignore fields they do not
  recognise while still validating every field the CLI reads, so a future server
  addition cannot break an older client.
- A `200` the CLI cannot parse and a genuine HTTP failure no longer share one
  message. An unreadable response names itself a client problem and points at an
  upgrade; an outage reports its status code.
- Hosted command failures are labelled with the command that failed rather than
  `Authentication failed`, which sent signed-in users to re-authenticate over
  errors that had nothing to do with authentication.

## [0.1.13] - 2026-08-02

### Added

- Fuzzy command palette spanning commands, files, sessions, models and themes,
  ranked by match quality blended with recency and frequency.
- Changes pane listing the files a session has touched with per-file `+/-`
  counts, expandable to a scrollable inline diff.
- Session HUD: context pressure, burn rate, live tokens/sec, time-to-first-token
  and a turn clock, each appearing only once it has something to report.
- Live git context on the startup screen and the header — branch, dirty count,
  ahead/behind and the tip commit.
- Startup suggestions adapt to repository state and are keyboard-navigable:
  arrows move the caret, Enter runs the row, Tab loads it into the composer.
- `scripts/render-frames.ts`, a frame-capture rig that writes the real ANSI the
  UI paints, one file per state, for visual review.

### Changed

- The default `space` palette carries a single warm accent; body text stays
  greyscale so a dense transcript remains readable over long sessions.
- The composer is a cmdline rather than a bordered panel, and the chrome beneath
  it is one hairline rule plus one status row.
- Slash-command output renders in full. Long notes still collapse, but output
  the user explicitly asked for is exempt — `/resume` was hiding the very list
  it told you to choose from.
- The shortcut bar is permanent again, folded into the status row. Progressive
  hints ride beside it rather than replacing it.
- The context meter no longer renders at zero usage.

### Removed

- The live World Cup score surface: `/football`, its `football` config block,
  the header segment and the supporting modules. Existing settings files keep
  loading — the key is ignored, not rejected.
- The character mascot and its animated faces, replaced by a typographic mark.
- The live row's dot-matrix loader, label shimmer and rotating status word. The
  elapsed clock reports the same fact more precisely.

### Fixed

- Enter no longer runs a fuzzy-matched command when the typed text merely
  resembles one; a match now requires an explicit prefix, alias or selection.
- Branch names and commit subjects are stripped of control characters and
  bidirectional overrides before reaching the terminal.

## [0.1.12] - 2026-08-01

### Added

- Refresh the model catalog from the authenticated endpoint when the
  interactive picker opens, so newly available models appear without
  restarting the CLI. A failed or empty refresh keeps the last usable list.

### Changed

- Render streaming replies from a committed-prefix cache that re-parses only
  the unstable tail instead of the whole accumulated message.
- Memoize transcript turns so a rebuild costs the new turns rather than the
  entire session history.
- Emit one text node per viewport row so scrolling stops retaining a
  multi-kilobyte interned string for every scroll position.
- Share one clock between the spinner tick and the stream flush, and play the
  splash blink as a bounded greeting instead of an endless idle timer.

### Fixed

- Open the model picker even when only one model is known, instead of
  refusing with a note.
- Restore the blank line between a heading and the block after it while a
  reply streams. marked 18 emits a block separator after headings, which the
  incremental renderer had been suppressing.

### Dependencies

- Update chalk to 6, marked to 18, nanoid to 6, `@modelcontextprotocol/sdk` to
  1.30, OpenTelemetry to 0.221/2.10, and the TypeScript type packages.

## [0.1.11] - 2026-07-27

### Fixed

- Keep bypass mode live for the rest of an in-progress turn after it is enabled
  from a permission prompt, so later tools do not ask again.
- Migrate retired Kimi and GLM Nitro selections to their current model aliases.
- Cancel silent or interrupted provider streams promptly instead of leaving the
  CLI waiting for the full request timeout.
- Bound whole-turn recovery to three attempts and avoid multiplying transport
  retries after a request fails before streaming begins.
- Make Escape and the first Ctrl+C immediately interrupt an active turn while
  keeping a second Ctrl+C available as the forced-exit path.

## [0.1.10] - 2026-07-27

### Added

- Add isolated mission workspaces with verification, independent review,
  recoverable refs, and conflict-safe integration.
- Add role-specific worker, reviewer, synthesizer, and scout model selection
  with `/crew` controls and session-model fallback.
- Add the compact Rover companion and live mission fleet panel without keeping
  animation timers active while the interface is idle.

### Fixed

- Keep command callbacks on the latest mission, model, and companion state.
- Probe independent Git metadata concurrently and avoid duplicate mission
  updates during integration.

## [0.1.9] - 2026-07-26

### Fixed

- Run the privileged npm OIDC publish job on a GitHub-hosted runner, as
  required by npm trusted publishing.

## [0.1.8] - 2026-07-26

### Security

- Prevent recursive fatal-error logging from exhausting the host disk when
  stderr is broken. Interactive fatal errors now write one redacted, bounded,
  owner-only diagnostic and terminate non-zero; existing oversized crash logs
  are truncated on startup, and symlinked crash-log targets are rejected.
- Pin `brace-expansion` to 5.0.8 to remove the high-severity unbounded-expansion
  denial-of-service advisory from the release toolchain.

## [0.1.7] - 2026-07-23

### Fixed

- Fix package-manager installs on Windows. 0.1.6's launcher used a `#!/bin/sh`
  shebang, which Windows shims (and Bun) resolve to `/bin/sh` — absent on Windows
  — so `npm`/`pnpm`/`yarn`/`bun` global installs produced a `command not found`
  / `interpreter "/bin/sh" not found` error. The launcher now uses a Node shebang,
  which every package manager provides a working shim for on all platforms.

### Changed

- The globally installed command now runs under **Node 18+** (which npm, pnpm,
  yarn, and Bun all provide). A machine with Bun but no Node uses the native
  `install.sh` / `install.ps1` script, which needs no runtime.

## [0.1.6] - 2026-07-23

### Added

- Add one-line native installers, `install.sh` (macOS/Linux) and `install.ps1`
  (Windows), that download the platform binary straight from npm, verify its
  checksum, install under `~/.keelcode`, and — unless `--no-modify-path` /
  `-NoModifyPath` is passed — add Keelcode to `PATH`. No package manager, Node,
  or Bun required.

### Changed

- The globally installed launcher now runs under **Node 18+ or Bun**, whichever
  is present, so `npm`, `pnpm`, `yarn`, and `bun` global installs all work on
  machines that do not have Bun.
- Remove the `engines` constraint from the published package so no package
  manager refuses to install on an older or non-Bun runtime.
- A standalone (curl-installed) executable now finds its ripgrep sidecar next to
  itself, so it needs no `KEELCODE_RG_PATH` or bundled npm dependency.

### Fixed

- `command not found` after installing with `npm`/`pnpm`/`yarn` on a machine
  without Bun, and pnpm's engine-strict refusal to install the previous package.

## [0.1.5] - 2026-07-20

### Fixed

- Make the trusted project settings path test portable across Windows and POSIX CI runners.

## [0.1.4] - 2026-07-20

### Added

- Add `kc`, `kcode`, and `keel` as packaged aliases for the `keelcode` command.
- Add safe custom command aliases with `keelcode alias setup|add|list|remove`.
- Show client-observed TTFT and provider-token TPS in interactive and headless
  output without mixing tool time into model throughput; keep interactive
  context and performance telemetry immediately above the input box.
- Make the CLI hosted-only: `keelcode login` now owns the API origin and bearer
  session, while customer provider keys, custom endpoints, and `ANTHROPIC_*`
  transport configuration are ignored.
- Add `/ship`, an objective-bound deliverable workflow whose runtime withholds
  completion until a persisted passing `Verify` receipt, a read-only final
  review, the cited receipt ID, and a stabilized Git fingerprint all agree.
- Discover layered `AGENTS.md` and `KEELCODE.md` instructions from the Git root
  to the active directory with source provenance and bounded, symlink-safe reads.
- Add stable `json` and streaming `jsonl` headless output formats with explicit
  terminal outcomes and automation-friendly exit codes.
- Add lease-checked detached Git worktree primitives for repository-state
  isolation without claiming operating-system sandboxing.
- Add standalone `proof list/show` and `worktree create/status/cleanup` commands
  so automation can inspect receipts and manage isolated runs without an API key.
- Add an opt-in-aware World Cup match surface with a startup Match Centre,
  compact in-session scores, scorer-specific goal takeovers, and `/football`
  controls. Live provider traffic stays in the Keelcode backend.

### Security

- Treat checked-in `.keelcode/settings.json` and `.keelcode/settings.local.json`
  as untrusted input: repositories can add restrictions and presentation
  preferences, but cannot enable prompt-free modes, auto-allow tools, inject
  environment variables, or launch MCP processes.
- Store explicitly approved per-project MCP and allow settings in an owner-only
  directory outside the checkout, and pass MCP processes a minimal environment
  instead of every credential held by the CLI.
- Require an explicit permission decision for filesystem access outside the
  active workspace, including symbolic-link escapes, and make delegated agents
  inherit the parent permission gate and deny rules.
- Pin each WebFetch HTTPS connection to the public address that was validated,
  revalidating every redirect to prevent DNS-rebinding SSRF.
- Require approval for Git commands that write through `--output` or `-o`.
- Redact and cap persisted verification output, store receipts and worktree
  leases with owner-only POSIX permissions, and bound gate count and execution time.
- Make Verify approvals fully scrollable and auditable, hard-limit observed
  output, show exact executable command text, reject destructive checks and
  common background syntax, detect residual children, and anchor local receipt
  bytes per ship run.
- Hash actual tracked/untracked worktree bytes and submodules independently of
  Git stat-cache, filter, assume-unchanged, and skip-worktree behavior; isolate
  Git subprocesses from ambient `GIT_*` injection.
- Reject symlinked private receipt roots and revalidate directory/file identity
  across receipt publication and reads.
- Block WebFetch access to unsafe URL schemes, credentials, local/private
  networks, and unsafe redirect targets.
- Store user transcripts, mission artifacts, caches, and crash logs with
  owner-only POSIX permissions.
- Clarify that `bypassPermissions` is an explicit no-prompt mode while deny
  rules and tool hard denials remain enforced.

### Fixed

- Keep installation and normal startup independent of Nerd Fonts; failed,
  skipped, unsupported, and manual-only setup paths now remain on safe Unicode.
- Extract the optional pinned Nerd Font archive inside Keelcode instead of
  depending on `tar` or PowerShell, and verify its SHA-256 before installation.
- Reject Windows UNC paths before POSIX normalization can hide their form.
- Run the npm launcher with Bun so globally installed commands work on Bun-only
  machines without a `node` executable.
- Avoid misclassifying an already-exiting Windows verification child as a
  residual background process when no process tree was actually terminated.

### Build

- Standardize development, security auditing, packing, and CI on Bun.
- Replace the unpublished JavaScript npm artifact with a small launcher and
  exact platform-suffixed versions of the single `keelcode` package name for
  macOS, Linux, and Windows on x64/arm64.
- Fail release packing when source files, source maps, private metadata,
  upstream endpoints, or secret-shaped credentials appear in an npm artifact.
