OpenKnowledge

v0.48.0

Released Aug 4, 2026. Part of the OpenKnowledge changelog.

Minor Changes

  • Comment on code blocks and table rows.

    Dragging a selection across a code block and asking for a comment failed with "The quoted passage is not in the document." A fenced block's backticks were already invisible to the anchor matcher, but the language tag after them was not, so any selection crossing into or out of a ```ts block could not be located. The same gap closed for thematic breaks, setext heading underlines, table delimiter rows, table cell boundaries, and task-list checkboxes — every source line that renders as nothing.

    A code block's sparkle button used to hand the whole block straight to a fresh agent session, with no way to file the note for a later batch. It now opens the same composer the text toolbar's Ask AI opens, offering both. A selection already inside the block is what the comment is about; with nothing selected, the block itself is.

    Table rows and columns can now be selected by clicking the handle above a column or beside a row, so a row can be commented on, copied, or deleted as a unit. Previously the handles only opened a menu and the sole way to select cells was sweeping the pointer across them.

    Commenting on a selected row quoted a single cell. The composer read the selection's first range, which for a table selection is the anchor cell alone, so picking a three-column row and commenting on it filed the comment against one word of it.

  • The 15 starter-pack skills drop their generated open-knowledge-pack-… names and take marketplace short names, taken from SKILL.md frontmatter: the orientation skills become note-taking (was …-plain-notes), writing-workflow (was …-writing-pipeline), personal-crm (was …-entity-vault), okf-knowledge-base (was …-okf), and worldbuilding, codebase-wiki, knowledge-base, software-lifecycle (prefix dropped); the member skills become frame-a-proposal, record-a-decision, write-a-spec, review-a-design, write-a-postmortem (prefix dropped) plus research-with-sources (was …-knowledge-base-research) and consolidate-notes (was …-knowledge-base-consolidate). Every shipped skill now carries metadata.author/metadata.repository.

    Skills you already have are left exactly as they are. Nothing is renamed on your disk, your .ok/skills-lock.json is not rewritten, and no boot pass touches your project. These are project-level skills that normally live in your git repo, and silently renaming one you are already using would show up as an unexplained diff for you and for everyone who pulls. An existing install keeps its current name, keeps working, and still updates from source: "Update" resolves the old name to the renamed bundle in the mirror. Re-seeding a pack recognizes the skill you already have and will not author a second copy of it under the new name. Only newly installed skills use the new names.

    Note for maintainers: this is the reason there is no migration. If you later want existing installs on the new names, it needs to be something a user opts into and can see, not a rename that happens under them at boot.

    Four Claude Code marketplace plugins are renamed to match their published skill names: entity-vault becomes personal-crm, okf becomes okf-knowledge-base, plain-notes becomes note-taking, and writing-pipeline becomes writing-workflow. The old /plugin install <name>@open-knowledge-skills commands keep working for one more release: the four old handles remain in the marketplace as aliases, marked outdated and installing the same skills as the entry that replaced them. Switch to the new names — the aliases are removed in a later release. The other seven plugins keep their names, but every plugin's skill paths move as the published repo regroups into skills/core/ and skills/starter-packs/. ok seed --pack <id> flags are unchanged.

    Switching a project's OpenKnowledge skill off in Settings now sticks. It previously came back on the next open, because the project-open sweep recreates the skill for any editor already wired for OpenKnowledge and nothing recorded that you had turned it off. Your choice is now remembered per project, on your machine only, so it does not follow the repo to teammates. A project where you never expressed a choice still gets the skill seeded as before.

    Installs of OpenKnowledge's own skills are now counted on skills.sh wherever they actually happen. Previously only some routes reported, so most installs were invisible: creating a project, the desktop seeding its built-in skills on first launch, opening a wired project that had no project skill, switching the skill on in Settings, installing for Claude Desktop, and importing our own repo by any route other than the Explore tab all counted nothing. What is NOT counted: an app launch that installs nothing, reopening a project, a failed write, a skill bundle you switched off, and — unchanged — any third-party repository you typed yourself. Reporting still honours telemetry.skillInstallReports.enabled, DO_NOT_TRACK and DISABLE_TELEMETRY.

    A skill installed into a project is now counted once per project rather than once per machine, because each project gets its own copy in its own editor directories. The project path is used only as a local key to avoid double counting and is never sent.

    Also: seeding now reports name collisions with your own skills instead of silently skipping, and rejected install reports are logged. skills.sh install counters for the old names reset under the new names.

    Install reporting to skills.sh now retries after a rejection. Previously a report the collector declined was recorded as sent and never retried, so any install rejected while a newly renamed listing was still being indexed was lost for good. Only rejections the collector application issued (400, 404, 410, 422) are retried: a 5xx, a 429, or an edge 403 may have been raised after the event was already counted, so those keep their claim, as do reports the collector never received. A flaky network or a struggling collector can never inflate a count.

  • Register <Toggle> as a canonical block descriptor — Notion-style vocabulary alias for <Accordion>. Same props (title, defaultOpen, icon, description, id, name, children), same expand/collapse component; the two descriptors serialize under their own JSX names so <Toggle> and <Accordion> both round-trip without rewriting. /toggle in the slash menu now inserts a first-class <Toggle> instead of routing to <Accordion>.

  • Add a resizable vertical split workspace with one shared header for tabs and workspace actions. Drag tabs or sidebar files into pane edges, or use tab actions, to create and populate panes. Pane sizes, open tabs, and focus restore with the project session, while editor actions and the right document and agent panels follow the focused pane below the header.

    Polish tabs across single and split panes with stable sizing, horizontal overflow scrolling, edge fades, clearer drag feedback, and shared actions for documents, folders, and assets. Existing preview tabs keep their replaceable and pinned behavior while supporting the same drag, split, and overflow interactions as regular tabs.

