smartwriter
A creative midwife for writers. Say what you want to write — for the whole piece, for a chapter, for the paragraph in front of you, for one sentence — and be questioned, given directions, read back against your own intent, or handed a draft you can take or leave. By a model running on your own machine: nothing you write, intend or gather leaves the computer, and the app works with the network off.
A Wails desktop app: Go backend, Svelte 5 frontend, and a local Qwen3.5 served by the model runtime every kumulativ app shares (kumulativ-core). It shares its stack and its “Maison” design language with smartreader and smartdiary.
What it does
Write. One measure of serif text on paper. The page is a rich-text
editor: bold, italic, underline, strike-through, headings, quotations and list
items, set from a small bar that appears over a selection or with the
shortcuts everyone already has (Ctrl+B/I/U, Ctrl+Alt+1/2/3 for headings,
Ctrl+Alt+0 for a paragraph, Ctrl+Shift+. for a quotation). Pastes arrive as
plain text with their paragraphs kept and their fonts left behind. Chapters
sit in a list on the left and can be added, reordered and removed. Everything
saves on a pause in typing; the bar says saved.
Export. As a Word document, a PDF, or Markdown, from the Export button. The Word file carries real styles — Title, Heading 1 for chapters, Quote, List Paragraph — so Word’s navigation pane and style gallery work on it. The PDF is set in Source Serif 4 with a title page, one chapter per page break and page numbers.
Say what you want. This is the point of the app. There are four levels of intent, and the midwife panel on the right works at whichever one you choose:
| Level | Where it is set | What it is for |
|---|---|---|
| Piece | The Brief tab, or the panel at Piece level | The premise, the reader, the register, what it should leave behind |
| Chapter | The Brief tab, or the panel at Chapter level | What this chapter has to do for the piece |
| Paragraph | The panel, with the caret in the paragraph | What this paragraph must accomplish |
| Sentence | Select the sentence, then What I want here on the bar | What this one sentence must do |
A paragraph with an intent carries a small dot in the margin. A sentence with one is underlined faintly. Both are paint rather than markup — the page’s DOM is exactly what you typed — so they never catch the caret.
Ask the midwife. With a level chosen and an intent stated (or not — she will ask for it), five things can be asked:
| Ask | What comes back |
|---|---|
| Ask me | Probing questions that draw out what you mean, without answering them |
| Suggest | Three or four concrete directions the piece, chapter, paragraph or sentence could take |
| Read back | What is already working, quoted, and exactly where the text parts company with the intent; then the one change that would matter most |
| Draft | A draft in your own voice as it shows on the page — a paragraph, the next paragraph of a chapter, a sentence, or (for the piece) an outline. Insert it after the paragraph, at the end of the chapter, or in place of the sentence; or copy it |
| Rephrase | Other ways to say the sentence or paragraph, each one a click to use |
Plus a free question about whatever is in focus. Every conversation is kept against its focus: come back to a paragraph a week later and the panel shows what was asked and said about it, and a follow-up is answered in the light of it. Forget this drops one.
The midwife’s manner — gentle, direct or rigorous — is a setting. What she knows is the same either way: the intents at every level above the focus, the chapter so far (whole when it fits, otherwise the paragraphs around the focus), what the other chapters are and do, and the sources.
Sources. Add PDFs, EPUBs, Word files or plain text to a piece — from the Sources tab, the Sources screen, or by dropping them on the window. Each one is read once, in the background, and summarised section by section and as a whole (the same pass smartreader’s Study this makes, with the same right-of-way rule: a question you ask cuts the summarising short, and it resumes after). When you ask about the piece or a chapter, the midwife is shown every source’s summary; when you ask about a paragraph or a sentence, she is shown the passages of your sources that bear on it, found by lexical retrieval (BM25 over paragraph-sized chunks) from what you wrote there and what you said you wanted. She is told to name the source she draws on and never to invent what one says.
Where things live
<UserConfigDir>/smartwriter/
data.json settings, the pieces, the sources, every conversation
projects/<id>.json one piece's chapters and paragraphs
sources/<id>.json one source's extracted text
sources/<id>.study.json its summaries
<UserConfigDir>/kumulativ/ shared with every kumulativ app — see kumulativ-core
models/ the Qwen weights, one copy for every app
engines/ the llama.cpp build for this machine
run/ the one running server: registry, lock, presets
Environment variables
| Variable | Effect |
|---|---|
SMARTWRITER_CONFIG_DIR |
Move the whole data directory (portable install, tests) |
SMARTWRITER_GGUF |
Use this .gguf and download nothing |
KUMULATIV_HOME |
Move the shared model store |
KUMULATIV_BIN_DIR |
Look for llama-server here first |
KUMULATIV_IDLE_UNLOAD_MIN |
Minutes of quiet before the model is put to sleep (default 1; 0 = never) |
KUMULATIV_LLAMA_ASSET / KUMULATIV_LLAMA_RELEASE |
Pin a specific llama.cpp release asset |
KUMULATIV_LLAMA_EXTRA_ARGS |
Extra flags for the llama-server command line |
KUMULATIV_FORCE_CPU |
Never use the CUDA build |
KUMULATIV_NO_ENGINE_FETCH |
Refuse to download an engine |
KUMULATIV_DEBUG |
Forward the server’s output instead of swallowing it |
How it is put together
The page is the DOM
The editor is one contenteditable element whose children are the chapter’s
paragraphs, each carrying its id in data-block. The browser does the
editing; after every input editor.ts reads the DOM back into Para
records, reducing the markup to five inline tags (strong, em, u, s, br) and
carrying each paragraph’s intent and sentence marks across by id. A paragraph
the browser clones (Enter in the middle of one copies its attributes) keeps
its id on the first half and gets a fresh one on the second, so an intent
stays with the words it was written about.
The Go side makes the same reduction again on save (richtext.go), derives
the plain text from the reduced markup, and re-finds each sentence mark in
the new text by its words. Offsets are UTF-16 units, because JavaScript is
the one counting.
Marked and focused sentences are drawn with the CSS Custom Highlight API —
ranges the browser paints over, not elements — so the DOM stays exactly what
was typed and the offset invariant (text nodes in order, one \n per <br>,
concatenate to the paragraph’s text) never depends on a wrapper staying put.
The prompt
prompts.go builds every message. One system message carries the midwife’s
standing instructions and the whole context from the general to the
particular: the piece and its intent, an outline of the other chapters, the
chapter in focus and its intent and text, the paragraph and its intent, the
sentence and its intent, the sources. Then the conversation about this focus
so far as user/assistant pairs, then one imperative for the mode and level.
Context goes in the system message on purpose: the history clamp trims from
the oldest non-system message, and the text must be the last thing to go.
Retrieval
sources.go cuts each source into chunks of about nine hundred characters on
paragraph boundaries and scores them with BM25 against a query made of the
question, the intents and the text in focus. A chunk has to meet the query on
at least two terms to count. It is lexical on purpose: exact, instant, no
second model beside the one that writes, and explainable when it misses.
Known limits
- The midwife drafts in the voice of what is on the page. An empty chapter with a one-line intent gets a generic opening; the more you have written and said, the better she reads you.
- Retrieval is by words. A source that says the same thing in different words is not found for a paragraph; its summary still reaches the midwife at piece and chapter level.
- Scanned PDFs need OCR first, and PDFs with broken font maps are refused, as in smartreader.
- The PDF export does not justify and does not hyphenate; the Word export leaves that to Word.