Skip to content

Cairn v2.5.9

Choose a tag to compare

@github-actions github-actions released this 25 Jul 17:39
· 988 commits to main since this release

v2.5.9

Added

  • Merge one project into another. Each project's ⋯ menu in the sidebar now has a Merge into… action. Pick a destination project and everything in the current project — notes, tasks, board columns, and Idea Flow nodes — is moved into it, then the emptied project is removed. Cards land in the destination's matching board column by type (Todo → Todo, Done → Done, …), and any custom columns are recreated in the destination so nothing is lost. This is the clean way to consolidate duplicate or overlapping projects (for example, two projects that ended up with the same name). The move is transactional and syncs to your other devices.

  • Save and switch between AI providers. The AI settings (Settings → AI, Cloud / Local API) now let you save several named OpenAI-compatible connections — each with its own name, base URL, API key, and default model — and switch the active one from a dropdown instead of retyping your endpoint every time you change services. Add a provider for OpenAI, another for OpenRouter or Together, one for a local Ollama / LM Studio server, and flip between them in a click; the active provider fills the endpoint, key, and model fields below and is used everywhere the app talks to the cloud/local API (chat, inline AI, PRD writer, summaries). Add, edit, and delete providers inline. Your existing endpoint carries over unchanged — nothing to reconfigure. The On-Device Llama option is unaffected and keeps its own toggle.

  • The Coding Agent shares the same saved providers. The Coding Agent settings (Settings → Agents → Cairn Coding Agent) use the same list of saved providers as AI Chat — add or edit a connection once and it's available in both places. The agent keeps its own active selection, so you can point it at a higher-capacity model while chat stays on a cheaper one. Deleting or editing a provider updates both consistently.

Changed

  • Cleaner note toolbar. In the note view, the word count / reading time (e.g. "270 words · 2 min read") has moved out of the top toolbar and now sits just below the note title, next to the tags. The remaining toolbar actions have been regrouped — the Pin, similar-notes, and distraction-free icons are clustered together, and the Spawn tasks / PDF buttons and the "last updated" timestamp no longer wrap their text mid-word — so the bar stays legible when the notes pane is narrow instead of collapsing into a jumbled multi-line mess.

  • Cleaner endpoint settings. Now that saved providers cover switching endpoints, the old OpenAI / Ollama / LM Studio quick-preset pills under the Base URL field have been removed from both the AI settings and the Coding Agent settings — pick or add a saved provider instead. The provider picker now uses Cairn's standard dropdown menu (matching the model picker and other settings) instead of a plain OS select box, and shows each provider's model beneath its name. The standalone Base URL / API Key / Model rows are gone too — a provider's connection is edited entirely inside its Add/Edit form.

  • Consistent model picker everywhere. The model selector from the in-chat quick settings — a searchable dropdown of the endpoint's models with a Refresh action and a "Custom model…" option for anything the endpoint doesn't list — is now used across the app: in Settings → AI, in the Coding Agent settings, and in the saved-provider Add/Edit form. This replaces the older text box + Fetch button + wall of model pills, so choosing a model looks and behaves the same in every place.

  • The model dropdown only shows real models. It no longer pre-fills with a hardcoded list of guessed model names. Instead each endpoint's model list is fetched and cached per endpoint (base URL + key), so it appears instantly the next time you open the picker; if nothing is cached yet, it fetches once automatically. You can still Refresh to re-query or enter a custom model id. Cached lists refresh after a week.

  • Pick the model for the active provider — separately for Chat and the Agent. The connection (endpoint + key) is shared across your saved providers, but the model is now a per-surface choice: there's a Model picker right under the provider switcher in both Settings → AI and the Coding Agent settings, so you can change the active provider's model without opening its Edit form. Because the choice lives with each surface, AI Chat and the Coding Agent can run different models on the same provider (e.g. the agent on a larger model, chat on a cheaper one). Each provider still has a "default model" that seeds a surface the first time it selects that provider.

Security

  • AI provider API keys now live in the OS keychain. Keys for the cloud/local AI providers (chat + coding agent, one per saved provider) are stored encrypted in your operating system's keychain (macOS Keychain, Windows DPAPI, Linux libsecret) — the same secure store already used for MCP/service credentials — instead of in plain text in the app's settings files or local storage. The app only ever holds an opaque reference token; the real key is decrypted in the background process at request time, and never written to disk unencrypted, kept in the window, or exposed to web content. Existing keys are relocated into the keychain automatically on first launch and scrubbed from the old plaintext locations. Fetching an endpoint's model list now also happens in the background process, so your key isn't handled by the in-app browser layer. The API-key field shows only whether a key is stored ("•••• stored — type to replace"), never the saved key.

Fixed

  • AI settings no longer crash with an infinite render loop. Opening Settings → AI could throw "The result of getSnapshot should be cached to avoid an infinite loop" because the saved-providers list was rebuilt on every render. The provider switcher now reads a stable list reference, so the settings panel renders cleanly.
  • Local endpoints work with 127.0.0.1, not just localhost. Pointing an endpoint at http://127.0.0.1:<port> (instead of http://localhost:<port>) previously failed to fetch models or test the connection — the app's content-security-policy allowed localhost but not the equivalent 127.0.0.1 loopback address (CSP matches the origin string literally, so the two are treated as different). Both loopback spellings are now allowed, so a local server (Ollama, LM Studio, llama.cpp, …) works with whichever address you use.
  • PDF exports use a page footer instead of a big title banner. Exporting a note to PDF (desktop) no longer stamps a large title heading at the top of the first page. Instead every page carries a small running footer with the note title on the left and the page number ("2 / 5") on the right, so long notes are easy to navigate and the content starts at the top of the page. (Mobile PDF exports keep a compact title heading, since the mobile print engine can't render running footers.)
  • Dragging a task to another project now sticks. Moving a card to a different project (via drag-and-drop onto a project in the sidebar) updated its column but silently failed to persist the project/workspace change — so on the next board refresh or device sync the card jumped back to its original project. Cross-project card moves (and their undo/redo) now persist the project, workspace, and column together, matching how notes already move between projects.