Releases: VerinFast/waikiki
Release list
v1.0.4
A proposed edit now says so on the page
Asking chat to change an article looked like it did nothing. It had worked — the
proposal was filed and waiting — but a pending change rendered only on the
Details tab, so from the article with the chat panel open there was nothing to
see.
The article now says a change is waiting and links straight to the diff, where
Apply and Reject already lived. Review still happens there; only the
announcement is new. Nothing a proposal contains touches your page until you
apply it.
Parent page moved back to the editor
Where a page sits in the tree is part of writing it, so the selector lives on the
edit screen rather than under Page options.
That move exposed a real bug: the editor never received the page's current
parent, so the selector always read "— none (top-level) —". Correct by accident
for a top-level page, wrong for every page that has a parent — and pressing Move
without touching it would have quietly promoted the page and orphaned it. It now
shows where the page actually sits.
Under the hood
- The MCP server reports its own version at the handshake instead of the
framework's. It runs from source as a long-lived process while the app is a
frozen bundle, so the two can be on different code against the same database —
now that mismatch is readable rather than inferred. - Dependabot tracks GitHub Actions as well as Python, and a test asserts
requirements.txtandpyproject.tomlstill agree — packaging for PyPI made
both declare the runtime dependencies, and Dependabot moves one file at a time. - Dependency floors raised (anthropic 1.0, cryptography 50, fastmcp 3.4.7,
pycrdt 0.14.4, pygments 2.21, uvicorn 0.52.4), each verified together in a
clean environment — including that the updater still verifies a real release
signature against its pinned key.
Install
brew tap VerinFast/waikiki && brew install --cask waikiki
or pipx install waikiki for the server and MCP endpoint on any platform.
Installed copies update themselves.
v1.0.3
Chat can offer an edit instead of failing at one
Asking chat to change the page you were discussing failed outright.
Chat is granted read-only tools deliberately — a conversation should not
silently rewrite your wiki — but nothing ever told the model that. So it reached
for an editing tool, the CLI refused a tool it was never granted, and you got an
error instead of an offer.
The right tool already existed and was simply not wired up. Chat can now
propose an edit: it writes the revised page as a suggestion, tells you it is
waiting, and you apply or reject it on the page itself. Nothing it proposes
changes anything until you say so — the property that made it read-only in the
first place is intact, and the thing you actually asked for now happens.
Only that one tool was added. Direct page writes stay out.
A Reload button
Between Back and Forward, where a browser puts it.
Install
brew tap VerinFast/waikiki && brew install --cask waikiki
or pipx install waikiki. Installed copies update themselves.
v1.0.2
Patch release: a page save is now atomic, and a page tells you it has a past.
One save, one transaction (#72)
A page save writes several things — the markdown projection, the version
snapshot, the tag index, and the canonical CRDT document that CLAUDE.md calls
the source of truth. Those were separate autocommitted statements, so a crash
between them could leave the canonical document a revision behind its
projection, and the projection is what wins on next open. The rule said the
Y.Doc was authoritative; nothing enforced it across a crash.
A save is now one transaction: it all lands or none of it does. Verified by
failing at each seam and confirming the page is untouched — same text, same
version count, same canonical document.
Two details that matter more than they look:
- The embedding stays outside the write lock. Re-indexing for search chunks
and embeds, and holding a write transaction open across that would queue every
other writer — you, an agent, and the live-editing flusher — behind a model
load. Search is a cache rebuildable from the text, so it runs just after the
commit instead. BEGIN IMMEDIATE, not deferred. A save reads before it writes, and a
deferred lock upgrade can fail outright when a second writer exists — which
here it always does. It also closes a check-then-insert race on new page slugs.
Tested on both SQLite backends: the bundled apsw and the standard library.
A page tells you it has a past (#73)
Every edit was already versioned, with a diff, and nothing on the page said so —
the only "History" on screen was the browser back button's label. An article now
carries one quiet line: Edited 3 minutes ago — 4 earlier versions you can go
back to, linking straight into the history with it already open. A page with a
single revision says so plainly and links nowhere, because there is nothing
earlier to see.
The safety net was real and unreachable, which for someone who has just pasted
over a paragraph is the same as not having one.
Install
brew tap VerinFast/waikiki && brew install --cask waikiki
or pipx install waikiki for the server and MCP endpoint on any platform.
Installed copies update themselves.
v1.0.1
Patch release. One damaged file no longer takes the app down with it.
Fixed
A corrupt wiki file could stop Waikiki from starting entirely (#71). If the
default wiki's database was damaged, startup failed and every other wiki —
however healthy — became unreachable. Wikis are supposed to be isolated, and now
they behave that way:
- The app starts. The damaged wiki explains itself on a page that names the file,
says what is wrong with it, and links the others. - Manage wikis stays usable and is where you recover from: it lists the
healthy wikis normally, badges the damaged one with the reason and its size on
disk, and shows where your backups are and the three steps to restore one. - Nothing touches the file. A corrupt database is your data in a damaged
state and may still be recoverable, so Waikiki never deletes, truncates or
"repairs" it — it is left byte-for-byte as it was. - The MCP
switch_wikitool refuses an unreadable wiki with the same reason, so
an agent gets the same answer you do.
The distinction that makes this safe: SQLite refusing a file is treated as
damage, while a fault in Waikiki's own code still raises loudly. A missing table
or a bug in our code is not corruption and is not silently swallowed.
Two more found on the way:
- A single unreadable wiki aborted the whole scheduled backup run and deleted
the half-written snapshot — so one bad file meant no backup for any wiki. It
now skips that wiki and reports it. - Lock contention on open could surface as a database error rather than a wait,
because the busy timeout was set too late. Contention is not damage.
Install
brew tap VerinFast/waikiki && brew install --cask waikiki
or pipx install waikiki for the server and MCP endpoint on any platform.
Installed copies update themselves.
v1.0.0
First stable release. Waikiki is a small local wiki built for a human and an LLM
to edit the same page — SQLite per wiki, hybrid search, live co-editing, and an
MCP server so Claude works in the same document you do.
Install — macOS app:
brew tap VerinFast/waikiki
brew install --cask waikiki
Or download the zip below. The app updates itself from then on.
What's in 1.0
- A wiki per file. Each wiki is one SQLite database you can copy, back up,
or hand to someone. Wikis are fully isolated — pages, search and[[links]]
never cross between them. - Live co-editing backed by CRDTs, so a human and an agent editing the same
page merge instead of overwriting. - An MCP server sharing one code path with the UI: whatever Claude does to a
page renders, versions and indexes exactly as your own edit would. - Hybrid search — FTS5 BM25 fused with vector similarity.
- Agent-aware navigation. Tools report what they are not showing you, so
an agent can tell the difference between "no such page" and "not in this
listing" (#45–#48). - Typed page metadata, declared per template and reported rather than
enforced — an invalid value is flagged, never silently rewritten (#28). - Doorman integration, entirely optional: when it is running, generation,
chat, images and speech route through its agents, and Waikiki says which
backend answered. - A capabilities view that tells you what this machine can do and puts a
button on what it can't, instead of failing at the moment you click something. - Signed self-update. Every release is Ed25519-verified against a key pinned
at build time before it is installed; an unverifiable download is refused.
Known limits, stated plainly
- Apple Silicon only. An Intel build is a second pipeline, not a flag.
- Not notarized yet (#66) — the Apple Developer portal is unavailable. Until
it is, install withbrew install --cask waikiki --no-quarantine, or
right-click → Open the first time. - A page save is several transactions rather than one, so a crash at exactly the
wrong moment can leave the canonical document a revision behind its projection
(#72).docs/data-safety.mdhas the measurements, including what a hard kill
actually costs (0 of 826 acknowledged writes) and how restore works. - A corrupt default wiki currently stops the app starting (#71).
Source-available under the Elastic License 2.0: use it, modify it, redistribute
it — just don't offer it to others as a hosted service.
Also on PyPI, for the half that runs anywhere Python does — the web app and the MCP server, no desktop window:
pipx install waikiki # then: waikiki (web) / waikiki-mcp (stdio MCP)
Published via Trusted Publishing (OIDC), so no long-lived token exists to leak.
v0.21.0
Doorman does the thinking, when you have it (#63, #37)
Waikiki reaches for a model in three places — the editor's Generate button,
chat with an article, and image generation. All three now go through
Doorman's agents when Doorman offers them, so a user who has already configured
models there doesn't configure a second set of API keys and CLIs here.
And when Waikiki is embedded in Doorman's own window, the integration stops
pretending to be optional. Doorman shows Waikiki in an iframe; Waikiki now
recognises that (the framed-document signal plus a live health probe, so
"embedded" means embedded in Doorman rather than in any iframe) and the
"Use Doorman when it is running" checkbox renders locked, with the reason,
instead of offering a choice that has no meaning when Doorman is the host.
Every surface reports which backend answered. Silently swapping your configured
model for a Doorman agent would be its own bug.
Nothing here is required. Standalone Waikiki behaves exactly as before. An
older Doorman that doesn't have these endpoints, or one with no agent
configured, falls back to the local path silently — Waikiki capability-probes
rather than version-sniffs, so it works against old and new Doorman with no
action from you.
One honest limit: Doorman's image endpoint takes a prompt, model and size, so a
wiki's style reference images can't travel that path. The style prompt
still does.
Whole-wiki export and import (#57)
export_wiki_bundle / import_wiki_bundle move an entire wiki, not one page at
a time — pages and their hierarchy, ordering, starred flags, custom
elements, templates (with the metadata schemas added in v0.20.0) and images,
de-duplicated. Round-tripped against a real 215-page wiki: identical page set,
identical hierarchy, no drift.
Both directions stream, so a 43MB wiki costs one page of memory rather than all
of it, and the whole payload is decoded before the first write — a bad bundle
leaves your wiki untouched instead of half-imported. Hierarchy travels by slug,
never by database id, because ids are local and an id that crossed would point
at whatever page happened to hold that number on the other side.
This is the local half of the Kahala round-trip; the interchange format is
version-gated and now at spec 2, in lockstep with the hosted side.
Fixes
v0.20.0
Agents can find the context they need
Completes the cluster started in v0.19.0. An agent handed one page can now see
what that page points at, be told to go read it, and fetch several linked pages
in one call.
get_pageasks you to follow the links. The tool docstring and the server
instructions now name the moment — before you write about, quote, or edit
something a page links to, read the linked page first — and each response
carries a one-linehintwhen the page has links. Only when it has links: a
hint on every page is one agents learn to skip.read_pages(slugs)fetches up to 10 pages in one call, so following five
links costs one round-trip instead of five. Missing slugs come back in
missingrather than failing the batch, and slugs past the cap are reported
indroppedrather than silently truncated. It shares one code path with
get_page, so a page reads identically whichever tool you used.
Worth noting from the implementation: fetching the live-edit overlay
concurrently made the batch slower (421ms vs 82ms) because each call built and
discarded its own HTTP client. One shared client for the batch was the real fix
(82ms → 11ms); bounded concurrency on top only pays once the server is actually
slow (354ms → 90ms at 30ms/request). Ten pages now cost 82ms where ten
get_page calls cost 498ms.
Templates can declare their metadata's shape (#28)
A template can now say what the frontmatter of pages made from it should look
like — hp: int, role: player | npc, born: date, blade*: int for required
— compiled to pydantic checks. It warns, never blocks, for humans and agents
alike: an invalid value is reported with what was expected and left verbatim on
disk, never coerced and never rejected. Rejecting a write while a human types
into the same document would make validation a source of lost work.
Existing wikis are unaffected: the migration adds an empty column, and a page is
only checked if it carries a template: property, which is only stamped when
the template actually declares a schema.
Also
pydantic>=2.7 is now an explicit dependency (it was only transitively present
via FastAPI).
v0.19.0
Agents can finally see the whole wiki
Two fixes to how the MCP tools describe a wiki, from measuring what an agent
actually receives.
list_pages was hiding most of the wiki. It listed only top-level pages —
on a real 215-page wiki that is 18 pages, so 91% was invisible. An agent
that read a page, saw [[Igni]], and checked whether Igni existed was told it
didn't, and would reasonably go create a duplicate. It now takes children:
omit it for top-level only (unchanged, the sidebar depends on it), true or
null for the whole wiki, or ["a-parent"] for one branch. It always reports
children_hidden, so silence is never mistaken for absence, and every row now
carries parent_slug — the raw parent_id integer was meaningless to a caller
that never sees page ids.
get_page now resolves the page's outbound links. Previously wikilinks were
visible only as raw [[syntax]] the agent had to parse itself, which is a trap
with aliases: in [[Edaphos|earth]] the visible word is "earth" and the page is
edaphos, so slugifying the label finds nothing. Each link now comes back with
its target, title, the label the reader sees, and whether it exists —
red links included, since those mark stubs worth writing and save a doomed
fetch. Repeats collapse to one row with a count.
Dependencies
httpx ≥0.28.1, uvicorn ≥0.52.3, anthropic ≥0.122.0, markdown-it-py ≥4.2.0,
xhtml2pdf ≥0.2.17. The two that weren't already installed were verified by
booting the real server (HTTP, static, API, and the /collab websocket) and by
checking the Anthropic streaming surface ai.py depends on.
v0.18.0
Doorman, when you happen to run it (#37)
Waikiki now notices the sibling Doorman
app and routes text-to-speech through its Voicebox voices instead of the
browser's robotic speechSynthesis. Strictly optional in both directions:
nothing is installed or launched on your behalf, and if Doorman isn't running
Waikiki says nothing and uses its own voice. Settings shows what it found.
Using Doorman's agents for chat-with-page needs an endpoint that doesn't exist
there yet — tracked as VerinFast/doorman#78.
Fixes
- The lead section's edit and speaker controls were invisible. They hide at
opacity:0and are revealed by hovering a heading — but the lead section (the
text before the first heading) has no heading, so nothing could ever reveal
them. They now sit faintly visible at rest. - Speaker buttons were black on black.
.tts-btnset nocolor, so it
inherited the browser default black: readable on the light theme, invisible on
dark and sepia. This affected every heading's speaker, not just the lead's. - Right-click menus closed on their own opening gesture. A ctrl-click is a
left click, so the click that opens the menu also dismissed it — the menu only
appeared while the button was held. Dismissal now waits for a genuinely new
gesture.
tests/test_theme_contrast.py guards the invisible-control class of bug by
reading the stylesheet, since asserting an element is in the DOM proves nothing
about whether it can be seen.
v0.17.0 — a usable right-click menu, Say this word, dictation, inline image generation
Mostly an accessibility release.
The right-click menu works now
It used to appear while you held the click and vanish the moment you let go, so
nothing in it could actually be used. It now stays up until you click again —
either an item, or anywhere else to dismiss it. Escape closes it too.
"Say this word"
Right-click (or ctrl-click) any word and choose Say "…" to hear it. With text
selected it offers Say this and reads the selection.
Useful for an unfamiliar word, and useful if reading is the hard part.
Talk instead of typing
Chat and the image description box have a microphone button.
In the desktop app it starts macOS dictation and your words go straight into the
box. The first time, macOS will ask to allow Waikiki to control it — that
permission is what lets the app start dictation for you. If Dictation itself is
switched off, macOS will say so.
In a browser it uses the browser's own speech recognition where that exists.
Either way nothing is sent until you press the button yourself, so a misheard
word can be fixed first.
Image generation is part of the editor
Generating an image used to be a one-line browser box, with an error popup if it
failed — and the popup took your description with it.
It is now a panel in the editor: the page stays visible while you describe the
picture, the box is as big as the description needs to be, and if generation
fails your description is still there to adjust and try again. The panel also
shows the wiki's image style, which shapes every picture and was previously
invisible while writing.
Describe a template or component instead of building one
The template and element editors have a "Describe it and let an agent draft
it" box. Say what you want — "an infobox for a character with portrait, home
and factions" — and the draft is filled into the form.
Nothing is saved until you press Save. The draft lands in the editor so you
can read and change it first; components run their own HTML and JavaScript on
every page that uses them, so that stays a deliberate step.
Notes
Updating from 0.16.x needs nothing beyond Settings → Updates.
383 tests passing.