Patch Changes

  • Queued review comments can now be sent into the agent thread you already have open. The agent panel's composer carries the same + Comments chip as the Ask AI composer: click it to attach everything waiting in the queue, expand it to review or uncheck individual comments, and send.

  • You can now pick one color theme for light mode and a different one for dark mode. Every tile in Settings → Plugins → Themes carries a sun and a moon: press the sun to make that palette your light theme, the moon to make it your dark theme, or both to use it either way. Whichever mode you're in — including when your theme setting is System and your OS decides — the matching palette applies, and it swaps as the appearance changes without touching your config.

    Any palette can sit in either slot; it still forces its own light/dark variant, so choosing a dark scheme as your light-mode theme does what it says. Existing setups are unchanged: a single palette chosen before this release applies to both modes until you pick a new one.

    Switching themes is also faster than it was. Repainting the terminal palette used to re-measure each color separately against styles the switch had just invalidated, which stalled the window for a moment on every change; the whole palette is now measured in one pass and refreshed once per frame.

  • A crash report that arrives without its crash dump now explains itself. Until now, a bug report whose crash dump was missing looked identical whether you had unchecked the box on purpose, the app had found no crash dump it could prove was its own, or the dump had been cleaned up in the moment between choosing to send it and the report being packed. All three arrived as an empty slot, and there was no way to tell them apart afterwards, so a report that should have carried the most useful evidence about a crash could not even be shown to be missing it. The app now writes a line into its own log recording which of those actually happened, and, when a dump was looked for, how many crash dumps were skipped because they belonged to a helper process or could not be read at all. The line is written before the report is packed, so it travels inside the very report it explains rather than turning up in the next one.

    A dump that was on hand is the one case that line cannot settle by itself, since whether it survived being packed is only known once packing is done. The report answers that on its own: the line says a dump was going in, and the report either carries it or it does not. Relatedly, a dump cleaned up mid-pack used to abort the whole detailed report; it is now recorded as a dump that did not make it, and the rest of the report is still produced.

    This changes nothing about what gets attached. The crash-dump checkbox behaves exactly as before: it appears only when a crash dump for that crash is on disk, comes pre-checked because that dump is the evidence the report exists to carry, and can be unchecked. This only observes the choice already being made. The record names no file: a crash dump is raw program memory that cannot be scrubbed, and its filename identifies the individual crash, so the line carries only the outcome, the counts, and a size in bytes.

  • A bug report filed after a crash now names the version that actually crashed. The report is composed by the session that notices the crash on the next launch, and if an automatic update landed in between, that is a different build from the one that died. Reports were being attributed to the new build, which in some cases had only been running for a fraction of a second before it was asked to explain a crash it never saw. The crashed version is now read from the crash dump's own metadata, or from the marker the previous session left behind, and it appears in the report alongside the version you are running now. Seeing the two together is the point: when they differ, an update happened between the crash and the report, and the crash belongs to the older build. When a report cannot establish which version crashed, it says nothing rather than guessing, so a stated version can always be trusted.

  • Agents can now reach a project opened from a development build of the desktop app. The dev launch path bound its server to localhost, which macOS resolves IPv6-first — so the server listened on [::1] while the MCP server, the agent keepalive, and ok ps all dial numeric IPv4 loopback. Nothing was listening at the address they called, so tool calls against that project failed and the agent's connection retried forever, even though the editor window itself worked normally. Development launches now bind the same numeric IPv4 loopback address that ok start already uses, so a signed release and any number of local development builds can run side by side with each project reachable by its own agent.

  • Hovering an editor tab now shows the file's full path immediately in an app-styled tooltip, so two tabs with the same file name — SPEC.md from two different folders, say — are no longer indistinguishable. Asset and skill-bundle-file tabs now disclose their paths too, and the tooltip stays out of the way while you drag a tab to reorder it.

  • A graphics glitch that fixed itself no longer asks you to file a bug report. Chromium draws each window from a separate process and replaces that process on its own when it dies, restoring the picture in about a second — usually before there is anything to notice. OpenKnowledge was treating every one of those deaths as a crash worth interrupting you over, so the report dialog could arrive moments after nothing visibly happened, leaving you to describe a failure you never saw. One such report was filed for a session that had recovered a full second before the invitation appeared and then ran normally for the rest of its life.

    Those deaths are now recorded quietly instead. If the graphics process keeps dying — three times inside five minutes, the point where it is no longer recovering and the window really does degrade — the invitation appears exactly as before. Either way every death is still written to the log, so a report filed for something else still carries the whole picture, and a recovered one says in as many words that it was suppressed and why.

  • The project server now exposes standard health-check endpoints: GET /healthz answers 200 as soon as the listener is up (process liveness), and GET /readyz reports 503 while the project runtime is still initializing, then 200 with a degraded list naming any subsystems that failed to start. The moment shutdown begins, /readyz flips to 503 with status draining so probe-driven routers stop sending traffic before the listener closes. Both endpoints are exempt from the Host/Origin admission checks so container orchestrators and reverse-proxy health probes (which send IP Host headers and traverse proxies) work without configuration.

  • Agents in the in-app agent panel no longer answer with a localhost preview link to the app you are already looking at. After creating or editing a doc, an agent would sometimes end its reply with a bare http://localhost:<port>/#/... URL instead of just bringing the doc up on screen. The steer that prevents this already existed, but it only recognized agents running in the desktop app's built-in terminal, so the agent panel never received it and fell back to handing over a raw URL. Every way a panel agent reaches OpenKnowledge now carries the same signal, and the guidance is explicit that the URL should not be pasted into a reply when you are already in the app. Agents outside the app are unaffected and still get a URL they can open.

  • Images with a missing src (404, broken URL, deleted asset) now render a visible placeholder card — icon + "Image failed to load" + the truncated path — instead of leaving the reader with the browser's default 16×16 broken-image glyph. Same behavior on cached-broken images that fail before onError fires (detected via img.complete && naturalWidth === 0). Placeholder stays inline (<span>) so it's safe inside <p> and inside <Zoom wrapElement="span">.

  • Typing a markdown shortcut around an image, wiki link, tag, inline math, footnote reference, or inline JSX no longer breaks the editor. Closing a shortcut whose text spanned one of these — typing the last * of **see ![img](a.png) here**, for example — computed a replacement range that was five positions too wide per object, so the keystroke either threw and was lost, or, in a longer paragraph, silently rewrote the wrong span and ate the text in front of it. Inline JSX was skewed by an amount that grew with its own length, so a short one could work and a realistic one could not. Every inline object now reports its width to the shortcut matcher as the single position it actually occupies, which fixes the range for every shortcut rather than any one of them.

  • Managed Node.js and uv runtimes now install correctly when the temporary directory and OpenKnowledge home are on different filesystems. Runtime archives are staged beside their destination for an atomic same-filesystem rename, and concurrent launches adopt an already completed install instead of removing it.

  • ok init --no-skills now only skips installing the built-in skills for that run. It no longer turns them off across your whole machine.

    Previously the flag recorded a machine-wide opt-out and deleted the built-in skills from your user-global skill folders, so running it once in a throwaway directory disabled them for every project. The recorded opt-out also persisted, so the desktop and ok repair-skills sweeps removed the skills again on every launch — reinstalling them by hand appeared to work until the next launch deleted them.

    The flag now installs nothing and records nothing. Skills already on your machine are left alone. If you want to turn the built-in skills off for real, use the Settings toggle or the first-launch prompt, which is where that choice was always meant to live. If you have already been affected, re-enable them from Settings or run ok init without the flag.

  • Side panels no longer get stuck in a non-resizable state. Two separate faults could wedge them, both triggered by a drag that the browser ended with a cancel rather than a release — which happens when a scroll or zoom gesture takes over, or the system invalidates the pointer mid-drag.

    The document panel and agents column tracked whether a drag was in flight but only ever cleared that flag on pointer release. After a cancelled drag the flag stayed set, and while it was set the layout correction that opens, closes and re-pins the right rail refused to run — so the document-panel toggle, ⌥⌘B, the avatar-click expand and the sticky panel widths all silently stopped working. The bottom terminal dock had the same gap, where a stuck flag instead made every later resize look like a user drag: the dock could hide itself unprompted and overwrite its saved height. Both handles now end a drag on cancel as well as release, restore the panel widths when a gesture is aborted rather than committing a drag-to-close the user never finished, and detach cleanly if the panel unmounts mid-drag.

    Separately, a fault in the resizable-panels library could wedge things harder: after an error while a panel was mounting, every panel in the window could stop responding to drags until a full reload. A bundled patch now lets the library recover from that on its own, without the reload.

  • Fixed the problem count on the document panel's Problems tab painting over the Comments tab next to it. The count hung off the outside of the tab's top-right corner, which was fine while Problems was the last tab in the row, but the Comments tab now sits to its right. The count is drawn inside the tab it belongs to.

  • A window whose renderer stops responding now recovers itself instead of going blank. Chromium occasionally tears down the process that draws a window; the frame stays and nothing paints in it. OpenKnowledge noticed this and filed it for a crash report, but did nothing about the window, so it stayed empty until you happened to try Cmd-R. There was no message and no visible way back — one report described coming back from a short break to an empty window and reloading on a guess, unsure whether that would take the running agent and a session of unsaved comments with it.

    The window is now reloaded automatically the moment its renderer dies. If it dies a second time within a minute, the reload stops rather than looping, and a dialog offers to reload it or leave it alone. A window that dies repeatedly but slowly enough to keep dodging that minute is caught as well, after a few recoveries. That dialog also says what the blank window could not: documents and running agents live in the OpenKnowledge server, not in the window, so reloading restores the view without interrupting them.

  • Adding a frontmatter property now knows what the document's schema declares.

    The Add properties button already counted the schema-required properties a document was missing, but clicking it opened one blank row — you still had to go read the schema and retype every name it had already stated. It now stages a pre-named row for each missing property, with the widget type taken from the schema and the cursor in the first value. Nothing is written until you fill a row in and add it, so a half-finished batch never leaves empty properties behind (and never clears the "required" warning with a blank value).

    The name field on any add-row also offers the fields the governing schemas declare — filtered as you type, showing each field's type and description, and marking the required ones. Picking one fills in the name and its type together. It stays a free-text field: schemas don't own the whole vocabulary, and a document governed by no schema is unchanged. Enum-constrained fields now offer their vocabulary while being added, not only after.

  • Add a Delete action, with a confirmation dialog, to skill bundle files. It shows up wherever a skill file's actions do: the Skills sidebar row and the file's editor tab. Deleting closes the file's open tab and reports a miss instead of a silent success when the file is already gone.

    Editable .md reference tabs previously carried no skill-file actions at all; they now offer the same Rename, Reveal in Finder, Copy Path and Delete set as the sidebar row for that file.

    Also fixes a pre-existing hazard in DELETE /api/skill-file: the live-doc teardown for a project .md reference ran before the file's existence was known. Because bundle doc names are extension-less, deleting a path that was not on disk tore down the live doc of a same-stem sibling (references/x.md vs references/x.mdx) that survived the no-op unlink.

  • Typing in a large knowledge base no longer stutters while the template list is being gathered. Building the list of templates you can create from means walking the project's folders, and on a big repository that walk is long enough to hit its internal limit. It ran start to finish without pausing, on the same loop that carries your keystrokes between the editor and the server, so a burst of requests left the editor unable to keep up with typing for a noticeable stretch. The walk now pauses between folders (the longest uninterrupted pause it causes drops from roughly 84 ms to about 1 ms) and reads each folder with a single system call instead of one per entry. Symlinked folders are still followed.

  • The desktop app now ships for Windows and Linux, with auto-update on every platform. Each release publishes signed Windows installers (x64 and ARM64) and Linux .deb/.rpm packages (x64 and ARM64) alongside the macOS DMG, all attached to the GitHub Release. Installed apps update themselves: Windows updates install silently like macOS, and Linux updates download the new package and ask for your password to install it (via the system's polkit prompt) — no apt/rpm repository to configure.

View v0.48.0 on GitHub