Changelog
Stable releases, newest first, with notes for every version. Also published on GitHub.
Patch Changes
- Fixed a bug where deleting a template left the app unclickable until reload. The template row's actions menu opened a modal confirmation dialog while itself being a modal menu, stacking two
pointer-events: nonebody locks; the post-delete refresh then unmounted the still-open dialog before Radix could unwind the lock, freezing every button (including "New" and "New template"). The menu is now non-modal, matching the file tree and project switcher. - OK Desktop can now open a single markdown file without setting up a project. The single-file editing session that
ok <file>already provides is now reachable from the app itself: File → Open file… (⇧⌘O), the Cmd+K palette, and the Project Navigator, each opening a native.md/.mdxpicker. Loose files also open via Finder's Open With → OpenKnowledge. These flows open the file in a temporary session and never write a.okfolder into its directory, so previewing one file no longer risks turning its parent folder into a project. A picked file that already lives inside a project opens that project instead.
Patch Changes
- Bug-report bundles now capture why the server died and what the MCP server was doing. A fatal server crash (uncaught exception or unhandled rejection) writes a synchronous
last-server-crash.jsonrecord — timestamp, error name/message/stack, pid, uptime — under.ok/local/plus a final fatal line in the server log, both collected intook bug-reportandok diagnose bundleoutput; previously a hard crash lost the async log sink's tail and bundles could only say "server not running". The global MCP stdio server's[mcp]stderr diagnostics are now also mirrored to~/.ok/logs/mcp.<date>.log(pruned at each startup by a 7-day age window and an aggregate size cap), so bundles include the agent-ingress diagnostics that previously landed only in the MCP host's own log folder. - The
execMCP tool's post-command mutation safety sweep is now scoped to the paths the command can actually touch (its own path operands), instead of statting up to 1000 files across the whole knowledge base twice on every call. Path-scoped commands likecat notes/a.mdorgrep -rn oauth articles/now sweep only those paths, making exec noticeably cheaper on large knowledge bases. When the sweep does cover the whole tree (barels,find, recursivegrepwith no directory) and the corpus exceeds the 1000-file scan cap, the response now carries an explicit warning that mutation detection was partial, instead of silently presenting a capped sweep as full coverage. - The sidebar's page directory no longer storms the server during bulk agent writes. File-change pushes now coalesce into a single trailing refetch (~300 ms window) instead of refiring the full page-list and document-list walk on every push, so an agent writing many files per second no longer triggers ~10 full corpus refetches and index rebuilds per second. The initial load and manual refreshes stay immediate.
- New operational metrics for cloud/server operators (all zero-overhead when telemetry is disabled): event-loop delay percentiles (
ok.server.event_loop.delay_ms), CPU utilization (ok.server.cpu.utilization), extended memory sections (external,array_buffers) onok.server.memory.usage_megabytes, a loaded-documents gauge (ok.server.docs.loaded), and write-spine queue depths (ok.persistence.queue.depth,ok.bridge.drain_backlog). Error responses now stamp their correlation UUID on the active trace span (ok.error.instance) so a client-reported error can be joined against its trace. The standardOTEL_TRACES_SAMPLER/OTEL_TRACES_SAMPLER_ARGsampling contract is now pinned by tests and documented, including its interaction with the local bug-bundle span sink.
Patch Changes
- Diagnostics bundles now capture more of what matters for content-loss triage. The full bundle stages the shadow repo's recovery-checkpoint refs (
state/checkpoint-refs.txt— ref name, date, and content-free subject per checkpoint) and a newstate/agent-effects.jsonsnapshot of the per-document agent activity ring buffers, served by a new loopback-onlyGET /api/metrics/agent-effectsendpoint that summarizes agent writes as character counts (never raw text). Doc names in the new artifacts are anonymized by the existing--redactpass, which now also hashes the agent-presence bundle'scurrentDocpath so a redactedstate/agent-presence.jsonno longer leaks the document a person was editing. The web client's log forwarder also stops losing entries silently: drops from buffer overflow or failed uploads are counted and recorded as an explicitdroppedSinceLastFlushgap marker in the server log on the next delivered batch, and a failed recovery-checkpoint write now also emits a structured server-log line with doc and branch context. - Add "Import as template" to the File Tree context menu. Right-click a markdown file to copy it into the folder's
.ok/templates/, with the option to keep the original or convert it (delete the source). Backed by a newPOST /api/template/importendpoint that carries over the source frontmatter. - Added OpenClaw and Hermes as "Open with AI" terminal launch targets in OK Desktop, alongside the existing agent CLIs. OpenClaw opens its interactive TUI seeded with the composed prompt via
openclaw chat --message '<prompt>'. Hermes has no starting-prompt argument (its only prompt-carrying modes run once and exit), so OK launcheshermes chatand, once Hermes signals its input is ready, delivers the prompt as a bracketed-paste write — bracketed paste keeps a multi-line prompt intact and inert to the TUI's key handling, then submits it. The paste waits for Hermes' readiness signal (with a timeout fallback) rather than a fixed delay, so it lands cleanly on slow and fast boots alike. Both were already supported as MCP servers; this adds the one-click terminal handoff.
Patch Changes
Auto-restart a leftover server after an app update, and drop the dev "started a fresh server" notice
When OpenKnowledge Desktop auto-updated, a server for a project could survive the pre-install teardown (a CLI-spawned one, or one whose shutdown timed out). The relaunched app then attached to that stale build and asked you to click "Restart with this app's version" before it worked. Now, on the first launch after an update, the app detects the version-mismatched leftover and restarts it to match automatically — no prompt. This only happens on the launch right after an update: a running server that merely differs in version (for example a newer CLI you started yourself) is never auto-restarted. No extra notice appears — the existing "Updated to Version X" banner already tells you the app updated.
The dev-only "Started a fresh OpenKnowledge server…" toast that popped up whenever a dev session reclaimed a leftover server is removed — the reclaim still happens, just silently.
/imagenow keeps thesrcfield focused without immediately opening asset suggestions, so Upload from computer stays visible. Suggestions still open when you click or type insrc, or use the arrow keys.
Patch Changes
- Timeline version and agent activity diffs now render as formatted WYSIWYG documents with changes marked inline. A Source toggle keeps the raw markdown diff as well.
Patch Changes
When OpenKnowledge invites you to report a crash it just detected, and a crash dump for that crash is on disk, the dump is now included in the report by default. The crash dump is a memory snapshot from the moment of the crash, and the single most useful artifact for finding the cause, so it now rides along unless you uncheck it. Previously it was off by default, so crash reports usually arrived without the very dump that triggered them.
The crash-dump option now appears only when a dump actually exists for the crash, so a crash invite with nothing to include (for example a session that ended without a clean quit but left no native dump) no longer shows a dead checkbox.
Nothing about the consent flow changes: the crash dump is still a labeled checkbox you can turn off, the note still says it can contain document content and can't be redacted, and nothing is sent until you review the exact bundle. This only affects reports triggered by a detected crash; regular bug reports are unchanged and never include a crash dump.
Internal refactor (no user-facing change): the native application menu now renders its actionable command leaves from the same shared command registry the Cmd+K palette uses, so each command's identity — label, accelerator, keyboard shortcut, availability, and menu placement — is declared once in
@inkeep/open-knowledge-coreinstead of being hand-maintained in two lists. Command availability is a pure, declarative evaluator both surfaces call; the menu's structural scaffolding (roles, separators, submenus, the dynamic Recent-project list, and the platform branches) stays declarative. The "Check for updates" and "Settings" platform placements are now data-driven, and a ratchet fails the build if a command is ever hand-placed twice in the same platform's menu bar. Menu items, order, labels, accelerators, and enabled/checked states are unchanged on every platform.The desktop app no longer invites you to file a bug report after your machine reboots (kernel panic, forced restart, or power loss) while OpenKnowledge was running. It now recognizes when the previous session ended because the machine went down — not because the app crashed — and skips the prompt in that case, logging the event instead. Genuine app crashes still prompt exactly as before, and a crash that produced a crash dump still prompts, even across a reboot.
Minor Changes
The Cmd+K command palette now reaches the actions that used to live only in the native menu bar. You can search for and run Check for updates, New from template, New worktree, Switch worktree, Rename, Duplicate, Move to Trash, Reveal in Finder, Copy full path, Copy relative path, Close tab, the View toggles (sidebar, document panel, terminal, hidden files, .ok folders, only markdown files, skills section), Expand all, Collapse all, New Terminal, Kill Terminal, Set up OpenKnowledge integrations, Check spelling while typing, and OpenKnowledge on GitHub, all from Cmd+K.
Toggle commands show their current state (for example the sidebar row reads "Hide sidebar" when the sidebar is open), and the commands that do not need the desktop shell now work in the web app too, not just Desktop. These rows appear once you start typing, so the palette's empty state stays lean.
On macOS, "Check for updates" no longer appears twice. It shows once, in the App menu, matching where Settings lives.
Patch Changes
- Opening a shared document by cloning from GitHub no longer requires the GitHub CLI (
gh) to be installed. OpenKnowledge now authenticates the clone with the GitHub account you're already connected as, rather than delegating toghor to whatever credential helper your machine's git config happens to point at. "Clone to a new folder" previously failed on a clean machine — or one with a leftovergh auth setup-gitconfig afterghwas removed — withgh: command not foundandcould not read Username; it now uses your connected account directly. - Internal refactor of the Cmd+K command palette: the fixed command rows now render from a single command registry that also drives the palette/menu parity ratchets, replacing the per-command hand-wiring. No user-facing changes — the commands, their grouping, labels, shortcuts, and behavior are unchanged.
- Fixed a rare content-corruption bug where text written to a document while a collaborator's connection was catching up could split freshly-applied content in two. When a file changed on disk (or an agent rewrote a document) at the same time as an offline-typed edit from another client arrived, part of the new content could end up interleaved with the concurrent edit — observed as a line split in the middle with the other client's text in between. The sync bridge now applies changed content as whole lines in single contiguous runs, which makes this interleaving structurally impossible; unchanged lines still keep their edit history and attribution.
- The Problems panel gains two new lint actions. A "Fix all" button applies every auto-fixable problem at once — in the "This doc" tab it fixes the open document instantly (undoable, attributed to you), and in the "Project" tab it sweeps every fixable file and refreshes the audit, reporting any files it could not fix. On desktop, each problem row also offers "Ask AI": it composes a grounded fix prompt (document, rule, line, message, and the offending text) and types it into your running agent terminal for review — or launches Claude with it if no terminal is open. Deterministic fixes triggered from the UI are now attributed to you (the principal) rather than to an agent; fixes requested by agents over MCP keep agent attribution.
- Feature: typing
$$…$$or$…$in the WYSIWYG editor now autoconverts to an inline math atom on the closing delimiter (currency-safe; Ctrl+Z restores the raw literal). The inline-math edit popover closes on Enter, gains a Done button, and drops the caret right after the atom so typing continues inline. - Background shadow-repo garbage collection no longer races the write path. Previously, the maintenance gc (which packs and prunes the attribution journal's loose objects) could run concurrently with an in-flight auto-save commit; in the race window, git's object-directory cleanup could make the commit fail transiently (
unable to create temporary file/failed to insert into database), logged as a per-writer shadow commit failure and dropping that flush's version-history entry until the next auto-save. Shadow mutations and the gc leg are now mutually exclusive: gc waits for in-flight commits to drain and briefly holds new ones until it finishes, so auto-saves can no longer fail because maintenance happened to run at the same time. - Long Ask-AI, Open-with-AI, and Create-with-agent instructions are no longer silently cut to ~1,400-2,200 characters when launched in the docked terminal. The terminal launch previously inherited the web deep-link's URL budget even though it feeds the agent CLI directly through the terminal; it now carries instructions up to ~100 KB, so a long typed prompt reaches Claude, Codex, Cursor, and the other agent CLIs in full. Web deep-link handoffs keep their intentional URL-size budget, and selections continue to travel losslessly on both paths.
- Trailing spaces or tabs at the end of a paragraph, heading, or table cell no longer round-trip through an unstable intermediate form. Typing a trailing space in the editor used to serialize the space byte, which the next parse silently discarded — an avoidable divergence between the WYSIWYG view and the stored source that cost an extra canonicalization cycle on every save/load. The serializer now strips insignificant line-final whitespace up front (the exact canonicalization the parser already applies to files on disk), so the first emission is a stable fixed point. Significant whitespace is untouched: boundary spaces inside bold/italic/strike/highlight still mint their
 character references, mid-paragraph hard breaks of both markdown styles stay byte-exact, non-breaking spaces survive, and whitespace-only paragraphs keep their minted character reference. A new editor-construct fixed-point sweep now guards this whole class: every editor-constructible document shape must serialize to bytes that are their own re-parse fixed point, with an explicit fail-closed allowlist for documented benign canonicalizations. - Typing or pasting a literal whitespace character reference such as
 (space),	(tab), or (non-breaking space) into the editor no longer silently turns it into the actual whitespace character. Previously these typed characters were stored unescaped, so on the next reopen or sync every reader saw an invisible space instead of the text you typed. They now survive edit, save, and reopen exactly as written. Spaces the editor itself preserves at bold/italic boundaries are unaffected, and existing files on disk keep their exact bytes. - Pasting source at the end of a document no longer corrupts or loses content when the paste lands against a code fence or a JSX container's closing tag. A paste that glued onto a closing ```line used to reopen the fence to the end of the document; the editor's serializer then invented a closing fence the document never had, the collaboration bridge saw permanent divergence between the WYSIWYG and source views, and its repeated repair merges could drop lines from both — in multi-user sessions this surfaced as text vanishing for everyone. Documents that end in an unclosed code fence now round-trip byte-exactly (the fence stays unclosed until you type below it), and content pasted directly under a
</Steps>-style container close is recognized as equivalent to its canonical blank-line-separated form instead of triggering endless repair cycles.
Minor Changes
- External link hover previews are now on by default. Previously they shipped off by default (opt-in). Hovering an external link in the editor now shows a preview card — site name, page title, description, favicon — fetched by your local server, which sends that link's URL to the destination site (one request per previewed link). Turn it off per machine in Settings → Link previews, which sets
linkPreviews.enabled: falsein project-local config. Unchanged: the SSRF guard still refuses to fetch private or internal hosts, no cookies or credentials are attached, results are cached locally, and any fetch failure falls back silently to the plain URL pill. Internal document-to-document previews were already always on and read entirely from the local index with no network request. Note: external previews are not yet available in the packaged desktop app; this default applies to the browser andok uisurfaces. - Mermaid diagrams are now fully visually editable. The diagram renderer is powered by mermaid-wysiwyg: click any node, edge, participant, or message to select it and get an action popover right on the diagram (change node shape, edge type, colors, message arrows, participant types, duplicate, delete, and more), double-click any label to edit its text in place, drag between nodes to connect them, and drag sequence messages to reorder. Every gesture becomes a minimal edit to the underlying Mermaid source, so the code fence (or standalone .mmd doc) stays the source of truth and collaborative editing keeps working. Diagram styling, theming, and the pan/zoom controls are unchanged.
Patch Changes
Report a bug now records why the server last exited. When the background server process goes away, the desktop app writes the exit code and the reason it left (a clean shutdown, a crash, or an out-of-memory / OS kill) to
state/last-server-exit.jsonin the report bundle, next toserver.lock. Until now a bundle could only show that the server's port was "unreachable", which looks identical whether the server crashed or was shut down cleanly, so a "my app crashed" report could not be told apart from a routine stop. The new record captures the death even when the server was killed and had no chance to log anything itself. It holds only the exit code, the reason, the pid, and a timestamp, so it carries no document content or paths.Opening a shared branch in a worktree (and other git operations) no longer fails with a generic error when the repository uses git-lfs installed via Homebrew. The desktop app and its server now append well-known tool directories (Homebrew, MacPorts,
~/.local/bin, asdf/mise shims) to the PATH used for git commands, so helpers git spawns mid-operation — LFS filters, credential helpers, hooks — resolve even though macOS launches apps with a minimal PATH. When a required helper still can't be found, the error now names it ("Git needs git-lfs…") instead of a retry-blind generic message, and the underlying git error is recorded in the desktop log.Migrate the build, test, and CI toolchain from Bun to pnpm 10 and Vitest 4.
This is an internal toolchain migration with no runtime behavior change for the published CLI: it still runs on Node (
engines.node >= 24, shebang#!/usr/bin/env node). The workspace is now a standard pnpm workspace (pnpm-workspace.yaml+pnpm-lock.yaml), all five test tiers run under Vitest via abun:testcompatibility shim and aBun.*facade, and every TypeScript entry point runs through Node +tsx.bun.lock,bunfig.toml, and.bun-versionare removed, and the public mirror ships pnpm.Bun-specific fences retired as part of the cutover:
findBunLockMetadataDriftand thebun.lockmetadata-drift guard (the pnpm frozen-lockfile check supersedes it).check:bun-run-fallthrough(thebun runPATH-fallthrough guard) and its test, along with therun-bun-if-available.shroot fan-out wrapper.bun-install-ci.sh, the retry/idle-timeout install wrapper that existed to work around a Bun install-hang issue; pnpm's install path replaces it.- The
run-test-dom.sh--isolatemock-leak mitigation (the per-file fresh-module-registry workaround for a Bunmock.modulein-place-patch leak); the DOM tier is now a dedicated Vitest project withisolate: true.
Two small, required behavioral deltas ride along with the swap:
ok diagnose --redactbundles now derive doc-name redaction tokens with sha256 instead of BLAKE2b-256 (Node's OpenSSL rejects BLAKE2outputLength), so thedoc:<hex>tokens differ while the in-bundle inverse map still resolves them; and the file-copy API now returns HTTP 409 forERR_FS_CP_DIR_TO_NON_DIR/ERR_FS_CP_NON_DIR_TO_DIR(previously an unhandled 500).Installing an update no longer lands you in the wrong window. When you relaunch to install the latest version with several project windows open, OpenKnowledge reopens all of them and now brings the window you were last working in back to the front, instead of whichever window happened to finish loading last.
Fix a stray backslash appearing at the end of lines after a hard break
A hard break (Shift+Enter) at the end of a paragraph serialized to a trailing
\in the markdown source, which users saw as an unexpected character at the end of a line. Worse, deleting it did not stick: the WYSIWYG fragment kept the break node, so the next sync re-emitted the\(a fragment/source round-trip that never settled, hidden from the invariant watchdog by parse-equivalence tolerance). A trailing hard break has no CommonMark meaning — a backslash form decays to a literal backslash on the next parse and a two-space form is stripped — so it is now dropped at serialize time. Mid-paragraph hard breaks, source-authored breaks, void<br>breaks, and literal backslashes in text are all unaffected and still round-trip byte-for-byte.
Minor Changes
- Report a bug can now include a screenshot of the app. When you open the Report a bug dialog, OpenKnowledge captures the app exactly as it looked underneath the dialog and shows you a preview inside the compose step. The screenshot is included by default; uncheck it to leave it out. Because the preview shows precisely what will be attached, you can confirm the picture before sending it: a screenshot is an image of your screen and is not redacted the way logs are, so review it and uncheck it if anything on screen shouldn't be shared. When kept, the screenshot rides inside the report bundle at
extra/screenshot.png, and like the rest of the bundle it never leaves your machine until you send the report. - Surface OpenKnowledge's built-in
open-knowledgeproject skill in the Skills UI as a read-only, "Managed by OpenKnowledge" entry. It was previously hidden, so you could not see what your agents actually load. You can now open and read its SKILL.md (and references) from the Skills sidebar and Settings; edit, rename, delete, and install are disabled in the UI, and the skill write/rename/delete APIs refuse mutations to the reserved built-in skills (defense in depth).
Patch Changes
Report a bug no longer points you to GitHub after you send. The "Thanks for the report!" confirmation used to suggest opening a public GitHub issue and included an "Open GitHub issue" button; both are gone. The dialog still shows your report reference to copy, and if you want to add more it offers writing to support@inkeep.com with that reference.
Fix "Report a Bug" in the packaged desktop app. Send now attempts to upload your report to the OpenKnowledge team, instead of always opening the "send it by email" draft. The packaged app shipped with no bug-report intake configured, so every Send skipped the upload and fell straight through to email. It now targets the production intake by default (
OK_BUG_REPORT_INTAKE_URLstill overrides, and unpackaged dev builds are unchanged so a dev run never uploads by accident). If the upload cannot be completed, Send still offers the same email draft, so no report is lost.Fix the Mermaid slash command inserting an invisible, zero-height block. Selecting Mermaid from the
/menu previously dropped a near-zero-height sliver: nothing appeared to happen, repeated inserts stacked invisibly, and the hover toolbar (edit / delete) was clipped. An empty Mermaid block now renders a clear "Add a Mermaid diagram" placeholder card and opens the diagram source editor on insert, so there is always a visible, adequately-sized target to author or remove.Stop OpenKnowledge's built-in
open-knowledgeproject skill from causing recurring git sync conflicts. The app regenerates that skill into each editor's host dir (.claude/skills/open-knowledge/,.cursor/…,.codex/…,.github/…,.opencode/…,.pi/…) on every open, and different app builds stamp a different version into it — so when it was committed, teammates on different builds collided under git auto-sync (merge conflicts, or a repeatedly "paused, external changes pending" sync).It is now treated as a local, per-machine artifact. On
ok initand on project open, OpenKnowledge writes a committed.gitignoreentry that always excludes this projection (in both shared and local-only sharing modes, and it travels to every clone so a fresh checkout is protected before the app ever runs). Nothing is lost, since the app regenerates the file on each open. For repos where the skill was already committed, opening the project untracks it automatically via a dedicated, conflict-safe commit; teammates will see it removed on their next pull. Authored skills you place under.{editor}/skills/<your-name>/are unaffected and continue to follow your OpenKnowledge sharing setting.Show a read-only Properties panel when viewing a read-only skill file's markdown (for example the built-in
open-knowledgeskill's SKILL.md). Previously the frontmatter was stripped and hidden in the read-only viewer, so a managed skill'sname,description, and other metadata were invisible; the editable skill panel already showed them. The read-only viewer now renders the same "Properties" section above the body, in read-only mode.
Minor Changes
Add content rules (a markdown linter with native markdownlint support) and themes as a toggleable plugin, introducing the new Settings → Plugins area. Shipping the two together makes the Plugins taxonomy legible from day one: content rules are a project-scope plugin (shared via git) and Themes is a user-scope plugin (personal to this device) — the two kinds of plugin the system is built around.
Content rules. Powered by markdownlint (the engine behind VS Code's markdownlint), the linter surfaces common issues — hard tabs, trailing whitespace, heading-increment skips, inconsistent list markers, and more — as non-blocking warnings, with a gutter marker, hover tooltip, and a one-click "Fix" for auto-fixable rules. WYSIWYG mode flags the offending block with the same wavy underline — or, for textless blocks like thematic breaks and images, an outline so the mark is always visible. Enable it per project in Settings → This project → Plugins.
Your rules live in your project's own
.markdownlint.{json,jsonc,yaml,yml}(or.markdownlintrc) file — the portable, ecosystem-standard config, discovered at the content root — not in Open Knowledge's config. With no file present, the default matches the VS Code markdownlint extension exactly: every rule on, exceptMD013(line-length). The only OK-config surface is the plugin toggle (contentRules.markdownlint.enabled, project scope).Problems panel. A new Problems tab in the right-hand document panel lists every problem for the active document with a count badge, and clicking a row jumps to the offending spot: the exact line and column in source mode, the containing block in WYSIWYG. It reads the document's source CRDT directly, so problems show in WYSIWYG mode too — not just source mode; the WYSIWYG block markers lint those same source bytes, so byte-level findings (hard tabs, trailing spaces, blank-line runs) paint in both modes. A "This doc / Project" scope toggle runs a whole-project audit on demand (a refresh button re-runs it; nothing polls in the background), listing every violating file with its diagnostics and error/warning totals. Clicking a diagnostic opens the offending file and lands on the violation — the exact line in source mode, the containing block in WYSIWYG. Auto-fixable diagnostics carry a one-click Fix in every mode: the CodeMirror lint action in source mode, a Fix button on the panel row, and a Fix button in the WYSIWYG block's hover tooltip — each applies the fix straight to the document's source.
Settings → Plugins. A new Plugins management page (under This project) groups plugins by scope — "This project — shared via git" (content rules) and "This device — personal" (Themes) — and each enabled plugin appears under a Plugins header in the settings sidebar with its own panel. Whole-project audits run from the Problems panel, not Settings.
Full-catalog rule browser. The markdownlint panel lists every rule the installed engine ships (53 today), grouped by category, with search over id/alias/name and an "Only modified" filter. Rule metadata — names, descriptions, documentation links, option types — is generated from markdownlint's own config schema, so the catalog always matches the installed engine version. Toggle any rule on or off and the change lands in your native
.markdownlint.json(created from OK's tuned defaults on first edit). Expanding a rule row reveals its documentation link and a typed field per option: switches for booleans, dropdowns for enums, number inputs honoring the schema's minimum/maximum, plain text for strings, and tag-pill lists for arrays — setMD013'sline_length,MD003's heading style, orMD043's required headings without hand-editing JSON. The rare option shape with no generic control shows an "Edit in config file" chip.Themes as a plugin. The IDE color palettes (Dracula, Catppuccin Frappé, Catppuccin Latte, Monokai, Gruvbox, Solarized, and a six-seed-color Custom theme with live preview) now live under Settings → Plugins → Themes, and the Themes plugin itself can be toggled off — disabling it reverts the whole app to the default palette (including the pre-paint flash cache) without losing your saved choice. Palettes are personal, user-scoped preferences (
appearance.colorTheme,appearance.customTheme,appearance.colorThemeEnabled) applied flash-free on launch; dark IDE palettes force dark mode so Tailwinddark:styles stay correct, and switching back to Default restores your saved light/dark choice.Native config, the markdownlint-cli2 way. Rules live in the project's own
.markdownlint.{jsonc,json,yaml,yml}files and are honored exactly as markdownlint-cli2 would: full JSONC (comments, trailing commas, BOM),extendschains (relative file paths, escape-guarded; package refs declined with a clear diagnostic;extends: nullaccepted), and a per-directory cascade — the nearest config file on a document's path governs it wholesale, with no OK-flavored merging underneath. The VS Code-parity defaults apply only when a project has no markdownlint file at all, and the first rule edit from Settings materializes them into a real.markdownlint.json. Executable configs (.cjs/.mjs) are detected but never executed. Malformed configs and unresolvableextendssurface loudly (Settings,ok lint, and the lint API) instead of silently falling back, with the underlying parse/read detail in the message.Edits are faithful to your file. Alias-keyed configs (
line-lengthforMD013, case-insensitive, last matching key wins) are resolved with markdownlint's own semantics, and rule edits rewrite the key form already in your file instead of appending a duplicate canonical id. Toggling a rule in a.markdownlint.jsoncpreserves your comments, and unrelated keys and formatting stay untouched. Severity strings ("error"/"warning") — valid in markdownlint's own schema — load and lint normally; the rule browser shows such a rule as enabled with a read-only severity chip, and discloses before an edit that saving replaces the severity string. Lint reads are symlink-safe: a path that resolves outside the content directory is refused, matching the escape protection on every other read surface.Headless + MCP. A new
ok lint [path]CLI command lints the project (or a folder/file) from the terminal, with--fixto apply auto-fixable rules and--jsonfor structured output — exits non-zero on problems, CI-friendly. A newlintMCP tool surfaces problems to connected agents — for a single document or as a whole-project audit, capped at 10 files × 10 diagnostics with explicit "… and N more" indicators and always-accurate totals. Passingfix: truewith a document applies markdownlint's auto-fixes in place through the collaborative document — attributed to the agent and reflected in the live preview, exactly like the editor's Fix button, never an unattributed shell edit; it stays auto-approvable because the fix is a recoverable, shadow-versioned content write likewrite/edit. Agentwrite/editresponses also carry the written document's problems as advisory warnings, so agents see the same issues the editor shows without an extra round-trip. The lint HTTP API is grouped under/api/lint/*.Hovering a link in the editor now shows a preview card. Links to other documents in the project get a doc card — title, folder path, tags, last-edited time, backlink count, and a short excerpt — built entirely from the local index and file contents: on by default, no configuration, and nothing leaves the machine. This is a hover-behavior change for existing users: resolved internal links previously showed only the URL pill, which is still there with all its actions — the card renders alongside it.
External links get an opt-in preview card showing the destination's site name, page title, description, and favicon. It is off by default and per machine: enable it from Settings → This project → Link previews, which sets
linkPreviews.enabledin project-local config (never shared with collaborators). When enabled, hovering an external link sends that link's URL to the destination site to fetch preview metadata — one request per previewed link, with no cookies or credentials attached. The opt-in is enforced by the local server (with it off, no external request is made), results are cached locally, requests to private or internal addresses are refused, and any preview that can't be fetched quietly falls back to the plain URL pill.Report a bug from inside the app. OpenKnowledge Desktop gains a full in-app bug-report flow: open it from Help → Report a Bug… or the ⌘K command palette, add an optional note, and the app packages logs and system info into a single secret-redacted zip you can reveal in Finder and review before anything leaves your machine. By default, Send makes no network request — it opens a prefilled email draft to support@inkeep.com that names the saved report file for you to attach. When a bug-report intake endpoint is configured (
OK_BUG_REPORT_INTAKE_URL), Send instead uploads the report privately to the OpenKnowledge team, falling back to the same email draft if the upload fails — nothing is ever sent without your explicit action. A successful upload returns a report reference you can quote when opening a public GitHub issue.The desktop app also detects crashes locally, first-party only (Electron's crash reporter runs with uploads disabled): if a window process dies or the app quit unexpectedly last session, it invites — never auto-sends — the same report flow, with crash context prefilled and an opt-in, default-off checkbox for attaching the crash dump. Error screens (per-document and a new app-wide boundary) gain a "Report this error" action wired to the same dialog.
For programmatic use, the CLI package now exports
collectReportBundle({ level, projectDir, note, redact, outputPath, extraFiles }), the leveled bundling API behind both the desktop flow andok bug-report: levelstandardcollects the bug-report set (logs, lock/spawn diagnostics, system info),fulladds the diagnose superset (telemetry spans, live server state, runtime metadata) and degrades gracefully by omitting pieces that aren't available, andextraFilesattaches pre-collected artifacts verbatim (the crash-dump opt-in rides on it).ok bug-reportoutput is unchanged — it now routes through the same API.
Patch Changes
Add GitHub Copilot CLI integration with MCP, skills, and terminal launching.
Fix: GitHub Enterprise Server hosts are no longer ignored (#597). Auth resolution, the sync token relay, and the push-permission probe now derive their host from the workspace's origin remote instead of assuming github.com:
resolveAuthscopes gh detection to the origin host, the sync engine relays the origin host's token, origin classification recognizes GHES hostnames (with browsable remote links in the sync UI), theok authsubcommands default--hostto the workspace origin, and the app's account panel shows the identity for the host the workspace actually syncs with. Sharing remains github.com-only for now.Literal non-breaking spaces (U+00A0) in document prose now survive WYSIWYG edits and every markdown re-parse, instead of silently becoming regular spaces. The pipeline's internal NBSP whitespace sentinel was removed; whitespace-only text nodes are preserved by the existing inline-whitespace char-ref mechanism, which now also covers NBSP at emphasis boundaries.
Inline images now render in desktop windows attached to a server the desktop did not spawn (MCP-autostarted or terminal
ok start). Every server now serves content-directory assets (images, video, audio, PDF, file attachments) at their/<contentDir-relative>paths by default, through the same admission middlewareok uiuses (inline/attachment dispatch, fail-closed 404 for missing assets). Previously only desktop-spawned servers passed--serve-content-assets, so a desktop window attaching to an MCP-spawned server resolved image srcs against an origin that 404'd them, showing broken images for every inline image in the project. The flag is still accepted for compatibility; passserveContentAssets: falsetobootServerto opt out. Servers that don't serve the React shell keep theok uipointer hint in their catch-all 404.Fix the branch-mismatch share dialog ("Open shared document") overflowing its modal when the shared branch has a long name. The switch action used to embed the full branch name ("Switch to
<branch>"), which pushed the button row past the dialog edge and clipped the primary "Open in worktree" button. The action now reads simply "Switch branch" — the branch name is already shown in the dialog's Branch row and body — and the footer's action group wraps instead of overflowing when all three actions (Open in current branch / Switch branch / Open in worktree) are present.Stable releases are now versioned as a single semantic-version bump over the previous stable, derived from the changeset delta between them, instead of stripping the
-beta.Nsuffix from the promoted beta tag. Accumulated patch work now ships as its own patch (for example0.30.1 → 0.30.2) rather than collapsing an entire beta cycle to one stable, and a minor changeset in the delta still bumps the minor. Promotion selection and version determination are also separated: a scheduled job selects the newest soak-proven beta, and the promote job — shared by manual and automatic promotions — computes the version.The desktop app now checks for updates hourly instead of every 5 minutes. The 5-minute cadence was a temporary pre-release setting; hourly matches the steady-state interval comparable apps use.
It also keeps the beta update channel well within GitHub's API rate limits. The beta channel resolves the newest build through a docs-site proxy that calls GitHub's unauthenticated releases API on a budget shared across the whole install base (keyed on the proxy's IP, not each client's), so how often clients poll feeds straight into that one budget. Hourly, with a wider jitter window to break fleet lockstep, keeps it comfortable. Stable is unaffected either way, as it resolves through GitHub's own "latest release" redirect with no API call.
An automatic update may now be noticed up to about an hour later than before. Auto-download and install-on-quit are unchanged, and "Check for Updates" still forces an immediate check on demand.
Minor Changes
Share links for a branch you don't have checked out now default to opening in a worktree. The branch-mismatch dialog's primary action, "Open in worktree", creates (or reuses) the branch's worktree under
.ok/worktrees/<branch>and opens it in its own window at the shared doc or folder — your current checkout, branch, and uncommitted changes stay exactly where they were. Switching the current window's branch is still available as "Switch branch", with unchanged behavior including the post-checkout navigation gate.This also un-dead-ends the dirty-conflict case: where uncommitted changes used to block the share entirely ("commit or stash, then open the link again"), the worktree action now works immediately. The share branch resolves wherever it lives — an existing local branch, a remote-tracking ref, or never fetched, in which case a bounded
git fetch origin <branch>(15s cap, no credential prompts) runs first. Failures stay honest: a branch deleted upstream dismisses the dialog with the existing "no longer exists" notice, a connection failure keeps it open so you can retry or pick another action, and the new window inherits the target-existence check, so a stale branch shows the honest missing-target panel instead of silently creating an empty doc. New worktrees appear in the worktree switcher immediately.
Patch Changes
- Fix: the code-block chrome's Ask AI now grounds the composer with the selected block's fenced source on first click, matching the second-click and text-selection Ask AI paths. Previously, when no terminal was live, the composer opened with an empty pill and the receiving agent got no passage.
- Add
ok embeddings set-url <url>/ok embeddings clear-urlto configure the semantic-search embeddings endpoint from the CLI (for headless / CI users), and validate the endpoint URL inline in Settings → This project → Search. Both the CLI and the Settings field now reject a guaranteed-to-fail endpoint at entry — a malformed URL, or a plaintexthttp://host other than localhost — using the same rule the server enforces before it will send the API key, instead of letting a bad value degrade silently to lexical search later. - External links now reliably open in your OS default browser on desktop. Clicking an http(s) link in the visual editor, source mode, or a wiki-link chip previously could open another Open Knowledge window rendering the page — most visibly in windows created after a server restart. Link clicks now route straight through the desktop bridge, and the window-level safety net is attached to every editor window (including the server-restart recreate path), so external URLs land in your browser everywhere. Web behavior is unchanged (links still open in a new tab).
- Terminal CLI launch rows now reflect what's installed. Across every launch surface — the header / tab-strip "New chat", the "Ask X" composer button, and the "Open with AI" menus — the docked-terminal CLI rows are gated on a PATH-detection probe, so you only see the CLIs you actually have (e.g. no Codex or Cursor-agent row when they aren't on PATH). Claude always keeps its row as an install anchor. The gate fails open: a CLI whose install state is still unknown (probe pending, probe failure, or an older desktop bridge) stays visible rather than being silently dropped, so a probe miss never hides a CLI you have installed.
- Fixes Backspace/Delete merges at nested list-item boundaries (inkeep/open-knowledge#609). Backspace at the start of an item whose previous sibling has a nested sublist no longer lifts the item out of the list as a bare paragraph with no bullet or checkbox, and forward Delete at the end of a nested item before a shallower item no longer re-nests that item at the wrong depth or silently drops its checkbox. Both fixes cover the Mod-Backspace/Mod-Delete variants; flat-list merges, first-item lift-out, and autoformat undo behavior are unchanged. Supersedes and credits inkeep/open-knowledge#613 by @blokboy.
- Remember each project's window position across opens and relaunches. Reopening a project now restores its editor window to the exact frame it last had — same display, same position, same size, including maximized / full-screen state — instead of the cascade default; cascade remains the fallback for projects with no memory or whose remembered display is no longer connected. The desktop also tracks which project window was most recently focused: a post-update relaunch reopens every window at its old position and brings the window you were actually working in back to the front, and a normal cold start reopens the project you were last in rather than the one you happened to open last. Focus tracking freezes the moment shutdown begins, so the window-close cascade during quit or update install can't mislabel "last active" with whichever window closed last.
New file,New folder, and template creation from sidebar menus now keep the inline name field focused, so you can rename the item instead of immediately committing its default name.- Fix the
[[wiki-link suggestion box silently freezing when a folder and a note share a name. The search index keyed every entry aspage:<name>, so a folderwikiand a notewiki.mdcollided onpage:wiki; the index rejected the duplicate key and the uncaught error killed the typeahead, leaving the dropdown stuck on the same initial results no matter what you typed. Entries are now keyed by their true kind (folder:,page:,file:), folders and assets are explicitly included in the corpus, and results map back to notes by the full kind-qualified key so a folder can't stand in for a same-named note. - Preserve agent-authored byte-forms through WYSIWYG edits in the same block. Blockquote lazy continuations now round-trip via a
'lazy'marker-spacing capture, tight ATX heading-paragraph adjacency round-trips via the existing contiguity attribute, and Observer A's map-driven splice narrows into an edited blockquote, list, or list item so untouched sibling children keep their source bytes (including blank-line runs inside list items).
Minor Changes
Links in the terminal are now clickable. URLs open in your browser (including explicit OSC 8 hyperlinks from tools like
ls --hyperlink), and file paths in output — absolute, relative, or with a trailing:line:col— become clickable when they point to something inside your project: Markdown documents open in the editor, folders open to their overview, and other files open in the OS default app (revealed in Finder when the type can't be opened directly). Only in-project paths that actually exist are highlighted, so there are no dead links. A file path pointing outside your project asks first with a "this file is outside your project — reveal in Finder?" prompt. Links inside a full-screen terminal app that handles its own clicks (like the Claude TUI) are left to that app, so they open exactly once.Remove the launch.json preview machinery and the pane-target arming subsystem. Claude Code Desktop's in-app Browser pane now opens the preview URL directly (
preview_start({url})+navigate({url})), so OK no longer scaffolds.claude/launch.json.Breaking for consumers: the
@inkeep/open-knowledgepackage drops thescaffoldLaunchJson,LAUNCH_UI_CHAIN_*, andLaunchJsonResultexports; thepreview_urlMCP tool drops thearmPaneTargetparam; and the/api/configresponse drops thepaneTargetfield. Theok startrepair sweep now removes any pre-existingopen-knowledge-uilaunch.json entry instead of rewriting it.The file tree gains a "Show" visibility menu. The tree-options button in the sidebar toolbar (now always visible) opens a two-section popover: the existing Expand all / Collapse all commands, and a new Show group with four checkboxes: Hidden files, .ok folders, Only markdown files, and Skills. The same toggles appear in the sidebar right-click menu and the macOS View menu, and every surface stays in sync. Preferences persist per project per machine.
- "Only markdown files" strips the tree to just your .md/.mdx documents and folders, for a focused notes view.
- ".ok folders" reveals OpenKnowledge's own project state (skills, templates,
config) in place, read-only: clicks route to the Skills editor, the template
editor, or a read-only viewer, and internal
worktrees/andlocal/directories are never listed. Search is unaffected by the reveal. - "Skills" hides or shows the sidebar Skills section.
- Opening a doc the tree currently hides (via a link, search, or URL) now leaves the tree quiet: no stale row stays highlighted and the tree no longer scrolls to the wrong row (two pre-existing defects fixed). A subtle editor indicator names which toggles hide the current doc, with one-click flips.
- When active filters hide everything, the tree explains itself and offers a one-click "Reset view filters".
Patch Changes
Configure a custom OpenAI-compatible embeddings endpoint from Settings → This project → Search instead of editing config files by hand. The semantic-search settings now expose the embeddings base URL directly, and the account copy plus
ok embeddings set-keymessaging now describe the key as belonging to the configured embeddings provider rather than only to OpenAI.Files written into a freshly created folder no longer go missing from page listings, search, and the collaboration layer when the watcher's per-directory subscription loses the race with the write (seen on Linux under load, e.g.
mkdir notes && cp draft.md notes/). The new-folder rescan that already recovered dropped subfolders now recovers the files inside them too, replaying them through the normal change pipeline.Fixed a rare corruption where typing into a document with unregistered JSX components (like
<Steps>/<Step>) in source mode could duplicate or re-indent a block in the saved document. Under CPU load, a background editor could issue a structural rewrite of the same block the server was rebuilding keystroke-by-keystroke, and both copies survived into the authoritative document. The editor no longer issues those background rewrites while it is hidden in source mode, and the server now detects and discards a duplicated block instead of persisting it, so the typed content stays intact.Add Hermes Agent (Nous Research) as an MCP-host AI option.
ok initnow detects Hermes (via~/.hermes/) and registers the OpenKnowledge server in~/.hermes/config.yamlundermcp_servers, using the same resilient launcher every other editor gets. Hermes keeps its whole config — models, tool filters, other MCP servers — in that one YAML file, so OK edits only its ownopen-knowledgeentry via a format-preserving surgical write (the first YAML host; comments, values, key order, and block formatting are preserved) and declines rather than clobber a config it can't safely parse. Uninstall/repair strip only OK's own entry, leaving a foreign server that shares the key untouched. Like OpenClaw, Hermes is written only when it's actually installed.Two editor insertion-route fixes.
Block components (Callout, and any block-level JSX component) can no longer be placed inside a table cell, where they previously disappeared on save: a table cell has no markdown spelling for block content, so the component serialized to zero bytes while still showing in the editor. Every insertion route now refuses it as a silent no-op — the slash menu omits block-component entries when the caret is in a cell, and pasting or dropping a block component into a cell leaves the document unchanged. Placing the same component outside a cell is unaffected, and content that already carries a component in a cell (from a collaborator or a raw file edit) is left alone rather than fought.
Pasting copied list items at a list-item boundary now lands them as sibling items instead of mis-placing them (inkeep/open-knowledge#609). Pasting at the start of an item puts the copied items above it, at the end puts them below it, and mid-item splits the item at the caret. Task, bullet, and ordered lists behave the same way, nested lists inside the pasted items are preserved, and pasting a whole component or table still reproduces it byte-for-byte.
Two markdown fidelity corrections. The editor no longer writes a redundant backslash into stored source when prose contains interior punctuation like
1)x,a@x,a{x,a![x], or an uncloseda![x— those characters now round-trip as the literal text you typed. And pasting a list item so it holds a nested list as its first block (common when rearranging to-do lists) no longer emits garbled bytes: the item and its nested list now serialize to a form that re-parses to the same structure, instead of leaking a stray- [ ]marker into the item text or silently re-indenting the trailing content.
Patch Changes
- Fix the desktop app's "Open in Antigravity" terminal launch dropping the prompt.
agyhas no positional prompt argument, soagy '<prompt>'opened an empty session; the prompt now rides onagy --prompt-interactive '<prompt>', which runs the initial prompt and keeps the interactive session open. Affects every terminal launch that targets Antigravity.
Minor Changes
First-class support for Google Antigravity — the agentic IDE and the
agyterminal agent (the Gemini CLI's successor).ok initnow registers the OpenKnowledge MCP server for Antigravity, writing the standardmcpServersentry (the same resilient launcher every other editor gets) into Antigravity's single user-global config at~/.gemini/config/mcp_config.json, shared by the IDE andagyalike. Antigravity has no project-scoped MCP config, so — like Claude Desktop and OpenClaw — it registers once at user scope, gated on the~/.gemini/home existing so a config is never written for a tool that isn't installed. It also appears in the macOS desktop app's consent dialog and is healed by the startup repair/reclaim sweeps.Antigravity also joins the docked-terminal launch registry: the OpenKnowledge desktop app's "Open in Antigravity" action launches
agy '<prompt>'in the docked terminal, scoped to the current doc, folder, or project. The row appears only whenagyis detected on yourPATH. A new integrations docs page covers MCP setup and theagyCLI.Built-in terminal now auto-approves OpenKnowledge's own tools for agents it launches, so the read/write loop runs without a per-call approval wall. Claude gets an allow-list (OK's MCP tools + the
ok opencommand) with a deny-list that keepsdelete/move/share_link/installprompting; Codex gets its per-serverapprovemode. Other shell commands, non-OK file edits, and other MCP servers stay gated as before. Applied per launch (nothing written to your CLI config files). Toggle in Settings → Terminal ("Let agents use OpenKnowledge without asking",agents.autoApproveOkTools), on by default, per machine. When Codex is installed but OpenKnowledge isn't configured for it, the toggle says so instead of letting Codex quietly keep asking.
Patch Changes
Fix "clone to a new folder" (Open shared document) failing with
Use of "EDITOR" is not permitted without enabling allowUnsafeEditorfor anyone who hasEDITORorGIT_EDITORset in their environment — i.e. most developers.ok cloneruns git as the user with the user's own environment, and simple-git 3.36 refuses to run when it sees an editor env var present (it checks presence, not value) unless explicitly told the env is trusted. The clone path already opted into the sibling PAGER / SSH / askpass flags for the same reason; this adds the missingallowUnsafeEditoropt-in. A clone never launches an editor, so honoring the env is safe.Also hardens the background sync path: the server's
git mergenow runs withGIT_MERGE_AUTOEDIT=no, so a merge commit can never launch an editor and hang the TTY-less sync process.Fix the
writeandeditMCP tools failing with a schema-conversion error on constrained-decoding hosts like LM Studio (Error resolving ref #/definitions/__schema0). Theirfrontmatterfield advertised a recursive JSON Schema ($ref: "#/definitions/__schema0"); LM Studio — and some function-calling APIs like Gemini — can't resolve an intra-schema$refin a tool definition and reject the whole tool. Claude and most MCP clients resolve it leniently, so this only surfaced on local-inference hosts.The
frontmattervalue now advertises a flat,$ref-free JSON Schema (scalar | array | object | null), while a runtime refinement re-applies the exact recursive validation — so accepted/rejected inputs are unchanged for every client and every write path (nestednullis still rejected, deep nesting and heterogeneous arrays still accepted). Added a test that compiles every tool's input and output schema and fails if any emits a$ref, so this can't silently regress.Keyboard shortcuts for staging a selection into an AI CLI in the terminal, plus new terminal tabs:
- ⌘J (Show/Hide Terminal): with text selected in the editor, ⌘J stages that selection into an AI CLI's input in the terminal — not submitted, so you can add context and press Enter yourself. If the active tab is already running a CLI (claude/codex/…), the passage goes into its prompt (no screen wipe); otherwise a new CLI tab opens and the passage is staged into it. With no selection, ⌘J toggles the terminal as before.
- ⇧⌘J: opens a new terminal tab. With text selected, it opens a new CLI tab with the passage staged into its input; with no selection, it starts a new chat with your preferred CLI.
The passage is grounded the same way the "Ask AI" selection button does (doc reference + text). Nothing is auto-run — the selection is staged for you to review, extend, and send. Brings AI-in-terminal keyboard parity with Cursor/Zed/VS Code.
Add LM Studio as a supported AI tool.
ok initand the desktop consent dialog now register the OpenKnowledge MCP server into LM Studio'smcp.json, so a locally-hosted model in LM Studio's chat can read, search, and write your knowledge base through OK's tools — fully local inference, nothing leaves your machine.LM Studio is an MCP host that follows Cursor's
mcp.jsonnotation, so the entry is the same resilient stdio launcher every other editor gets. It's user-global only (no project-scoped config, like Claude Desktop). OpenKnowledge probes both the documented~/.lmstudio/mcp.jsonand the location LM Studio actually uses on macOS (~/.cache/lm-studio/mcp.json, per lmstudio-ai/lmstudio-bug-tracker#1371) and writes wherever LM Studio already keeps its config.
Patch Changes
The text-selection formatting toolbar now stays within the editor's visible content area. Previously, selecting text and then scrolling let the floating toolbar ride over the bottom status bar and the Ask AI composer (collapsed or open). It now hides when the selected text scrolls out of view — behind the top toolbar, the composer, or past the edge of the pane — and reappears when the selection scrolls back in.
Code block chrome (language picker, edit source, hide preview, settings, copy, delete) now floats above the block on hover instead of overlapping the top-right corner — same visual pattern as the text-selection bubble menu, so it does not sit on top of an html preview iframe. Adds a sparkles Ask AI button next to copy that grounds the whole fence (language + meta + body) as the passage, wired through the same composer/terminal path as the text-selection Ask AI. Hidden inside an embedded agent host, same as the text-selection Ask AI.
The
entity-vaultstarter pack skill now covers ingesting meetings from a recorder. When a meeting-recorder MCP (Granola, Fireflies, Circleback, tl;dv, Fathom) is registered alongsideopen-knowledge, the agent pulls recent meetings and writes each one intomeetings/, addressed asmeetings/<source>-<source_meeting_id>so re-syncing a meeting updates it in place instead of creating a duplicate. Meeting notes ingested this way carrysource:andsource_meeting_id:frontmatter, which is the dedup key. Transcripts stay verbatim, and the recorder is bring-your-own, so the same behavior works on demand or driven by a scheduler. The pack'smeetingtemplate now carries thesourceandsource_meeting_idkeys and a## Transcriptsection, so a meeting you author by hand and one pulled from a recorder share the same shape.Clicking a label in a rendered mermaid diagram now places the cursor where you clicked, like clicking into ordinary text, instead of selecting the whole label. When the click can't be mapped to a character, the cursor lands at the end of the text rather than selecting everything.
While you're editing a label in a Mermaid diagram, the other places the same text appears now update live as you type — for example, editing a sequence-diagram participant's top box updates its bottom box in real time, before you press Enter. The preview is local to your view until you commit, so it doesn't interfere with real-time collaboration; box sizes reflow once you commit.
Clicking to edit a Mermaid label that wraps onto multiple lines no longer reflows the text onto a single line while editing. The edit surface now keeps the label's exact wrapped layout — same line breaks, same box — so entering and leaving edit mode is visually seamless, and it grows downward if your edit needs more lines.
Fix: the first-run onboarding card now appears when you start a project from a starter pack, not just from a blank project. A starter pack seeds content at create time, which previously made the card's "fresh project" check treat the project as already-established and stay hidden. The card now keys off the first-run create-new flow, so both blank and starter-pack projects get onboarding — while opening a pre-existing populated folder still correctly stays quiet.
Also fixed: on a starter-pack project the "Create your first file" step no longer auto-completes from the pack's seeded templates. The step now baselines the entry count at activation and completes only when you author a document beyond the seed, so the checkmark reflects an action you actually took.
Fix the "Open shared folder" dialog showing a heavy, misplaced outline around the "Clone to a new folder" card when you open a share link. The dialog auto-focuses that card, and its always-on 2px primary-colored border (with no fill) stacked with a hard, full-opacity focus ring read as a stray blue outline whose focus state overflowed the card.
The two choice cards now use the app's standard card treatment: the recommended clone card gets a subtle 1px primary border with a light primary tint (an intentional highlight instead of a bare outline), and both cards adopt the app-wide focus-visible ring, which suppresses the native outline and renders a soft, contained focus halo consistent with every other control.
Windows: hide the remaining brief console windows that could flash during CLI and MCP operations. A recent release stopped the server's per-edit
gitreads from popping a console window; this extends the same hiding to the hand-rolledchild_processspawns on the CLI side — the MCP host launching the server (ok start) and the UI (ok ui), the git worktree probe, project-root detection,sharegit calls, thenpmuninstall step, the browser opener (cmd /c start), and diagnostics. Each now runs with the console hidden. No effect on macOS or Linux (the flag is a no-op there).
Patch Changes
- Restore npm publishing for the beta channel. npm 12.0.0 ships without the bundled
sigstoremodule its publish-with-provenance path requires, which broke the npm uploads for0.28.1-beta.0and0.28.1-beta.1(their GitHub releases and macOS builds shipped normally). Release CI now pins npm to the 11.x line, and this release carries all changes from the two npm-skipped versions. - Standalone Mermaid files (
.mmd/.mermaid) are now editable, matching the WYSIWYG editing you already get for```mermaidcode fences inside a document. Opening one of these files shows the same rendered diagram — click a flowchart node/edge label, a sequence message, or a participant to edit it in place — and the toolbar's source toggle now opens an editable source view (with Mermaid syntax highlighting) instead of a read-only one. Edits autosave to the file and sync live, since these files are now backed by the same real-time collaboration and undo as every other document. - When you edit a label in a Mermaid diagram and the change propagates to other places the same text appears — for example, renaming a sequence-diagram participant updates both its top and bottom boxes — those other occurrences now briefly flash, so it's clear they changed too. Same visual cue as the flash the editor already uses when it absorbs an agent's edits. A one-off label edit with no linked occurrences stays quiet.
- Fix desktop terminal lagginess and resize flicker. Typing and scrolling no longer pay for xterm's screen-reader mode when no assistive tech is attached — the mode (which mirrors the whole viewport into a live accessibility DOM on every write and scroll) now follows the OS screen-reader signal, turning on the moment VoiceOver attaches and staying on for screen-reader users. Keystroke echo latency from output coalescing drops from 12ms to 5ms (VS Code's bufferer window), kept trailing-only so a TUI redraw burst always lands as one atomic push (no partial-frame tearing). Resizing sections with a terminal open no longer SIGWINCH-repaints the running TUI on every pointer frame — the PTY resize is throttled leading+trailing while the xterm grid fits per event. And resizing no longer flashes the terminal blank: setting a canvas's width clears its bitmap while xterm's repaint is rAF-scheduled (a frame late), and the WebGL addon's device-pixel observer re-clears a second time in the same frame when a fractional panel width snaps differently to device pixels — both clears now flush a synchronous repaint before the frame paints, verified frame-by-frame with screencast captures (32 blank frames per drag before, zero after).
Minor Changes
Link authoring in the visual editor now matches the conventions of the editors you already know. Typing a full URL (
https://…,www.…, or an email) and pressing space or enter converts it to a link — including local-development URLs likehttp://localhost:5174— while plain words and filenames likeAGENTS.mdare never touched, and one undo restores plain text. Pasting or dropping a lone URL links it; pasting a URL over selected text links that text instead;Cmd+Shift+Vstill pastes plain. Typing markdown's[text](url)shorthand converts on the closing parenthesis.Cmd+Kis now dual-role: with text selected it opens the link popover (focused, pre-filled from your clipboard when it holds a URL), with the cursor inside a link it opens that link for editing, and everywhere else it keeps opening the command palette. Linkification never fires on content written by other collaborators or agents — only on your own local typing and paste gestures. Also fixes two long-standing popover bugs: the URL input now reliably takes focus on open, and Escape correctly closes the popover after dismissing path suggestions.Settings → This project gains an "AI tools" section — the project-scoped sibling of the "AI tools & CLI" section. It manages OpenKnowledge's project-local footprint for the project the window has open: the per-editor project MCP config files (
.mcp.jsonfor Claude Code,.cursor/mcp.jsonfor Cursor,.codex/config.tomlfor Codex, and the OpenCode/Pi equivalents) and the project runtime skill (.claude/skills/open-knowledge/). Every row reflects its live installed state and a click installs or uninstalls that one component; removal follows the same guest discipline as install (only entries OpenKnowledge recognizably wrote are deleted). Because a written project config is not always a connected one, each installed row states its remaining step — Claude Code needs a one-time approval, Cursor must be enabled in its own settings, Codex auto-connects on a trusted project. Editors with no project surface (Claude Desktop) are omitted. Mutations resolve and serialize against the sender window's project in the desktop main process.This also fixes a related leak in the existing "AI tools & CLI" (user-global) section: project-only editors such as Pi have no user-global MCP config, so they surfaced there as a permanently-unmanageable row. The global section now lists only
scope: 'global'editors; Pi is managed in the new project section instead.Add manual rename to terminal tabs. Double-click a terminal tab (or press F2 while it's focused) to give it a name of your own, edited inline. The custom name pins over whatever title the running program sets via its terminal title escape sequence — useful for agent CLIs that constantly rewrite their title — so the tab keeps the name you chose. Press Enter or click away to commit, Escape to cancel; committing an empty name clears the custom label and restores the program's title (or the positional "Terminal N" default).
Reorder terminal tabs. Drag a terminal tab to a new position with the pointer, or move the active tab with ⌘⇧← / ⌘⇧→ (keyboard reorders are announced to screen readers). Untitled tabs now carry a sticky number that stays with the session, so reordering them is visibly meaningful instead of silently renumbering, and ⌘1–9 still jumps to the tab at that visual position. The drag reuses the editor's tab chrome, and in the standalone terminal window a drag across the tab strip reorders tabs instead of moving the window. Your custom names and tab order are retained by the main process, so a renderer reload (View → Reload) restores both names and order; a full app quit still starts fresh, since the shells themselves don't survive a restart.
Patch Changes
- Clicking anywhere in an Ask AI composer now focuses its input. Previously only the text itself was a focus target, so the padding around the field, the gaps beside the send button, and the empty space in the card were dead — a click there did nothing. Both composers get the standard chat-composer behavior: the bottom docked composer (open-doc and folder-overview modes) and the empty-state "Create with AI" composer. Keyboard and screen-reader users are unaffected — the input keeps its own semantics and is still reached directly via Tab and ⌘L.
- Fix git rename detection for non-ASCII filenames in two more places. A shared doc or folder whose name contains non-ASCII characters (umlauts, accents, CJK) that is renamed on the remote now reports its correct new location instead of showing as deleted, and version-timeline entries recover the real document name instead of garbled text. All git path-listing now routes through a single NUL-safe helper (
git-paths.ts) so this class of bug cannot recur at new call sites. ok deinit(and the per-project sweep inok uninstall) now removes OK-installed skill projection symlinks (pack and authored skills under.claude/skills/,.cursor/skills/, etc.) instead of refusing them with "Refusing to write through a symbolic link" — removal unlinks the link itself and never touches its target. Dangling projection symlinks are cleaned up too; the refusal for symlinked ancestors that escape the project is unchanged.- Editing inside a Callout, tab, accordion, or table cell in the visual editor now always reaches collaborators and reopens with exactly the content you see. Previously the editor could hold a cached copy of a component's original source and, when the inside was edited, serialize those stale bytes for everyone else — your own editor looked right while a teammate (or a fresh reopen) got the pre-edit content, or in rare cases structurally-broken markdown. The server now re-derives a component's markdown whenever its live contents diverge from that cached source, regardless of which surface made the edit, so what you see is what everyone gets. A drain-time legality check backs this up: if a serialization would still lose content, it is caught at the moment the bytes are written and a recoverable snapshot is kept instead of shipping the corruption.
- Fix the
execMCP tool being unable to read any file below the project root on Windows.cat,ls,grep, andfindagainst a subpath (e.g.cat notes/x.md) returned "No such file or directory" even though the file existed, because the bundledjust-bashsandbox judged every backslash-separated Windows path as "outside sandbox". Bumpingjust-bashto the release that handles Windows path separators restores subpath reads/listings; only genuinely-absent files now report ENOENT. macOS and Linux are unaffected. - Fixed symbolic links (mode
120000) being automatically staged as deleted in the background Git sync loop. The filesystem scanner now correctly walks and includes symbolic links inside the content directory so they are recognized as on-disk files and are not incorrectly removed from Git tracking during sync commits. - Hide server-launched Git and helper subprocess console windows on Windows by setting hidden console spawn options on direct child-process calls.
- Make the
open-knowledge-uientry in.claude/launch.jsonlaunchable on Windows. Previouslyok initalways wrote a/bin/shchain, so Claude Code Desktop's preview pane could not start the OpenKnowledge UI on Windows (there is no/bin/sh). On Windows,ok initnow scaffolds apowershell -NoProfile -NonInteractive -Command <chain>entry that resolves the npm-globalok.cmdshim first, thennpx.cmd, then common version-manager/installer paths, and runsok start --ui-portso the opened folder gets its own collab server. Theok startlaunch.json repair sweep now recognizes both platforms' canonical shapes on every OS, so alaunch.jsoncommitted on one platform is never rewritten back and forth by the other, and custom entries are still preserved. - Fixed the project switcher's worktree list not refreshing until an app restart. A worktree created outside the current window — a
git worktree addin a terminal, or a create in another OpenKnowledge window — now appears when the window regains focus, instead of staying hidden until you quit and reopen. The cached worktree model now revalidates on window focus / tab-visible, the same signal the file list and graph already use to recover from stale data.
Minor Changes
- First-class support for the Pi coding agent (pi.dev), at parity with OpenCode. Pi has no MCP support, so
ok initnow drops a managed, dependency-free bridge extension at.pi/extensions/open-knowledge.ts— it launches OpenKnowledge's MCP server through the same resilient launcher every other editor gets and registers each OpenKnowledge tool as a native Pi tool under anok_prefix (so OK'sedit/writenever shadow Pi's built-ins). The OpenKnowledge project skill lands in.pi/skills/open-knowledge/, which Pi scans natively; both are trust-gated by Pi's folder-trust prompt, refreshed idempotently on re-init, healed by the repair/reclaim sweeps, and removed byok deinit/ok uninstall. Pi also joins the docked-terminal launch registry (pi '<prompt>') and skill install targets, and gets an integrations docs page. - Settings → User gains an "AI tools & CLI" section — the first-launch "Connect your AI tools to OpenKnowledge" dialog is now a persistent, stateful surface in the desktop app's settings. Every global component is listed with a checkbox reflecting its live installed state, and clicking installs or uninstalls that one component: the per-editor
open-knowledgeMCP entries (Claude Code, Claude Desktop, Cursor, Codex, OpenCode, OpenClaw), theokshell-PATH shim, and the user-global Agent Skills (open-knowledge-discovery,open-knowledge-write-skill). Removal follows the same guest discipline as install: only entries OpenKnowledge recognizably wrote are deleted (customized/forked entries are reported and left byte-unchanged), PATH removal strips only the managed rc block and records a declined consent so startup self-heal never re-appends it, and skill removal records the per-bundle decision every install actor honors.
Patch Changes
Keep the first-launch "Added ok to your PATH" notification clear of the MCP consent dialog. It now anchors bottom-left instead of covering the dialog's Add/Skip buttons, and toasts once again render above modal overlays so notifications from in-dialog forms stay visible and dismissible.
Deleted docs now stay deleted. Two long-standing gaps let a deleted or renamed-away markdown file silently re-materialize on disk with its old content:
- Deleting or renaming through the app, the HTTP API, or the MCP
deletetool could race a pending autosave — the store fired after the file was removed and rewrote it at the old path (externalrmwas already protected; the in-app paths were not). Every teardown path now marks the doc as no-longer-tracking-disk before connections close, so late stores are suppressed instead of resurrecting the file. - Deletions did not survive a server restart: every anti-resurrection
guard was in-memory, so a browser tab (or any client) still holding the
doc's cached state would be admitted after a restart and re-create the
file — including files deleted while the server was stopped. Removals
are now journaled durably under
.ok/local/, reloaded at boot, and files that vanished during downtime are detected and tombstoned at the next start. Rename redirects for stale tabs also survive restarts now.
Re-creating a doc on purpose (new file at the same path,
create-page, agent writes) keeps working exactly as before.- Deleting or renaming through the app, the HTTP API, or the MCP
Add Files sidebar keyboard shortcuts for duplicating and deleting files and folders, including copy/paste duplication.
Add the editor's footer fade to the folder view while the Ask AI composer is open. The folder list now dissolves softly into the background just above the composer instead of meeting a hard edge, matching the fade already shown in the document editor. The fade only appears when the Ask AI composer is visible.
Restore two extension-normalization behaviors that regressed when the file tree began preserving exact
.md/.mdxdocument identities.- Share-receive miss detection again recognizes an extension-bearing armed navigation against the resolver's extension-stripped missing target, so a moved or deleted shared doc renders the honest miss panel instead of falling into create-mode at the shared path.
- An extension-only file rename (same base name,
foo.mdtofoo.mdx) once more performs the on-disk move without recording a phantom rename-history entry, since the two paths refer to the same logical document.
Redesign the "What gets created" preview in the Initialize dialog so it's easier to read at a glance. The preview now groups what a starter pack scaffolds into clear Folders, Files, and Skill sections, each row showing the item's name and a one-line description of what it's for. The old collapsible file tree (which also exposed internal
.ok/paths) is gone — the sections carry the same information more legibly. Hovering or focusing a folder's template count now reveals the names of the templates it installs.
Minor Changes
- New projects can now start from a starter pack right on the first-run launcher. When you open OpenKnowledge with no recent projects, the launcher leads with the starter packs (Knowledge base, Software lifecycle, Codebase wiki, and more) so a brand-new project opens already scaffolded with folders and templates instead of a blank editor. Picking a pack flows into the create dialog with the pack pre-selected; you still name the project and choose where it lives, then the editor opens populated. Prefer to start differently? Open a folder, clone from GitHub, or start a blank project from the same screen. Returning users (anyone with recent projects) see the launcher exactly as before.
Patch Changes
Fix copying from the editor dropping line breaks when you paste into other apps.
Soft line breaks — the everyday single-newline wraps inside a paragraph, blockquote, or list item — used to be copied as a bare newline whose rendering relied on the editor's own styling. Gmail and other rich-text destinations strip that styling, so the lines merged into one on paste. They now copy as real line breaks that survive everywhere. Copying a partial selection that contained a
<br>line break used to fail silently and paste as raw markdown source (pipes, dashes, and literal<br>text); those breaks now come through as proper line breaks too.Multi-line table cells also copy correctly to spreadsheets: a cell with an in-cell line break is exported as a quoted field so Excel and Google Sheets keep both lines in one cell instead of merging them or splitting the row.
Checked items in a task list now keep their checked state when copied, so a ticked checkbox no longer pastes as unticked in apps that preserve checkboxes.
Reworked the first-launch "Connect your AI tools to OpenKnowledge" consent dialog. The description copy is now plain-language instead of listing internal implementation details, undetected editors show a "How to set up" link to their integration docs, and the shell-PATH rc-file detail moved into an info tooltip.
Fix duplicate servers per project during teardown/restart windows. Previously a server released its
server.lockseconds before its process actually exited (and, after an idle shutdown, sometimes never exited at all), so restart flows — desktop relaunch, auto-update, MCP auto-start — could spawn a second server alongside a still-live predecessor, losing in-app threads and splitting writes from the preview. The lock now stays owned (markeddraining) until the process truly exits; spawners wait out a draining predecessor instead of racing it; the desktop waits for process death rather than lock disappearance before respawning; and idle shutdown now exits the process explicitly, logging any leaked handles that previously produced immortal zombie servers. Lock identity also moved from the OS hostname (which macOS renames on network changes) to a stable per-machine ID at~/.ok/machine-id— a hostname rename no longer lets a new server silently steal a live server's lock; ambiguous cases now fail closed with a clear "already running" error. When the desktop can't open a project because a conflicting server holds its lock, the error dialog now offers "Stop Server & Retry" to resolve it in one click.Fixed the desktop in-app terminal failing on every open with "The terminal stopped unexpectedly." in v0.25.0 (Restart Terminal included). Moving node-pty to optionalDependencies had stopped electron-vite from externalizing it, so the packaged pty host bundled node-pty's loader and could no longer reach the native binding. node-pty is now explicitly externalized in the main-process build, and a packaging guard test pins the seam.
Opening a workspace that contains same-stem
.mdand.mdxfiles, such asfoo.mdandfoo.mdx, no longer blanks or wedges the app. Show All now lists both files, URLs keep the extension when needed, both siblings open and edit independently, and sidebar actions like share, handoff, rename, delete, and duplicate target the selected sibling instead of collapsing to the extensionless stem.Correct user-facing copy that described behavior that doesn't exist, and finish the Personal CRM rename in seeded content:
- The OKF pack's seeded skill no longer claims an "OKF export" normalizes
[[wiki-link]]shorthand to standard links — no export feature exists; OpenKnowledge accepts the shorthand as a native superset and preserves it byte-for-byte. - The
entity-vaultpack's seeded skill now titles itself Personal CRM (GBrain-compatible), matching the starter-pack picker and theok seedoutput. Applies to newly seeded projects; an existing project's skill is user-owned and left untouched. ok seed --helpno longer promises a--rootprompt on a TTY — omitting--rootscaffolds at the project root (only the apply confirmation prompts).- The template dialog's filename hint no longer says the filename "can't be changed later" (local templates are renamable from the edit dialog), and the description placeholder says "under the title", matching the field it sits beside.
- The OKF pack's seeded skill no longer claims an "OKF export" normalizes
Fix search snippets splitting an emoji across the truncation boundary, which left a lone UTF-16 surrogate in the JSON-RPC response and caused strict MCP clients (Rust / pydantic parsers) to reject it as invalid UTF-8. Snippets are now well-formed before serialization.
Minor Changes
Add per-skill opt-in for the two user-global OpenKnowledge skills (
open-knowledge-discoveryandopen-knowledge-write-skill). The first-launch desktop consent dialog now shows a checkbox per skill (pre-checked), andok initaccepts--skills <ids>/--no-skills. The decision is recorded in~/.ok/skill-state.ymland honored by every install path — desktop launch reclaim,ok start,ok repair-skills, andok init— so a declined skill is never re-installed, and unchecking one that is already installed removes it. Existing installs are grandfathered (treated as opted in, nothing removed).ok initnow installs both user-global skills when enabled instead of onlydiscovery.A new Terminal → New Terminal Window command opens a terminal in its own dedicated window, so you can keep a full-height shell alongside the editor instead of sharing the docked panel's vertical space.
- The window reuses the docked terminal's multi-session tabs: open more shells with the
+affordance, switch with ⌘-number, and each tab is its own login shell. The ⌘-number chord is always active here since the whole window is the terminal. - Launched from a project, the window inherits that project's cwd and config (its shells start at the project root); launched with no project focused, it opens a shell at your home directory.
- Open it as many times as you like — each invocation is an independent window with its own terminal host, including multiple windows for the same project.
- Closing the last tab closes the window, and closing a window reaps all of its shells (no orphaned processes).
The command matches VS Code's "New Terminal Window" and ships without a keyboard shortcut; ⌘J stays the docked Show/Hide Terminal toggle. The docked terminal is unchanged, and the security posture is the same: terminals remain human-only and default-on, with no new IPC surface.
- The window reuses the docked terminal's multi-session tabs: open more shells with the
Add
ok uninstallandok deinit— one command each to reverse OpenKnowledge's footprint.ok uninstallremoves OpenKnowledge from your whole machine: it stops running servers, clears credentials (the GitHub keychain token,auth.yml, and the embeddings key), reverts the PATH shim (strips the managed block from your shell rc files and removes the recorded~/.ok/binsymlinks), surgically removes only OpenKnowledge's own entry from each editor's MCP config (keeping your other servers, comments, and formatting), tears down the installed skill bundles, deletes the app-data directories, and finally removes~/.ok. It offers to also clean recent projects, and detects how the app was installed to print the exact removal command — it never deletes its own binary. Your markdown content and your authored skills (~/.ok/skills) are kept unless you pass--purge-content.ok deinitremoves OpenKnowledge from a single project — its.ok/, editor MCP entries,.claude/launch.jsonentry,.git/info/excludelines, and shadow repo — while leaving your markdown untouched, so re-runningok startre-scaffolds cleanly.Both commands are safe by default: a
--dry-runpreview, a confirmation that defaults to No,--jsonoutput, and surgical edits that never clobber your non-OpenKnowledge config.Add first-class OpenClaw support to
ok init. OpenClaw is now a detected editor: when~/.openclaw/exists,ok init(and OK Desktop's first-launch consent dialog) register the OpenKnowledge MCP server in~/.openclaw/openclaw.jsonundermcp.servers, using the same resilient launcher every other editor gets — it findsokwhether you installed the desktop app or the npm CLI, so there's no PATH to hand-configure. OpenClaw is only ever configured when it's actually installed: the detection gate holds even in the desktop consent flow, so~/.openclaw/openclaw.jsonis never written on a machine without OpenClaw. The format-preserving JSON writer now handles editors whose server map nests one level deeper (mcp.servers.<name>), preserving comments, key order, and unrelated entries byte-for-byte.Share links now tell you at share time when the link won't show what you see. When you copy a share link for a doc or folder that isn't on GitHub yet, or that has unpushed changes, the share popover shows a non-blocking warning — for example "This doc isn't on GitHub yet. The link won't work until it's pushed." or "This doc has unpushed changes. Recipients will see the last pushed version." The warning stays out of the way when the target is already up to date (and when auto-sync will publish the change on its own). The
share_linkMCP tool relays the same signal so agents don't hand over a dead link as if it were good. The freshness check is local-only — it never reaches the network, and it never blocks or fails the share.Opening a share link whose target moved or vanished now gives an honest, cause-specific answer instead of a misleading "not on this branch yet." When the shared doc or folder is missing on your current branch, the branch-switch dialog checks GitHub and tells you what actually happened: if it was just added to the branch, "Switch and update branch" fast-forwards your local branch and opens it; if it moved, you're offered to open it at its new path; if it was removed, you're told so plainly; and if it was never pushed, you're told that distinctly (never "removed"). If your local branch has diverged from GitHub, the update is refused and a plain switch is offered with an honest note — the receive flow never merges or rebases, leaving reconciliation to sync. When the check can't reach GitHub, it falls back to today's behavior.
Opening a share link no longer drops you into an empty "start writing" editor when the shared doc isn't on your checkout — the trap where typing would silently fork a new doc at the shared path. When a share-receive navigation lands on a target your branch doesn't carry, the editor now renders an honest panel that checks GitHub and explains what happened: your local copy is just behind (pull to get it), the doc moved (open it at its new path), it was removed, or it was never pushed to the branch. Each panel offers a Browse folder escape to the parent folder, and the check falls back to today's pull guidance when GitHub can't be reached. Ordinary in-app navigation (wiki links) still creates a new page on navigate as before.
Patch Changes
Fix the Git auto-sync and semantic-search toggles silently having no effect until the server restarts. Config changes made in the editor are now applied to the running server immediately, without depending on the filesystem watcher echo (which could drop the event on some platforms, notably Windows). The server now re-applies a validated config change to its live consumers directly at persist time, so toggling auto-sync or semantic search on or off takes effect right away.
Show the "Ask AI" composer in a regular browser, not just the desktop app. Previously the bottom composer was hidden everywhere except OK Desktop, which was an oversight — it now appears whenever you have a document (or folder) open. It stays hidden only when OK's preview is embedded inside a desktop coding agent (Claude Code, Codex, Cursor), where that agent is already the AI surface. On the web the composer degrades cleanly: no docked-terminal CLI options, and the agent picker deep-links to your locally installed agents.
Also hardens the composer so it never grabs focus when a document opens — it focuses only on an explicit reopen — so opening or creating a file no longer pulls the caret out of the editor or an in-progress inline rename.
The "Ask AI" button in the text-selection toolbar now appears on every platform too (it was macOS-only because the composer used to be), so selecting text in a browser on any OS offers the same one-click handoff. Its Ctrl+Shift+I keyboard shortcut stays macOS-only, since that chord is the browser DevTools shortcut on Windows/Linux.
Fix spurious bridge warnings and re-derive churn on documents containing CommonMark lazy continuations.
Documents whose markdown carries a lazy continuation — an unindented wrapped line inside a list item, a paragraph glued directly under a list's last bullet, or a blockquote continuation without the
>prefix — previously emitted recurringbridge-invariant-violation(tolerance-classuntracked) andbridge-split-brain-rederivewarnings, re-derived the editor fragment on every settlement, and re-ran fragment reconciliation on every save, even though the document parsed correctly and no data was ever at risk.The bridge health checks now recognize parse-equivalence: when the stored bytes and the canonical serialization parse to the same document, the resting byte difference is reported through the
bridge-tolerance-appliedchannel (classparse-equivalence) instead of alerting, and no re-derive or reconciliation churn runs. Genuine divergences — where the editor state does not match what the stored markdown parses to — keep alerting exactly as before. Stored bytes are never rewritten; your authored lazy-continuation form stays on disk verbatim.Opening a share link with the app fully closed now reliably lands on the shared target. Previously, if macOS delivered the link a beat after launch, the app restored your last-open project first and the clone dialog for a repo you don't have locally was immediately buried behind that restored window.
The desktop boot path now waits for cold-start link delivery to settle (a short grace window that ends early the moment a link claims the launch) before deciding whether to restore the previous project. A launch without any link still restores your last project as before; the wait overlaps startup work, so it adds no noticeable delay.
Fix the merge-conflict controls (Exit merge / Undo / Save resolution) being covered by the floating Ask AI composer, which made conflicts impossible to confirm without first collapsing the composer. The conflict footer now publishes its height and the composer anchors above it, so the controls stay visible and clickable with Ask AI stacked on top of them. The conflict diff also reserves scroll room under the composer, so the last hunks' Accept and Reject buttons can always scroll clear.
The create composer's "Open the OK editor in web view." directive now rides its own paragraph. It was appended to the last line of the prompt body, which glued it onto the final line of your blockquoted brief (or the last
@-mention), so it read as part of your own quoted words — effectively invisible as an instruction to the receiving agent. Bare create prompts (no brief, no mentions) keep the same single-line shape as the other handoff prompts, and turningappearance.preview.autoOpenoff still drops the directive entirely.Fix git sync mishandling files whose names contain non-ASCII characters (e.g.
ä,ö,å, accents, CJK). Git quotes such names in its output ("hyv\303\244\303\244 y\303\266t\303\244.md"), and the sync engine reused that escaped string as a literal path, so:- Deleting a non-ASCII-named file was never committed — the deletion silently failed to sync on every cycle, forever, until the file was removed manually with
git rm. - A merge conflict on a non-ASCII-named file was misclassified and could abort the merge and pause sync instead of surfacing the conflict for resolution.
- Auto-save commit messages and the conflict list showed the escaped gibberish instead of the real filename.
Git path output is now read NUL-separated (
-z), so filenames round-trip as real UTF-8 everywhere the server parses paths: the sync push/delete cycle, conflict detection and resolution, dirty-file overlap checks before branch switches, and skill version restore.- Deleting a non-ASCII-named file was never committed — the deletion silently failed to sync on every cycle, forever, until the file was removed manually with
Fix the right-docked terminal and the doc panel fighting over space. Six confusing interactions are gone:
- Dragging the terminal narrower no longer pops the closed doc panel open — the space returns to the editor.
- With the doc panel closed, the two resize handles no longer overlap at the same pixel seam — a drag aimed at the terminal edge can't land on the (now inert) doc-panel handle and expand the doc panel instead.
- Closing the doc panel while the terminal is open no longer inflates the terminal; the editor absorbs the width, and the terminal keeps its own size.
- The terminal column can now be dragged shut: dragging it below half its minimum width snaps it closed and hides it on release, matching the doc panel's drag-to-close affordance. (The hide button still works.)
- Hiding the terminal no longer resurrects a doc panel you had closed while it was open (and showing the terminal no longer restores a stale doc-panel state either) — the doc panel keeps whatever state it was in.
- Collapsing the left file sidebar no longer widens the terminal column (measured 480px → 673px before the fix) — the editor absorbs the freed space while the doc panel and terminal hold their pinned widths.
Closed panels are now consistently button-only to reopen: a collapsed doc panel no longer exposes a draggable rail handle (use the toolbar toggle or ⌥⌘B), matching the terminal, whose handle disappears when it is hidden.
bun installon a fresh clone no longer fails on machines without a C build toolchain. The desktop app'snode-ptynative dependency is now optional (its build is only needed for the macOS desktop terminal), and puppeteer's Chrome-for-Testing download (~600 MB on disk) — used only by an engineer-local memory probe — is skipped at install time (bunx puppeteer browsers install chromefetches it on demand).Make the right-docked terminal consistent with the bottom dock on the "new tab" (empty editor) screen: opening a terminal in either position now collapses the empty state to its header, dropping the chat composer bubble and starter-pack list — the open terminal is its own AI entry point, so the bubble no longer competes with it. The header bottom-anchors above a bottom dock and centers beside a right-docked terminal.
The right-docked terminal can also be resized much wider now: the fixed 900px cap is gone, and the column can grow to near-full width while the editor always keeps a minimum visible sliver — mirroring how tall the bottom dock can already be dragged. A wide width persists across reloads instead of snapping back.
When the desktop app can't reach a project's collab server, the "couldn't reach server" banner now offers a Restart server button alongside Retry. Retry only re-attempts the same server, which never succeeds once that server has stopped (for example after an idle-shutdown); Restart spawns a fresh one so the window recovers instead of retrying a server that will never answer.
Also routes the desktop keepalive's connect / disconnect / reconnect lifecycle through the logs. That keepalive is what keeps a project's server alive while its window is open — when it silently fails to hold the connection the server can idle-shut-down with the window still open, and there was previously nothing in the logs to explain why.
The share-receive "changed locally" panel no longer offers to enable auto-sync when it is already on. With sync enabled it now offers Sync now instead: the push publishes your local move/rename/delete, and the panel re-checks the branch and pivots to the honest outcome — including the "moved to
— open it there?" redirect when the doc was renamed. With sync off, the Enable auto-sync flow is unchanged, and when the sync engine is unavailable or failing the panel defers to the sync badge rather than offering a dead button. The macOS app now asks before editing your shell startup files. Putting
okon yourPATHused to happen silently on first launch — a managed block appended to~/.zshrc/~/.bash_profile/ fish'sconf.d. That write is now a pre-checked "Add theokcommand to your terminal" toggle in the first-launch dialog, and nothing touches your shell config if you uncheck it. Declining only affectsoktyped in an external terminal: the app's built-in terminal now hasokavailable regardless, and MCP wiring and "Open with AI" launches never depended on it. Decisions are recorded inpath-install.json; machines that already have the managed block are treated as consented (no re-prompt, block left in place), deleting the block still opts out permanently, and a declined install can be re-run any time from File → Set up OpenKnowledge integrations… (the renamed "Configure AI tool integrations…" item).Fixed extra left indentation under the project name and in the Skills section of the file sidebar, introduced when the Skills viewer was added — both sections now align with the file tree's rows.
Refine the empty-state starter-pack picker. Secondary packs now collapse behind a "Show more" toggle with an animated reveal, an info tooltip explains what a starter pack is, and each pack's folders show short plain-language summaries instead of the agent-facing descriptions. The "what gets created" preview uses clearer folder / file / skill cards (the skill card shows the pack name with a "Skill" tag) and a simpler card-shaped loading state.
Fix tables corrupting for collaborators when a cell holds multi-line content.
Pressing Enter, Shift+Enter, or adding a list inside a table cell used to serialize raw newlines into the markdown row — the table stayed intact in your own editor but split into a truncated table plus stray bullet lines for everyone else re-opening the file (teammates, restarts, fresh checkouts), and adjacent lines could glue together with their boundary lost. Multi-line cell content now serializes to single-line GFM rows joined by
<br />, the standard idiom GitHub renders as a line break: extra paragraphs become line breaks, list items keep a-(or1.) marker on their own visual line, and nothing in the row ever emits a raw newline. The same guard covers Shift+Enter inside headings.In the other direction,
<br>,<br/>, and<br />in markdown now parse as real line breaks in the editor instead of showing as literal angle-bracket text, and each spelling is preserved byte-for-byte on save. Backslash-escaped\<br />and<br>inside inline code stay literal, as authored. A structurally empty table (no cells) now serializes to nothing instead of junk||bytes that reparsed as a text paragraph, and a childless component inside a cell now fires the structured drop event instead of vanishing silently.The bottom resize grabber no longer lingers when the terminal is docked to the right column. Previously the grabber stayed visible and could be dragged up to reveal an empty bottom panel; it now disappears while the terminal is right-docked and returns when you dock the terminal back to the bottom.
Add browser-style keyboard shortcuts for editor tabs, including new/reopen tab, tab cycling, and numeric tab jumps.
Fix toasts becoming un-dismissable and covering controls while a modal dialog or sheet is open. Sonner renders its toaster in a portal on
<body>, and a modal Radix layer setsbody { pointer-events: none }, which the toast inherited, so it painted on top of the dialog but its close/action buttons were click-dead (the "Added ok to your PATH" onboarding toast was the visible case). Toasts are now kept interactive under a modal layer, and dismissing a toast no longer closes the dialog or sheet beneath it.Keep the toast stack below an open modal dialog or sheet. The previous toast fix made toasts clickable while a modal Radix layer is open, but sonner's toaster renders above the dialog, so a toast overlapping the modal (like the first-launch "Added ok to your PATH" notice over the MCP consent dialog) intercepted clicks aimed at the modal's own buttons. While a Dialog or Sheet overlay is mounted the toaster now sits under the overlay: the modal stays fully clickable, the toast shows dimmed without pretending to be interactive, and it becomes clickable again as soon as the modal closes.
Typing into a document that carries organic markdown forms (like a list whose next paragraph starts without a blank line) no longer emits spurious
bridge-split-brain-rederivetelemetry on space keystrokes. The bridge health check now treats trailing line whitespace — a byte shape the serializer already declares insignificant — as the documented normalization it is, while genuine divergence keeps failing loud. Keystroke-granularity coverage (per-character transactions, zero-gap bursts, two-peer concurrent typing) is now part of the integration and fuzz suites.Fix the server being unreachable on Windows when your coding agent auto-starts it over MCP. The server used to bind the
localhosthostname, which Windows resolves to IPv6 (::1) only, while the tools connecting to it use IPv4 (127.0.0.1) — soedit/execcalls failed with "Server unreachable: fetch failed". The server now binds numeric127.0.0.1directly (still loopback-only), and the MCP shim and theok uiproxy connect to the same address, so there is no longer any IPv4/IPv6 mismatch on any platform. You no longer need the--host 127.0.0.1/HOST=127.0.0.1workaround. macOS and Linux behavior is unchanged.Generate a Windows-launchable MCP server config instead of the macOS-only
/bin/shchain. On Windows,ok init,ok start, and MCP autostart now registerpowershell -NoProfile -NonInteractive -Command <chain>(resolving the npm-globalok.cmdshim first, thennpx). The reclaim sweep now recognizes both platforms' canonical entries everywhere, so a config written on one OS is never clobbered back and forth by the other, and hand-fixes on Windows stop being overwritten with an unlaunchable entry.Nudge agents toward folder templates on write. When a document is created in a folder that ships templates but the write passed no
template, thewritetool now lists the folder's available templates in its result and suggests passing one next time. The write still lands unchanged — the hint is advisory, so folders that pre-selected a shape stop getting free-form docs just because the agent wrote from memory without listing the folder first.Point the in-app social links at the new X (Twitter) handle
@OpenKnowledge(x.com/OpenKnowledge), replacing the former@OpenKnowledgeAI. Affects the Resources popover and the subscribe card's "Follow us on X" link.
Minor Changes
- Add
ok migrate notion [dir]to clean up a Notion "Markdown & CSV" export in place. It normalizes internal links so they both render and resolve (decodes%20, handles targets with parentheses, and angle-wraps targets containing spaces), converts<aside>callouts to> [!note], lifts database row properties into YAML frontmatter, extracts inline base64 images to files, and renders each database CSV as a markdown table (keeping the per-row pages). Dry-run by default; pass--applyto write. Every transform is idempotent, and the command refuses non-Notion directories unless--force. - Invite OK Desktop users to subscribe to product updates from the post-update release-notes card. When the app updates, the "Updated to Version X · Release notes" card in the sidebar footer now also carries a compact "Stay in the loop" subscribe form (reusing the existing form) with a "Follow us on" row for X, GitHub, and Discord — shown only when the user hasn't already subscribed or dismissed it, and for at most three distinct update versions. It never appears on its own (no update, no prompt) and won't re-nag on reopen. Dismissing closes the whole card and stops the prompt for good; subscribing from here or the Resources menu retires it. All state is device-local; web and CLI are unaffected.
Patch Changes
Add always-on desktop startup instrumentation. Every launch now emits one structured
desktop.startup-timelinelog line with bounded per-phase durations (app-ready → bootstrap → server spawn → lock-ready → window → load → shown, plus the server's HTTP-listen / seed-walk / index / ready timings and the renderer's page-list and first-content marks). The server exposes its boot timings onGET /api/server-info, and the file-watcher seed walk and index phases are now traced (ok.boot.seed-walk,ok.boot.indexes). When OpenTelemetry is enabled, the three processes (Electron main, server, renderer) join into a single correlatedok.app-startuptrace via W3C traceparent propagation. This is instrumentation only; startup behavior and timing are unchanged.execreads now surface more of what OpenKnowledge already tracks, in the human-readable listing an agent reads.lsshows a folder's available templates (name, description, scope), its description and tags, and direct-vs-recursive file counts with the most-recent date;catshows the backlink and forward-link source paths (not just counts) and a coarse graph role (hub / connector / leaf / orphan); both render triage frontmatter (status,type) per file.Stop OK Desktop's "Update to X didn't install" notice from firing spuriously and re-appearing on every launch. The boot-time failed-install detector now handles two cases it previously got wrong:
Cross-channel state. The stable and beta builds share one settings file (same app id), so a version that one channel armed as its pending install could poison the other channel's boot check. A beta build would show "Update to 0.23.0 didn't install" for a stable version it can never install (cross-channel updates are blocked), and there was no way for it to clear, so the notice returned on every launch. That stale cross-channel record is now cleared silently.
No retry bound. A genuinely stuck install (a persistently-failing installer, a pulled release) re-surfaced the notice on every boot forever. It is now shown at most 3 times per failed update, after which the record is dropped. The 7-day "updates paused" hint remains the backstop.
Genuine same-channel install failures still surface as before, so a real failed update is not hidden.
Fix the "Download manually" link in OK Desktop's update notices. When an update fails to install (the "Update to X didn't install" card) or the app detects it hasn't received updates in a while, the manual-download link now points at the GitHub Releases page (https://github.com/inkeep/open-knowledge/releases) where the signed DMGs actually live, instead of a marketing URL that returned a 404.
fix(open-knowledge): make
ok initgit-root promotion visible + add--content-dirWhen
ok initruns in a sub-folder of a git repo, it scaffolds.ok/at the git root and scopes the whole repo as content (one.ok/per git repo). That promotion previously surfaced only as a singleconsole.logline on stdout, whichok init 2>&1 | tail/| headsilently dropped — so a large repo could get whole-repo-scoped without the user noticing.- The promotion is now disclosed as a styled warning on stderr at decision time, and repeated in the init summary directly next to the "Found N markdown files" preview, naming the sub-folder and how to narrow scope.
- New
ok init --content-dir <path>flag scopes content to<path>(resolved relative to the current directory, written to.ok/config.ymlascontent.dir).--content-dir .from a promoted sub-folder scopes the project to that folder. Paths outside the project root, missing paths, or files are rejected with a clear usage error (exit 64). Supplying the flag suppresses the whole-repo promotion warning (the scope choice was explicit). - New
ok init --jsonflag prints a machine-readable summary to stdout (projectRoot,gitRootPromoted,promotedFromDir,contentDir,contentDirRequested,contentDirApplied,contentFileCount,didGitInit,mcpAction,editors[]) so scripts and agents read the promotion + scope signals as fields instead of scraping log text. Diagnostics stay on stderr; stdout carries only the JSON document.
Behavior of the promotion itself is unchanged.
The editor's inline Ask AI button now sends a grounded prompt to the docked terminal instead of the raw selected text. When you highlight a passage and click Ask AI with a terminal open, the running CLI receives the passage together with a reference to the document it came from (or a pointer to read the full passage via the OpenKnowledge MCP server when the selection is large), so the agent can place it in your knowledge base instead of getting an unattributed blob.
Refined the docked terminal's chat controls. Show or hide the terminal with an edge "Show terminal" tab that appears right where you closed it — on the right column or under the editor, depending on where it's docked — and the header's separate chat toggle is gone. The terminal's "new tab" control is now a split button: click it to open a new tab in your current CLI, or use the dropdown to switch CLI (your pick sticks, shared with the Ask AI composer) or open a plain "Terminal" tab (which sticks too, just for the terminal). New-chat now respects a CLI you picked but haven't installed instead of silently falling back, terminal tabs show their full title on hover, and opening the chat no longer collapses the document panel.
Fix mouse-wheel scrolling in the desktop terminal for TUIs that hit-test mouse coordinates (opencode, bubbletea apps). The terminal's mouse-mode wheel reports were pinned to cell 1;1 — fine for claude/vim/less, which ignore the position, but coordinate-dispatching TUIs route the wheel to the component under the reported cell, and the top-left corner is never the scrollable region, so scrolling silently did nothing. Wheel reports now carry the pointer's actual cell (with a viewport-center fallback when the renderer hasn't measured yet), and SGR-pixels mode reports CSS-px coordinates. This also corrects scroll targeting in window-under-pointer TUIs: vim/neovim splits and tmux panes previously always scrolled the top-left window regardless of where the pointer was; they now scroll the hovered one, matching native terminals.
You can now work on several branches of a project at once, each in its own window. The project switcher in the sidebar footer groups your recent projects by repository: a project with open worktrees expands inline to show them (the current window's project is expanded by default), and typing in the search box matches projects, open worktrees, and the current project's branches. Opening a branch that has no worktree yet creates one on demand. "New worktree" lets you start a fresh branch or check out an existing one, and your worktrees also show up in the Cmd-K command palette. Worktrees are stored inside the project under
.ok/worktrees/and kept out of git status automatically, so each window stays fully isolated (its own editor and server) without touching your working copy.
Minor Changes
- Add a first-run onboarding card to OK Desktop. A dismissable checklist in the sidebar footer guides genuinely new users through three momentum steps — create a project (pre-checked), create the first file (⌘N), and ask AI (⌘L) — each showing its keyboard shortcut. Steps check off as the user actually performs them; on finishing all three the card celebrates in place with the OK blob mascot before animating away for good. It shows only for a fresh single-project desktop session and never returns once dismissed or completed (device-local). Web and CLI render nothing.
Patch Changes
Fix the Ask AI composer covering the last lines of a document. Two fixes: the bottom scroll inset now reserves room for the composer's gradient-fade band (not just the card), and in source/markdown mode the inset is no longer zeroed by CodeMirror's content-padding reset — so the final lines clear the field in both WYSIWYG and source mode instead of sitting under it.
ok seed --dry-runnow previews a pack in an uninitialized directory instead of erroring with "Runok initfirst". The prerequisite gate is skipped for dry-runs (whose purpose is to preview a pack before adopting it); real seeding still requires an initialized project.Starting an AI chat in the docked terminal is now a first-class action. A single Open chat / Close chat button in the editor header reveals the terminal (collapsing the right document panel so the chat gets the full column) and starts your default CLI when no session is open, or hides it when it's showing. The terminal tab strip's + is now New chat (launches your default CLI, next to the last tab) alongside a separate New terminal tab button for a plain shell.
Your default CLI is detected from what's installed — priority Claude → Codex → OpenCode → Cursor — and the one you pick anywhere sticks for next time; if none are installed it falls back to Claude with the existing install prompt. The "Ask AI" bubble and the file-tree / composer "Open in terminal" actions use the same default and also clear the document panel for the chat. Across the app, the Terminal option now leads the "Open with AI" menus ahead of Desktop.
The docked terminal now opens as a right-side column by default, sitting to the right of the document panel (editor, then document panel, then terminal). A dock button in the terminal's tab strip moves it between the right column and the bottom dock, and a collapse button hides it while keeping the running sessions alive. Your chosen dock position is remembered across sessions. Moving the terminal by dragging has been replaced by these buttons.
Minor Changes
- Add an in-app product-updates subscription. The Resources menu is regrouped into Resources, Community, and Product updates, and a new Subscribe action opens a reusable email form (shadcn + react-hook-form) that registers the address through the
/api/subscribeendpoint. The GitHub row now shows the repo's live star count.
Patch Changes
- Fix image and file paste/drop in the packaged desktop app, which failed with an "internal server error" toast (the upload endpoint returned HTTP 500).
- Coalesce the redundant
GET /api/documentsrequests fired on load. Several consumers independently fetch the same full listing — the page-list context (asset/folder/file paths), the empty-editor state (entry count for the onboarding branch), and the wiki-link suggestion source (referenced assets) — so on boot and on every file-change push the server ran the sameready-gated disk walk two or three times concurrently. These now share a single in-flight request and one JSON parse. It is single-flight, not a result cache: the slot is released as soon as the request settles, so consumers that refetch on a file-change push still get current data. The sidebar file tree keeps its owndepth=1lazy fetch (a different URL) unchanged. - Paint the sidebar file tree incrementally as the directory listing streams in, instead of withholding the whole level until the walk finishes. The
GET /api/documentsNDJSON walk is now applied to the tree per network chunk (additively, so folders not yet streamed are never pruned mid-stream), and the loading skeleton clears on the first batch. The authoritative prune + optimistic-merge reconcile still runs once as a single splice when the stream completes, so the final tree is unchanged. On a knowledge base with a large top-level directory the first rows appear sooner rather than after the entire level is enumerated. - Fix the Properties panel rejecting Obsidian-style empty
tags:/aliases:frontmatter. Files that use Obsidian's default "no tags" shape (an emptytags:list whose only item is a blank-, or a baretags:with no value) parse to YAMLnull, which the read schema previously rejected for the whole frontmatter block — so the panel showed nothing (or an error banner) and refused every edit, even to the file's other valid properties. The read path now coerces these empty shapes to empty values (tags:\n-reads as an empty list, a baretags:as empty text), so the panel reads and edits these files normally. Files are not rewritten on disk until you actually edit a property. - Speed up project load for knowledge bases with many files.
GET /api/pagespreviously re-read and re-parsed every markdown file from disk on every request — including the redundant full-directory pass that ran concurrently with the watcher's seed walk on cold load, and a fresh full re-read on every window focus / file-change refresh. Page titles and icons are now cached on the in-memory file index (derived from the content the watcher already reads for its change-detection hash, so no extra disk reads), and/api/pagesserves them straight from memory. Titles/icons stay current through create, edit, and rename events. Behavior is unchanged; only the cost of listing pages drops from O(files) disk reads per request to an in-memory scan. - Desktop: launching an agent CLI from the docked terminal ("Open in Claude/Codex/Cursor/OpenCode") no longer pollutes your shell history. The launch command used to be typed into an interactive shell, so every launch — prompt and all — was recorded in
~/.zsh_history, cluttering↑/Ctrl-Rand writing document content in plaintext outside.ok/. The launch is now baked into the tab's shell spawn ($SHELL -l -i -c '<cmd>; exec $SHELL -l -i'): the command runs without going through the line editor (so it is never recorded), PATH is unchanged (the same login-interactive shell is used), and the tab drops into a fresh interactive shell when the agent exits — so your own later commands still record normally. Applies to all four CLIs.
Minor Changes
- Make MCP server registration non-destructive and format-preserving.
ok initand the desktop reclaim sweeps now only ever add OpenKnowledge's own entry to a harness config: comments, formatting, key order, value types, and byte-level encoding (BOM, line endings, trailing newline) are preserved. A config that can't be parsed safely — invalid JSON/TOML, a duplicate server block, or an oversized file — is left byte-for-byte untouched and reported asleft unchanged (<reason>)rather than being reset or rewritten. Codexconfig.tomlis edited through a new napi-rstoml_editaddon so 64-bit integers and microsecond datetimes are no longer mis-flagged, and symlinked configs are written through to their real target instead of being replaced. - Add a project setting for attachment upload placement, including Settings controls and upload routing for content-root and current-folder attachment directories.
Patch Changes
- Stop the file-watcher's indexing lag from making freshly-written docs look missing across link, title, and dead-link results. The file index the watcher maintains can lag an in-session write — or permanently drop the create event for a file written into a just-created subfolder — which made a brand-new doc render as a raw red-link name (instead of its title) and could flag a valid link to it as dead, until a server restart. Two complementary fixes close the gap: doc-existence now also trusts the link graph (updated in-process on every write), and the agent write/edit/frontmatter-patch handlers register the doc they just wrote into the file index immediately, mirroring page creation. Both paths honor content-scope exclusions, so a gitignore/okignore'd doc never leaks its title through these endpoints.
Patch Changes
- Desktop: enable the openknowledge.ai update proxy for the stable (
latest) channel too. Stable builds now fetch updates throughopenknowledge.ai/updates/stable, which 302s to the byte-identical GitHub asset (preserving the manifest sha512 and the macOS signature) so stable updates are counted per version. This follows the verified end-to-end beta update (beta.13 to beta.14) through the proxy. The stable path resolves via GitHub's authoritativereleases/latestalias, and a feed failure still reverts to the GitHub provider for the session, so auto-update reliability never drops below GitHub-direct. - Fix
links({ kind: "dead" })falsely reporting freshly-written docs as dead. The dead-link check decided a target existed only from the file-watcher's file index, which lags behind in-session writes, so a doc the link graph had just registered a backlink for could still be flagged dead until a server restart. Dead-link resolution now also treats any doc the graph already holds as a live node (its body has been indexed) as a valid target, so a newly-written doc is a valid link target immediately — without changing how genuinely-missing targets are reported.
Minor Changes
- Write-time broken-link validation in the Open Knowledge MCP.
write/editnow returnbrokenLinksin the same response — outbound links that don't resolve are surfaced at write time, report-only so the write still lands and you can author a doc before its link target exists. Validation covers every local link, not just docs: the./-onto-a-content-root-path doubling footgun and missing[[wiki]]/ markdown doc targets (no-such-doc), root-escaping paths from one../too many (unresolvable), and links to assets or source files ([src](../../foo.py)) that don't exist on disk at the resolved path (no-such-file). That last reason closes the gap a real codebase-wiki run hit: wrong-depth source-file links that 404 silently because the doc-only link graph never tracked them. The platform and pack skills are updated to point agents atbrokenLinksas the primary write-time check and to clarify that a same-pass forward-reference reports asno-such-docuntil its target lands (thelinks({ kind: "dead" })audit is the authoritative end-state check).
Patch Changes
Shorten the "Codebase wiki" starter-pack description to a single short clause ("Architecture, modules, and flows.") so its card matches the length of the other packs on the starter-pack picker instead of overflowing with a full paragraph.
Rename the
ok install-skillCLI command to a deliberately hidden, unadvertisedok cowork. The old name implied an automatic install it never performed — it builds theopenknowledge.skillbundle and opens the Claude Desktop App for a manual upload (the only path to the separate Skills list Claude Chat & Cowork read, whichok init's editor wiring can't reach). The command is now registered hidden (absent fromok --help), andok initno longer pushes a hint toward it — it is a power-user escape hatch discovered pull-only via the Open Knowledge skill. The underlying bundle build and thePOST /api/install-skillroute are unchanged.Make "Open in agent" (handoff to Claude, Codex, and Cursor) work on Windows and Linux, not just macOS. Install detection was already cross-platform, so on Windows/Linux a registered agent showed an enabled row that, when clicked, failed with a 500 error and a "Couldn't reach …" toast. Handoff dispatch now opens the agent's protocol URL through the OS's registered handler on every platform (
/usr/bin/openon macOS,rundll32on Windows,xdg-openon Linux), and Cursor's workspace launch already worked cross-platform. On Windows/Linux the server first confirms the agent's URL scheme is actually registered, so an uninstalled agent reports "not installed" instead of a misleading success.Desktop: enable the openknowledge.ai update proxy for the beta channel. Beta builds now fetch updates through
openknowledge.ai/updates/beta, which 302s to the byte-identical GitHub asset (preserving the manifest sha512 and the macOS signature) so updates can be counted per version. Stable still reads the GitHubpublish:config; it gets the same path once an end-to-end beta update is confirmed. A feed failure reverts to the GitHub provider for the session, so auto-update reliability never drops below GitHub-direct.Desktop: add an opt-in path to fetch updates through the openknowledge.ai proxy so updates can be counted per version. When the build's channel is enabled, electron-updater's feed is pointed at
openknowledge.ai/updates/{channel}and requests are tagged withx-ok-from-version/x-ok-channelheaders. Default-off — production keeps reading the GitHubpublish:config until the proxy is verified live, then the enabled-channel set flips to beta-first. A feed failure on the first check reverts to the GitHub provider for the session, so auto-update reliability never drops below GitHub-direct.Stop starter-pack and other card descriptions from overflowing their cards. The "Codebase wiki" starter pack and the slash-command MirrorSource / Mirror block previews had descriptions long enough to wrap to many lines and stretch their cards; those are now concise. The description text on the starter-pack picker, the slash-command block preview, the Navigator project-launcher cards, and the share-receive dialog is now line-clamped, so any long description truncates cleanly instead of distorting the layout.
Fix
ok starthanging for about 10 seconds on Ctrl+C when a live client was connected. If a browser preview tab or an agent/MCP session held a WebSocket open, shutdown left that socket alive, so the WebSocket-server close and the HTTP-server close each blocked until a 5-second internal timeout fired — adding ~10s of delay and printing an alarmingAggregateErroron a routine quit. Shutdown now drains the live upgrade sockets up front (mirroring the existing drain inok ui), so both close steps finish promptly and the server lock is released right away.Fix a starter-pack skill being unable to reinstall after a user uninstalls it. Pack skills are named
open-knowledge-pack-<packId>, which sits under theopen-knowledge*prefix reserved for OK's shipped skills. The seed installs them by copying directly, but a user-triggered reinstall re-runs the install validation, which rejected the reserved prefix and failed with "uses the reservedopen-knowledge*prefix." The install guard now exempts theopen-knowledge-pack-*namespace (OK's own shipped pack content), so reinstalling a pack skill works while user-authoredopen-knowledge*names stay blocked.Fix skill reference graph links, the skills-dock flicker, and re-seed data safety.
- A
[[references/<name>]]wiki-link inside a skill's SKILL.md now resolves to that skill's bundle reference doc and forms a real backlink and graph edge. Previously the natural wiki form silently mis-resolved to a phantom top-level doc (only an absolute[[.ok/skills/<name>/references/<name>]]or a markdown link resolved), so a skill's references never appeared as links or backlinks. Adds a sharedresolveSkillBundleWikiTargethelper used by the link index, and updates the write-skill guidance to author references as wiki-links (a backtick code path renders dead and never joins the graph). - The skills sidebar no longer flickers (list flashing empty then repopulating) on every skill create/edit/install/move. The list now revalidates in place instead of resetting to a loading state.
- Re-running seed no longer overwrites a user-edited starter-pack skill.
installPackSkillonly authors the source when it does not already exist; an existing skill keeps its edits and shadow history while still refreshing its editor projection and install marker. POST /api/create-pagenow rejects a path inside.ok/(or a managed-artifact doc name), so a page can no longer be created inside the indexed.ok/skills/**namespace and bypass skill-schema validation.- The
installMCP tool description now lists OpenCode (.opencode/skills/) as a valid projection target, matching the schema.
- A
Fix skill move/scope-change losing bundle files and dropping references from the link graph. A cross-scope skill move (global ↔ project) copied only
SKILL.mdand then deleted the source, silently losing everyreferences/**andscripts/**file; both the MCPmoveverb and the editor's scope switch now copy the full bundle through the per-type write path (project.mdreferences rejoin the backlink/tag graph; global references and all scripts are written fs-direct) before deleting the source, aborting without data loss on any partial-copy failure. A same-scope skill rename now re-indexes the relocatedSKILL.mdand its.mdreferences into the live backlink/tag graph (previously they sat unindexed until a manual rescan). The editor also reconciles open skill tabs after a server-side move — a tab whose skill moved scopes retargets to the new location, and one whose skill was deleted closes — so no stale phantom tab is left behind. Also adds the new skill write routes to the mutating-route CSRF guard and the conflict gate.Fix Open Knowledge Desktop offering to set up a project inside a subfolder of a linked git worktree. Picking a subdirectory (for example
public/open-knowledge) inside agit worktree addcheckout misclassified it as a worktree root and showed "Setup Open Knowledge in this folder" in place, scaffolding.ok/in the subfolder instead of promoting to the git root. Subfolders of a linked worktree now correctly fall through to git-root promotion; only the worktree root itself is treated as a standalone project.Run the git preflight at the project-setup boundary so a broken or PATH-invisible git surfaces recoverable install/upgrade guidance instead of a raw error.
Creating a project, opening a folder, cloning from GitHub, and
ok initnow verify git is usable before invoking it, and run against the exact git the preflight validated (the resolved path for in-process git, or an enriched PATH for the spawned clone). When git is missing or unusable they report platform-specific install guidance;ok initexits 78 (EX_CONFIG) on a git-preflight failure, matchingok start.These setup paths now require git 2.31 or newer — the same floor
ok startand the desktop app already enforce at launch.Show global skill references in the link graph, connected to their skill.
A global skill's
SKILL.mdand itsreferences/*.mdnow appear as connected nodes in the link graph, the same as a project skill — even though global skills live in~/.ok/skills/outside the project content tree. Global bundle docs are ingested as graph nodes (at boot, on branch switch, and live via the managed-artifact watcher) and connected by structural edges derived from the bundle path. The connection is within-bundle only: a global skill is never linked into a specific project's knowledge base (a global reference's wiki-links are not followed), and its cluster is visible from every project since global skills are shared across all of them. Clicking a global reference node in the graph opens it read-only through the scope-aware skill-file viewer.Stop showing the "this project is running an older version of OpenKnowledge … restart it to use the current version" notification after a routine app update. When the desktop app updated, the background server it had started kept running on the old version (it's designed to survive the app quitting), so the freshly-updated app reconnected to that stale server and flagged the version mismatch on nearly every update. The app now shuts that server down as part of the update install itself, so the relaunched app starts a fresh matching server and there's nothing to warn about. Shutdown is gated to the update-install moment only — a normal app quit still leaves your server running, as before — and the server flushes any in-flight edits before exiting, so no work is lost.
Desktop: stop showing the sticky "Installed CLI shims." startup toast when an app upgrade only repoints the internal
~/.ok/binsymlinks. The startup PATH-reclaim toast now fires only for changes the user can see or act on — a shell rc-file edit, an opt-out, a legacy-symlink cleanup, or a failure. A no-op symlink repoint (the common case on every upgrade or bundle-path change) is now silent; the repoint still happens and still emits the structuredpath-install-symlink-successevent for operators. Restores the spec's "silent if no meaningful user-facing change" contract.Make a project skill's references show up connected in the graph, and let global skill bundle files be opened.
- A project skill's
SKILL.mdand itsreferences/*.mdnow appear connected in the link graph automatically, whether the body references them as[[wiki-links]]or as plain backticked paths. The references were already graph nodes; this adds the structural edges between a skill'sSKILL.mdand its own reference docs, derived strictly from the bundle path (.ok/skills/<name>/). Regular documents and folders are unaffected — co-membership in an ordinary folder does not create edges. Authored wiki-links keep working and are not double-drawn. - Clicking a global skill's reference or script in the Skills sidebar now opens it (read-only) instead of failing with "could not be found." Global skill bundle files live in
~/.ok/skills/outside the project content tree, so they are now read through the scope-aware skill-file endpoint rather than as a project content-dir asset. A fetch failure shows a terminal state instead of a perpetual loading spinner. - The write-skill guidance is updated: project skill references auto-connect in the graph regardless of how they are referenced, a wiki-link is only needed for a clickable inline link, and global skill references should stay plain backtick paths since global references are not graph docs.
- A project skill's
Make skills URL-addressable in the editor and resolvable by the MCP, so a skill behaves like a document. The skill editor now has its own route (
#/__skill__/<scope>/<name>): opening a skill updates the URL, so it is reload-stable, shareable, and deep-linkable; loading a skill URL opens straight into the editor with the file sidebar visible. Closing routes the URL back to the document underneath, and navigating to a document dismisses the skill editor. ThewriteandeditMCP tools now ride a route-onlypreviewUrlfor skills (the same preview envelope documents already carry), and thepreview_urltool accepts a{ skill }target, so an agent that authors a skill in Open Knowledge can hand back a URL that opens it in the running editor.Creating a skill now opens the editor directly instead of a modal: the "New skill" action drops a draft into the main editor pane with an inline Project/Personal scope toggle in its Properties panel. Filling in a name and description enables Create; once it commits, the editor re-points to the saved skill and switches into the same autosave edit experience, so authoring a new skill and editing an existing one are one continuous surface.
Render a global skill's
.md/.mdxreferences as formatted read-only markdown instead of raw source.Opening a global skill bundle file in the Skills sidebar previously showed the raw markdown source. Markdown references now render formatted (headings, bold, lists) read-only — the same rendering the editor uses (via the shared extension set, with no editing and no collaboration binding). Scripts and other non-markdown files keep the source view.
Render skill reference graph nodes as resolved, and address review follow-ups.
- A skill-bundle reference node in the link graph now renders as a normal resolved node instead of the dashed-red "missing" treatment. The node always resolved to a real, openable read-only viewer (clicking it works), but it was misclassified as a missing link target, so it read as broken.
- A failed skill move no longer turns into a 500 when the post-rename graph re-index throws — the rename already succeeded, so a re-index failure is logged and swallowed.
- The skills-list handler now logs when
readdirSyncfails instead of silently returning an empty list, and the bundle-file path classifier rejects NUL bytes for parity with its sibling validators. - The starter-pack update dialog and toasts show the skill's display name instead of its raw internal id, and the skill menu's update action reads as an imperative ("Update skill").
Clarify the skills import prompt. The one-time banner now states what Import actually does: it moves the editor-dir skills into
.ok/skillsand replaces the.claude,.codex, etc. copies with symlinks back to it, so the knowledge base is the single place to edit them and every editor stays in sync. It also flags the consequences a user needs before clicking: if those folders are committed to git the change should be reviewed, and symlinks can behave differently on some editors and on Windows.Stop two ways agents get derailed around the skill surface.
The
skillsMCP tool now short-circuits OpenKnowledge's own built-in skills instead of 404-ing. An agent told to "load the open-knowledge skill" would callskills({ name: "open-knowledge" }), hit a bareSkill not found., and fall back to cat-ing the bundled SKILL.md. The built-ins (open-knowledge,open-knowledge-discovery,open-knowledge-write-skill) are runtime agent skills projected into editor host dirs, never KB content skills, so a READ aimed at one now returns a teaching error explaining it is already in the agent's loaded skill list and is not fetched through this tool. User-authoredopen-knowledge-pack-*skills are unaffected, and the tool description states the boundary up front.The project SKILL.md escape hatch now tells agents that their initial tool list is not exhaustive: some clients (notably Codex) defer MCP tools behind a lazy
tool_searchstep, somcp__open-knowledge__*is absent until discovered. Absence from the visible list means "not discovered yet," not "not registered" — agents must run tool discovery before invoking the native-tools escape hatch.
Keep an open browser tab's
ok startserver alive. The web editor now holds a single, app-lifetime/collab/keepaliveWebSocket — the same presence-invisible keepalive the desktop app and MCP shim already use — so the server's 30-minute idle-shutdown can no longer fire out from under an open tab when no document is focused or during a brief reconnect. Previously, with no doc open the only liveness signal was the per-document collab connections, so an idle tab could lose its server and every editor/tool call would fail until reload. Closing the tab still lets the server idle-shut-down normally, and the keepalive reconnects across a server restart on a new port. Multiple tabs each hold their own keepalive. The keepalive adds no presence-bar entry.Fix Git auto-sync when server-spawned Git needs the user's home directory, SSH agent, or credential-helper environment to reach a remote. This most visibly affected Windows repositories using SSH remotes, where
ok syncand editor sync could fail with "Could not read from remote repository" while the samegit fetchorgit pushworked in a terminal.Because preserving the home directory also lets server-spawned Git read the user's global config, OK now pins
commit.gpgsign=falseandcore.autocrlf=falsefor its own Git commands only (via-c, leaving the user's own Git untouched): the first prevents the unattended sync commit from aborting when a global signing config can't prompt for a passphrase, and the second keeps line-ending conversion from churning content against OK's byte-exact round-trip.
No notes for this release.
View on GitHubPatch Changes
Add OpenCode as a supported agent.
ok init— and the desktop first-launch consent dialog plus the create-project dialog — now writes anopen-knowledgeMCP server entry toopencode.json(project-local, and~/.config/opencode/opencode.jsonfor a global install), using OpenCode'smcpconfig shape and the same resilient launcher every other editor gets. OpenCode reads the Open Knowledge skill from the shared.agents/skills/directory it already scans, so no extra skill copy is written.OpenCode is also wired into the Ask AI composer and Open-in-Agent menus as a terminal CLI: it appears as "OpenCode (CLI)" in the Terminal section and launches
opencode '<prompt>'in the docked terminal. (It's terminal-only — no URL scheme — so it's deliberately absent from the GUI deep-link Desktop section.)Adds an OpenCode integration page and brand logo to the docs site.
Rename the product brand from "Open Knowledge" to "OpenKnowledge" (one word) across the desktop app, CLI output, MCP/skill copy, and docs.
The macOS app, its helper bundle, the DMG artifact, and the userData directory are renamed. A one-time, identity-verified migration runs on the first launch of a renamed build: it relocates an existing user's app state (recent projects, window restore, auto-update cache) from
~/Library/Application Support/Open Knowledge/to.../OpenKnowledge/, but only after verifying the legacy directory is ours (itsstate.jsonparses as our schema), so another vendor's identically-named directory is never touched. It copies, verifies, then removes the legacy directory; any failure degrades to a clean first run.Technical identifiers are unchanged: the npm package
@inkeep/open-knowledge, the macOS appIdcom.inkeep.open-knowledge, theopenknowledge://deep-link scheme, theopenknowledge.aidomain, and theopen-knowledgeMCP server name. "Open Knowledge Format" (Google's external standard) is also preserved.Fix jumpy, inconsistent mouse-wheel scrolling inside full-screen terminal apps that capture the mouse (the
claudeTUI,vim,less,top). In mouse-tracking mode xterm.js forwards one mouse-wheel report per OS wheel event with no accumulation, so the high-frequency event stream from trackpad momentum and free-spin/fast-scroll wheels floods the app — scrolling that lurches and can run away ("rocket scroll"). The terminal now accumulates fractional rows of travel and emits one wheel report per whole row of distance crossed, so scroll tracks the actual distance moved regardless of how many events deliver it: gentle drags and fast flicks over the same distance scroll the same amount, with no dead zone and a per-event clamp that absorbs momentum spikes. Normal scrollback (no mouse-capturing app) additionally gets smooth scrolling. Desktop only.Tune embedded-terminal wheel scrolling to feel closer to a native terminal (e.g. Ghostty). The smooth mouse-tracking scroll added previously was correct but sluggish: macOS bakes velocity acceleration into wheel deltas, and the per-event clamp was clipping the accelerated fast-flick range. The mouse-mode accumulator now uses a modest base sensitivity with a higher per-event cap so OS acceleration carries through — fast flicks travel far while slow drags stay gentle — and normal scrollback gets a faster per-notch travel. Desktop only.
Patch Changes
- Renaming a file no longer rewrites the destination when the move already placed the final bytes there. A managed rename moves the file and then writes the reconciled content; previously that second write was unconditional, so a rename that did not change the file's content still wrote the destination twice. The rename spine now skips the rewrite when the reconciled content already matches what is on disk, so a no-content-change rename writes the destination exactly once. Renames that rewrite wiki-link references still write the destination twice — the move places the old bytes, then the link rewrite overwrites them — because that second write is doing real work.
- Terminal tabs now show the title the running program sets. Programs name the terminal through the standard
OSC 0/OSC 2escape sequences (ESC ] 0 ; <title> BEL) — shells viaPROMPT_COMMAND,vim, and theclaudeTUI all do this — and the docked terminal now picks that up and uses it as the tab label, the same way other terminals do. Each tab falls back to its positionalTerminal Ndefault until its program sets a title, reverts to that default if the program clears the title (e.g. on exit), and updates live as the title changes. This makes a strip of several shells (a few for a monorepo, a few Claude Code instances) navigable at a glance. Desktop only.