Quill
Quill is a native macOS writing studio built for novelists. A Zed-style editor in the middle, an AI writing partner on the right, a terminal / inbox / logs panel at the bottom, and a Python backend that orchestrates local and cloud LLMs behind the scenes. No Electron. No browser tab pretending to be an app. Just a real Mac app that happens to be very good at writing.
Quill β Writing Studio
What is Quill?
Quill is a native macOS writing studio for novelists. The editor is Zed-style, the AI chat sits in a side panel, and a bottom panel hosts a terminal, an email inbox and a log stream. Underneath, a Python Flask backend routes requests to whichever model you have selected β Ollama on your own silicon, MLX, LM Studio, a MiniMax cloud model, or your own OpenAI-compatible endpoint.
The idea that makes Quill tick: the app is the AI writing partner. You're not switching between a manuscript editor and a chatbot. Quill is one surface where the prose, the model, and the tooling sit side by side. You type, the AI suggests, you accept, autosave fires.
π₯οΈ Native macOS
SwiftUI + AppKit. No Electron, no embedded browser. The editor is a real NSTextView subclass with Tab intercepted for AI fixes.
β¨οΈ Tab-to-Fix
Press Tab in the editor and Quill asks the model to fix typos and grammar on the current selection or paragraph β in place.
π¬ Writing-Partner Chat
The AI answers as Quill, your writing partner. Long-form mode writes straight into chapter files; short-form mode brainstorms.
π§© Model Slots
Switch models without restarting anything. Slots cover Ollama, MLX, LM Studio, MiniMax cloud and any OpenAI-compatible endpoint.
π§ Up to 45 Skills Injected
Your OpenClaw skills are discovered from disk and injected into the system prompt (up to 45 per request), so the AI knows what it can do.
π₯οΈ Bottom Panel
Cmd+J toggles a bottom panel with Terminal, Inbox and Logs tabs. A real REPL, a real inbox, real logs.
β±οΈ Autosave
Every edit autosaves after a 2-second debounce, with a 30-second trailing save. Plus an explicit Save button and Cmd+S.
π§° CLI + MCP
The quill CLI talks to the same backend from your shell, and an MCP server lets Claude Desktop or Cursor drive Quill.
π Story Bible & Corkboard
A structured lore database (characters, locations, timeline, plot beats) and a Scrivener-style corkboard of chapter cards.
π€ Every Export
PDF, DOCX, Vellum-compatible DOCX, Markdown, TXT, HTML, ePub, RTF, OPML outline, or a ZIP bundle of the whole project.
The 30-second version
Clone the repo, start the Flask backend on http://127.0.0.1:5323, build the Swift app, open a project, and start writing. The AI is on the right. The terminal is at the bottom. The command line is quill.
Requirements
Quill is a developer install β there isn't a signed .app download. Here's what the source actually asks for.
| Requirement | Details | Why |
|---|---|---|
| macOS 15+ | Apple Silicon recommended | SwiftUI + AppKit editor; MLX slots really want a Metal GPU. |
| Xcode 15+ | Plus XcodeGen to regenerate the project | You build the SwiftUI frontend from source. |
| Python 3.11+ | Flask, Flask-CORS, PyYAML, ollama, pytest |
Runs the backend API, the model slots and the tool layer. |
| Ollama | Running, serving http://127.0.0.1:11434 |
The default local model provider for nearly every slot. |
| pandoc | Only for PDF / ePub / DOCX / RTF exports | Quill shells out to pandoc to build binary formats. |
| OpenClaw | Optional, for skill injection | Skills are discovered under ~/.openclaw/skills and ~/.openclaw/workspace/skills. |
Models you'll want
The default slot is Gemma 4 MLX 31B (via Ollama). Pull the ones that match your hardware:
# Big literary default (Apple Silicon, 24GB+)
ollama pull gemma4:31b-mlx
# Fast drafts
ollama pull gemma4:latest
# Long context for plotting
ollama pull qwen3:30b
# Thinking / research
ollama pull gpt-oss:20b
# The model Quill prefers for Tab-to-fix (precise, fast)
ollama pull llama3-groq-tool-use:8b
Cloud slots need a key
The MiniMax slots are cloud providers. Without an API key configured for that slot, the provider reports "no API key" and the slot fails its test. Local slots need nothing but Ollama.
Installation
Three pieces: the Python backend, the Swift frontend, and the quill CLI. Do them in that order.
Step 1: Clone and install backend deps
git clone https://github.com/TheSolai/quill.git
cd quill
# The dependency manifest lives in models/requirements.txt
pip install -r models/requirements.txt
# ...or just the essentials:
# pip install flask flask-cors pyyaml ollama
Heads up, the README misleads slightly
Some docs say cd backend && pip install -r requirements.txt. There is no backend/requirements.txt in the repo β the manifest is models/requirements.txt. Installing from the repo root works fine.
Step 2: Build the Swift frontend
The Xcode project is generated from frontend/project.yml with XcodeGen, so edit the YAML rather than the .xcodeproj.
cd frontend
xcodegen generate
# Build the app
xcodebuild -project Quill.xcodeproj -scheme Quill \
-configuration Debug build
Or just open it in Xcode and hit Run:
open frontend/Quill.xcodeproj
Step 3: Install the quill CLI
The CLI is a single Swift file that talks to the same backend over HTTP. Symlink it into your PATH:
ln -sf /path/to/quill/frontend/Helpers/quill-ai-helper ~/.local/bin/quill
# Make sure ~/.local/bin is on your PATH
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
quill --version # quill 1.1.0
The CLI is optional but good manners
Everything the CLI does is also available in the app's bottom Terminal tab. But a shell that knows about your manuscript is a genuinely different tool.
First Launch
Start the backend first. The app can also auto-start it via its ProcessManager, but running it by hand is how you get to see the logs.
cd backend
python3 server.py
# -> [Quill Backend] Starting on http://localhost:5323
Then open the app. If you're building from the command line:
open frontend/Helpers/Quill.app
# or the Xcode build output:
open ~/Library/Developer/Xcode/DerivedData/Quill-*/Build/Products/Debug/Quill.app
What's on screen
ββ Sidebar βββββββββ¬β Editor ββββββββββββββββββββ¬β AI βββββββββββ β QUILL β chapter-03.md β saved β QUILL β β [+ New Project] β β Gemma 4 MLX β β β The rain had not stopped β β β PROJECTS β for three days now, and β > write β β βΈ Salt & Iron β Maren had stopped counting β chapter 3 β β CHAPTERS β on them. β β β 1 The Salt Road β β Generatingβ¦ β β 2 Low Water β β β β β 3 The Drowned β β β β 4 Smoke β β β ββββββββββββββββββββ΄ββββββββββββββββββββββββββββββ΄ββββββββββββββββ€ β [Terminal] [Inbox] [Logs] β β $ quill status β β backend: ok ollama: ok slot: gemma4-mlx β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Sidebar (left) β projects and chapters. New Project and New Chapter buttons live here, and chapters can be renamed, duplicated or sent to the AI.
- Editor (middle) β the manuscript. A save indicator sits in the header and shows
unsavedβsavingβ¦ββ saved. - AI panel (right) β the writing partner, plus a slot picker showing which model is live.
- Bottom panel β Terminal, Inbox and Logs.
Where your work lives
Quill's base directory is ~/Quill/projects. At startup the backend also looks in ~/Projects/Quill/projects, ~/Projects/quill/projects and ~/quill/projects, and honours a QUILL_PROJECTS environment override. Each project gets a directory with chapter .md files, an exports/ folder, .quill_context.json and .quill_stats.json.
Check the wiring
curl http://127.0.0.1:5323/api/health returns backend status, whether Ollama answers, the active slot id, model id and slot type. /api/info returns the resolved base directory, the Ollama URL and the skill registry status.
The Editor & Tab-to-Fix
The editor is an AppKit NSTextView wrapped in SwiftUI β a real text view, not a web view. Quill subclasses it to intercept the Tab key before it inserts a literal tab character.
How Tab-to-fix works
- Select a passage β or just put the caret inside a sentence or paragraph.
- Press Tab.
- Quill posts the snippet to
/api/edit-fix, which routes it to a fast, instruction-precise model. The backend prefers thegroq-tool-useslot (llama3-groq-tool-use:8b) precisely because it follows instructions tightly and answers fast. - The fixed text replaces the selection in place. Accept it, or hit βZ to undo.
Why Tab and not a button
Because you don't want to leave the prose. You spot a typo mid-paragraph, you hit Tab, you keep writing. The correction is a keystroke, not a dialog. Zed-style, and it works.
Save behaviour
- Autosave β a 2-second debounce after every edit, with a 30-second trailing save as a backstop for continuous typing.
- Save button β in the editor header; its tooltip reads "Save (βS) β autosaves every 2s; press to save now".
- βS β the File β Save menu item. ββ§S is Save As.
Useful keyboard shortcuts
| Shortcut | Action |
|---|---|
| Tab | Inline AI fix on selection / paragraph |
| βS | Save now |
| ββ§S | Save As |
| βJ | Toggle the bottom panel (Terminal / Inbox / Logs) |
| βL | Focus the chapter editor |
| βK | Focus the AI chat input |
| ββS | Toggle the sidebar |
| βE | Export Bookβ¦ |
| ββ§P | Compile Preview |
| ββ₯M | Email the Bookβ¦ |
| βN | New Projectβ¦ |
| ββ§N | New Chapterβ¦ |
| β, | Settings |
Story Bible & Corkboard
Two views beyond plain prose editing. The Story Bible (Codex) is the structured lore database β characters, locations, timeline, relationships, themes, motifs, glossary, voice and plot beats, alongside freeform text fields. quill extract can populate it by reading your chapters. The Corkboard shows chapters as visual cards with editable synopses, Scrivener-style.
The AI Chat & Model Slots
The chat panel is where Quill talks to you as a writing partner. The backend streams the reply over Server-Sent Events from /api/chat, routing to the active slot and parsing tool calls the model emits.
Long form vs short form
- Long form (default) β "write chapter 3", "make chapter 1", "continue chapter 2". Quill switches to a prose-only system prompt: output is the chapter and nothing else β no preamble, no "here you go", no trailing question. The text is written straight into the chapter file.
- Short form β brainstorming, questions, feedback. The default Quill persona, which is allowed to chat back.
Slash commands
Type / in the chat box to pop the command menu: /new, /list, /switch <id>, /delete <id>, /rename <title>. Arrow keys navigate, Return selects, Esc closes. The backend handles slash commands directly rather than sending them to the model.
What the AI knows about
Quill injects a curated block of your OpenClaw skills into the system prompt (up to 45 skills per request, selected by keyword relevance β the full SKILL.md is not dumped in, just enough for the AI to recognise when a skill applies). The skill registry auto-discovers any directory containing a SKILL.md under:
~/.openclaw/skills/~/.openclaw/workspace/skills/~/Projects/thesolai.github.io/skills/
It also exposes a tool registry the model can call: web_search, web_fetch, email_send, email_list_inbox, email_reply, shell_exec, list_files, read_file, claude, codex, openclaw, clawhub and cli_status. Tool calls arrive as fenced JSON blocks that the backend executes and feeds back.
Model slots
A "slot" is a named model configuration. Switching slots switches models β no restart, no reload. The backend ships ten default slots:
| Slot id | Model | Type | Notes |
|---|---|---|---|
| gemma4-mlx | gemma4:31b-mlx | mlx | Default. 24GB, very high quality β the literary one. |
| gemma4-fast | gemma4:latest | ollama | 8GB, very fast. Quick drafts. |
| qwen3-30b | qwen3:30b | ollama | 32K context. Long coherent arcs. |
| gpt-oss-20b | gpt-oss:20b | ollama | 131K context, thinking-heavy. Outlines and structural review. |
| qwen-coder-30b | qwen3-coder:30b | ollama | Code and tooling. |
| llama3-70b | llama3.3:70b | ollama | Epic-scale prose. 42GB. |
| groq-tool-use | llama3-groq-tool-use:8b | ollama | Agentic, precise. Preferred for Tab-to-fix. |
| minimax-text | MiniMax-Text-01 | minimax | Cloud. |
| minimax-m27 | MiniMax-M2.7 | minimax | Cloud, reasoning. |
| minimax-highspeed | MiniMax-M2.7-highspeed | minimax | Cloud, fast. |
Slots are persisted to models/slots.yaml, with the active slot id in models/.active_slot. The provider layer also supports lmstudio (local OpenAI-compatible server on port 1234) and custom (bring your own endpoint and model id).
Switching slots from the CLI
The CLI's quill slots command takes the slot id directly: quill slots gemma4-mlx activates that slot. Passing list or ls prints the table instead. The HTTP API also exposes POST /api/slots/active and POST /api/slots/<id>/activate.
The Bottom Panel
Press βJ (View β Toggle Panel) and a panel slides up under the editor with three tabs. All three are visible by default and can be switched from the View β Tabs submenu.
Terminal
A REPL-style embedded shell. Each command runs as a one-shot Process rather than a full PTY β deliberately, so a runaway command can't wedge the app. Output accumulates in a scrollable history, β/β walk the command history, and Tab does path completion. Because the CLI is on your PATH, all of quill works here:
quill status
quill ask "what does this chapter need?"
quill fix chapter-03.md
quill chapters
quill cat "The Drowned"
Inbox
A full email UI backed by AgentMail. The app polls /api/agentmail/inbox every 30 seconds and shows a list, a reader and a compose form. You can read, reply, draft and send. The backend also exposes /api/agentmail/send, /api/agentmail/reply and /api/agentmail/draft, and the AI can send email on your behalf through the email_send tool.
Logs
Two streams merged into one view:
- Backend log β read from
/tmp/quill_backend.log, polled every 2 seconds. - Action log β in-app actions: saves, fixes, sends, exports.
Filter by level (info / warn / error) and free-text search across both.
Why this matters
Most AI writing tools are a black box: you type, something comes back, no idea if it worked. Quill shows you the terminal, the logs and the inbox in the same window as the prose. You can see what the model actually did.
The quill CLI
A single Swift file, version 1.1.0, that talks to the same Flask backend over HTTP. It points at http://127.0.0.1:5323 by default β override with the QUILL_BACKEND environment variable. Output is colourised (auto-disabled when stdout isn't a TTY, or with NO_COLOR=1 or --no-color), and --json makes any command machine-readable.
Quick commands
quill status # backend, ollama, active slot + model
quill ask "what is 2+2?" # one-shot Q&A with the active model
quill chat # interactive REPL (Tab autocompletes sessions)
quill fix chapter-03.md # fix typos/grammar in place
quill expand chapter-03.md # add sensory detail in place
quill condense chapter-03.md # tighten prose in place
Writing commands
quill projects # list projects
quill chapters # list chapters in the current project
quill cat "The Drowned" # print a chapter to stdout
quill write "The Drowned" "text" # append to a chapter (--overwrite to replace)
quill bible # show the Story Bible
quill bible characters # show one field of it
quill extract # build the Story Bible from your chapters
Inbox, search and external tools
quill inbox # list recent emails
quill inbox read <id>
quill inbox send "to@example.com" "Subject" "Body"
quill inbox reply <id> "text"
quill mail-book --to you@example.com # bundle the project and email it
quill search "Tolkien influences" # web search via DuckDuckGo
quill mmx "..." # MiniMax cloud AI
quill claude "..." # run the Claude Code CLI
quill codex "..." # run the OpenAI Codex CLI
quill openclaw "..." # run OpenClaw
quill clawhub "..." # run ClawHub
Admin commands
quill slots # list AI slots
quill slots gemma4-fast # switch the active slot
quill skills # list OpenClaw skills
quill skills show <name> # show one skill
quill mcp serve # MCP server on stdio
quill setup # diagnostic report: CLI, backend, slots
quill version
Global flags and pass-through
--project IDβ override the current project.--slot IDβ override the AI model slot.--jsonβ machine-readable output.--no-colorβ disable ANSI colours.-h, --helpβ general help, orquill help <cmd>for one command.
And the good trick: any unknown command is passed straight to /bin/sh -c. So quill ls, quill pwd and quill git status all work.
Heads up on the README's command list
The README advertises scenes and email subcommands. In the current CLI source those don't exist β the equivalents are quill inbox (for email) and quill bible / quill extract (for the story-bible side of things). The source is the authority; quill help prints the real list.
MCP Server
Quill speaks the Model Context Protocol, so an external agent β Claude Desktop, Cursor, or anything else that speaks MCP β can drive your manuscript.
stdio mode
The cleanest option for a desktop client. Add this to your MCP config:
{
"mcpServers": {
"quill": {
"command": "quill",
"args": ["mcp", "serve"]
}
}
}
Under the hood quill mcp serve re-executes the helper with --no-color mcp serve so stdin and stdout stay clean for the protocol. Ctrl-C to stop it.
HTTP mode
The backend also exposes JSON-RPC 2.0 over HTTP at http://127.0.0.1:5323/api/mcp. tools/list returns the tool catalogue; tools/call runs one.
Available MCP tools
| Tool | What it does |
|---|---|
| list_projects | List every Quill project |
| list_chapters | List chapters in a project |
| read_chapter | Read a chapter's full content |
| write_chapter | Write content to a chapter (creates if missing) |
| edit_fix | Fix typos and grammar in a text snippet |
| search_web | Web search via DuckDuckGo |
| shell_exec | Run a shell command |
| list_files / read_file | Browse and read local files |
| send_email / list_inbox | AgentMail inbox and sending |
| list_skills / read_skill | Inspect the OpenClaw skill registry |
| claude / codex / openclaw / clawhub / cli_status | Shell out to the external CLIs and report their status |
The MCP server is powerful
shell_exec, write_chapter and send_email are real capabilities with real consequences. Anyone who can reach the MCP server can write to your manuscript and run commands on your machine. Keep it bound to 127.0.0.1 and don't expose it to a network you don't control.
Exporting Your Work
File β Export Bookβ¦ (βE) opens the export sheet, with a Compile Preview tab (ββ§P) to see the assembled manuscript before you commit. The backend endpoint is GET /api/projects/<project_id>/export/<format>.
| Format | What you get |
|---|---|
| Print-ready PDF with formatting (via pandoc) | |
| DOCX | Standard Microsoft Word document |
| Vellum DOCX | Vellum-compatible β auto-detects chapters and scene breaks |
| MD | Raw markdown, all chapters merged |
| TXT | Plain text, no formatting |
| HTML | Standalone styled web page for reading |
| ePub | E-book format for Kindle, iBooks and friends |
| RTF | Rich Text Format β universal |
| OPML | Outline editor format (chapters only) |
| Bundle | ZIP with all chapters plus manifest.json |
Vellum-compatible DOCX
The Vellum export is the nerdier corner of Quill, and it's good. The builder follows Vellum's import rules precisely:
- Chapter titles are Word's Heading 1 style.
- Each chapter starts with a page break, then the Heading 1.
- Scene breaks are detected from a single blank line, or a centred
***(also###and+++). - No manual indentation in paragraphs β Vellum handles that.
- Subheads use Heading 2 / Heading 3.
Drop the file into Vellum and your chapters and scene breaks appear, already structured. No manual cleanup.
pandoc does the heavy lifting
PDF, ePub, DOCX and RTF are produced by shelling out to pandoc on the compiled markdown. Markdown, plain text and HTML are generated in pure Python and need no external tool. Install pandoc (brew install pandoc) or those four formats will fail with "could not build β¦ (is pandoc installed?)".
Emailing the manuscript
File β Email the Bookβ¦ (ββ₯M) packages the project and sends it. The backend exposes /api/projects/<id>/email-the-book, email-the-chapter, email-compiled and email-zip. The compiled-book endpoint accepts pdf, epub, docx, html, md, txt and rtf. From the shell, quill mail-book --to you@example.com does the same thing β with --dry-run to preview before it sends.
Troubleshooting
quill status says the backend isn't reachable
Start it:
cd backend && python3 server.py
# -> [Quill Backend] Starting on http://localhost:5323
Then check what the CLI is actually talking to β the CLI honours QUILL_BACKEND:
echo $QUILL_BACKEND # should be empty or http://127.0.0.1:5323
curl http://127.0.0.1:5323/api/health
Ollama shows as down
The health check pings http://127.0.0.1:11434/api/tags with a 2-second timeout. If Ollama isn't running, start it with ollama serve. If you're using a non-default Ollama port, the slot's endpoint needs updating in models/slots.yaml.
Tab-to-fix does nothing
- The backend picks the first slot whose model id mentions
groq-tool-use. If you don't have that model pulled, point it at another slot β or pullollama pull llama3-groq-tool-use:8b. - Make sure the editor has focus and there is a selection or caret inside a paragraph.
- Check the Logs tab in the bottom panel for a
/api/edit-fixerror.
The AI chat hangs with no output
Chat is streamed over SSE. Check the backend log at /tmp/quill_backend.log (also visible in the Logs tab). A 30-minute URLSession timeout applies to long generations, but a stalled slot will still look like nothing is happening. Run quill setup for a one-shot diagnostic of backend, slots and CLI tooling.
Export fails for PDF / ePub / DOCX / RTF
pandoc is missing or not on the PATH. brew install pandoc. Markdown, TXT and HTML work without it.
No skills are being injected
- Check
quill skillsβ if the registry is empty, the skill directories aren't where Quill looks. It scans~/.openclaw/skills,~/.openclaw/workspace/skillsand~/Projects/thesolai.github.io/skills. - Each skill needs a real
SKILL.mdβ the registry auto-discovers by directory scan, and reads YAML-ish frontmatter for the description and keywords. quill skillshits/api/skills;/api/skills/reloadforces a rescan.
Model looks slow or rambles
Switch slots. The default gemma4-mlx slot is optimised for literary prose with a long context; gemma4-fast is much quicker for drafts. For structural work, gpt-oss-20b has a 131K context window. Each slot carries its own temperature, top_p and context length, editable in models/slots.yaml.
Reset the model layer
# Wipe slots and the active-slot pointer (defaults are rewritten on next run)
rm -f models/slots.yaml models/.active_slot
Credits & Links
Quill is open source and MIT licensed. The repo is the guide; this page just makes it easier to read.
- Source: github.com/TheSolai/quill
- Issues: github.com/TheSolai/quill/issues
- Backend API:
http://127.0.0.1:5323β/api/health,/api/info,/api/projects,/api/slots,/api/chat,/api/edit-fix,/api/skills,/api/mcp
Built with
- SwiftUI + AppKit β the native frontend and the
NSTextViewsubclass behind Tab-to-fix - Flask β the backend API, streaming SSE chat, and the MCP endpoint
- Ollama β the default local model provider
- MLX β Apple Silicon inference for the MLX slots
- python-docx β the Vellum-compatible DOCX builder
- pandoc β PDF, ePub, DOCX and RTF conversion
- DuckDuckGo β the HTML-scraped web search behind
quill search - AgentMail β the inbox behind the Inbox tab
Tests
The backend ships a pytest suite:
cd backend
python3 -m pytest tests/ -v
Why Quill exists
Because the best AI writing tool isn't a chat window with a document picker β it's a writing app that happens to know a model. Quill puts the prose in the middle, the partner on the right, and the machine's guts in plain sight at the bottom. No Electron. No browser. Just an app that writes.
π¬ Join the Discussion
Share your thoughts. Requires a free GitHub account.