Sloth Suite
Sloth Suite is one native macOS window that wraps your local AI apps and turns them into a proper learning desk. Right now it ships with one wrapped app — SlothRead, which turns papers and books into executive briefing decks — and wraps it in a library, a briefing reader, notes, spaced repetition and audio narration that all live on your machine.
Sloth Suite 1.0.0
What is Sloth Suite?
Sloth Suite is a native macOS application, written in Swift against AppKit and WebKit. It is a shell: it hosts local AI apps in webviews and gives you a proper workspace around them — a library of everything you have imported, a reader that renders briefings natively instead of leaving them in a web page, notes attached to the documents they came from, a spaced-repetition desk, and narration you can listen to.
Today the suite ships one wrapped app:
| App | What it does | Frontend | Backend |
|---|---|---|---|
| SlothRead | Turns papers and books into executive briefing decks | http://127.0.0.1:8300 |
http://127.0.0.1:8100 |
The backend talks to a local Ollama daemon or to Apple's own on-device model. The suite itself adds no cloud dependency of its own — but note that the backends can perform web lookups if you switch them on, which is why the sidebar no longer claims "offline" as an unconditional promise.
The architecture, briefly
- One window. A sidebar, a module view and a status line, all native AppKit.
- One webview per app. Each app's
WKWebViewis kept alive and hidden when inactive, so an in-flight synthesis or an uploaded book survives switching away. - A supervisor. Health is polled every five seconds; when a service that was answering stops answering, that service is restarted — stop, wait for its ports to be released, then start.
- Native modules need no web app. The reader fetches the briefing straight from the backend and renders it itself.
Everything stays on your disk
Notes, favourites, read state and reading positions live in ~/Library/Application Support/SlothSuite/learning.json. The configuration sits next to it in config.json. Nothing else is stored anywhere.
What's Inside
Five modules in the sidebar, generated from one list in Modules.swift. Here is the whole thing, in one grid.
📚 The Library
Every document on this machine as a card grid, with search, filter chips, sorting, favourites, read state and a per-card action menu.
📖 The Reader
The briefing rendered natively: cover, pillars, key terms, overview. Find with ⌘F, listen with ⌘S, pop it out into its own window.
🗒 Notes
A note desk with a list, an editor and a search box that filters as you type. Notes auto-save, and keep the passage they were written about.
🧠 Review
Spaced repetition over your own notes. "Got it" pushes a note further out; "Again" brings it back in ten minutes.
🖥 The App
The wrapped SlothRead web app — the synthesis engine that produces briefings in the first place.
🔊 Audio
Briefings read aloud with a queue, plus a pop-out window listing every chapter and its full spoken script.
⌘K Palette
A Spotlight-style command palette that searches modules, the library and the notes — switch, import, export, speak, restart, toggle.
📤 Exporter
Writes a briefing out as a folder of separate files: an executive summary, one file per chapter, the audio scripts and the sources.
📥 Drag & Drop
Drop a PDF or EPUB onto the window and it imports straight away, instead of hunting for an upload button.
🩺 Self-test
An assertion-based self-test that checks the config, ports, health endpoints, bridge, library, voices and the reader — and exits non-zero on failure.
Requirements
Sloth Suite builds from source with swiftc. There is no prebuilt installer, and that is deliberate — the suite is a shell around your own local stack.
To build and run the shell
- macOS 13 or later — the shell's deployment target.
- Xcode Command Line Tools — the build uses
swiftc. Install withxcode-select --install. - Node 18+ — for the wrapped app's frontend.
- Python 3.11+ — for the SlothRead backend.
For the Mac services
- macOS 26 — the bundled
mac-servicehelper needs a macOS 26 deployment target, because Apple's on-device Foundation Models framework requires it. - Apple Silicon running macOS 26+ — needed for the on-device model and the Mac voice.
The helper is optional
If the FoundationModels SDK is missing, the build simply leaves the helper out and the suite falls back to Ollama and the browser's own voices. The build does not fail — it prints a note and carries on.
For the wrapped app's stack
The suite expects a stack script at ~/Projects/sloth-stack.sh that controls your local processes:
./sloth-stack.sh # start every service
./sloth-stack.sh start read # start one service
./sloth-stack.sh stop mode # stop one service
./sloth-stack.sh restart read # restart one service
./sloth-stack.sh status # show what is listening
Stack logs land in ~/Library/Logs/SlothSuite/stack/. The suite's own log is ~/Library/Logs/SlothSuite/slothsuite.log, rotated at 1.5 MB with the previous version kept as slothsuite.log.1. "Show Logs" in the sidebar opens the folder.
Installation
There is no download. You clone, you build, it installs itself into ~/Applications. Four commands, that is the whole story.
# Clone the repo
git clone https://github.com/TheSolai/slothsuite.git
cd slothsuite
# Build a universal binary and install to ~/Applications
./build.sh
# Build, install and launch
./build.sh run
# Build, then run the self-test gate (non-zero exit on failure)
./build.sh test
# Remove build artifacts
./build.sh clean
What the build actually does
- Compiles the Swift sources twice — once for
arm64, once forx86_64— andlipos them into one universal binary, so the same bundle runs on an Intel Mac. - Assembles
Sloth Suite.appwithInfo.plistand the app icon. - Compiles the
mac-servicehelper if the FoundationModels SDK is available (and skips it with a note if not). - Ad-hoc signs the bundle — enough to launch locally, no developer account needed.
- Copies it to
~/Applications/Sloth Suite.app.
Sharing it with anyone else
The bundle is ad-hoc signed, which is fine on your own Mac but will trip Gatekeeper on somebody else's. To hand it to another person you need a real Developer ID certificate and notarisation — neither of which the build script does for you.
Configuration
The suite reads ~/Library/Application Support/SlothSuite/config.json, written with sensible defaults on first run. Pass --config <path> to use a different file. If the file exists but cannot be read, the app says so out loud and falls back to the built-in defaults rather than failing silently.
{
"stackScript": "~/Projects/sloth-stack.sh",
"logDirectory": "~/Library/Logs/SlothSuite",
"pollInterval": 5.0,
"openAtLogin": false,
"services": [
{ "id": "read", "label": "SlothRead", "ports": [8300, 8100] }
],
"apps": [
{
"id": "read",
"title": "SlothRead",
"tagline": "Executive decks from papers & books",
"url": "http://127.0.0.1:8300/",
"healthURL": "http://127.0.0.1:8100/api/health",
"dotColorHex": "#2AA198",
"apiBase": "http://127.0.0.1:8100",
"libraryPath": "/api/documents",
"service": "read"
}
]
}
Old configs get migrated
If your config still lists the old SlothMode app, the suite strips those entries out on load and rewrites the file. A stale stack-script path — a moved project, a renamed machine — is also recovered automatically by looking in the usual places.
First Launch
Launch it and you land on the library, with the sidebar on the left and one quiet status line underneath the navigation. That status line takes the colour of the state it describes: green for running or ready, gold for starting or waiting, rose for down.
┌─ Sidebar ────────┐┌─ Library ────────────────────────────┐ │ Sloth Suite ││ Your library [Search…] │ │ Your learning desk││ │ │ ││ [All] [Favourites] [Unread] [Ready]│ │ 📚 Library ││ Sort: [Recent ▾] │ │ 📖 Reader ││ │ │ 🗒 Notes ││ ┌────────┐ ┌────────┐ ┌────────┐ │ │ 🧠 Review ││ │ Title │ │ Title │ │ Title │ │ │ 🖥 App ││ │ author │ │ author │ │ author │ │ │ ││ │ 8 min │ │ 12 min │ │ 4 min │ │ │ ● Running ││ │ saved │ │ saved │ │ saved │ │ │ Ollama · qwen3 ││ └────────┘ └────────┘ └────────┘ │ │ Phonon · default ││ │ │ (…) ││ [Add Document] [Continue reading] │ └───────────────────┘└──────────────────────────────────────┘
What happens in the first minute
- The suite reads
config.json, or writes the defaults if there isn't one yet. - It looks for
~/Projects/sloth-stack.shand runs it to bring the local services up. - The health monitor starts polling
http://127.0.0.1:8100/api/healthevery five seconds. The status line and the app's status dot follow it. - The library starts polling
/api/documentsevery fifteen seconds, so a document that finishes synthesising shows up without you refreshing anything. - The bundled
mac-servicehelper is launched on start and stopped on quit. It binds to127.0.0.1:8199only.
Pick your AI provider
The backend can summarise with Apple's on-device model instead of Ollama. Choose it from the sidebar, from the command palette, or with POST /api/provider {"provider":"mac"}. The choice is persisted on the backend, so it survives a restart. Ollama is the fast one; the Mac model is the private one — and it takes roughly 45 seconds to answer on an Apple Silicon Mac.
The window remembers
The suite reopens on whichever module you were using last. "Open at Login" is in the app menu, and is off by default.
The Library
The library is every document the backend knows about, as a card grid. It is the module you land on and the module you come back to, so it earns the most buttons.
Finding things
- Search — type in the search field, or press
⌘Lto focus it. It matches title, author and filename. - Filter chips — All, Favourites, Unread and Ready. Each chip shows how many documents it would show.
- Sort — Recent, Title, Author or Longest. In the default order, favourites float to the top; when you ask for an order, they don't — sorting by title and then putting favourites on top would not be the order you asked for.
What a card tells you
Type (PDF or EPUB), title, author, date, chapter count, word count, how many minutes the briefing saved you, and — for a book you have started — "Ch 3 of 10", so a half-read book does not look like an unread one. A card also carries a star (favourite) and a read tick.
The card menu
Every card has a ⋯ button, and a right-click on the card opens the same menu:
| Action | Shortcut | What happens |
|---|---|---|
| Open | ⌘O |
Opens the document in the reader, on its briefing. |
| Send to Notes… | ⇧⌘N |
Turns the briefing into a note: central argument, executive summary, primary conclusions and key points, as editable text. |
| Add a note… | — | An empty note on the book. |
| Speak the summary… | ⌘S |
Narrates the summary aloud. |
| Export as separate files… | ⌘E |
Writes the briefing out as a folder of files. |
| Favourite | ⌘D |
Toggles the star, and says which way round it is for this book. |
| Mark as read | ⌘R |
Toggles the read state. |
| Remove from library | ⌘⌫ |
Deletes the document from the library. |
Two more library buttons
- Add Document — opens a file picker. PDFs and EPUBs.
- Continue reading — the one thing that takes you straight back to where you were, because that is what its name says.
- Mark all read — marks the whole library read, and when everything is already read it offers to undo that.
Drag and drop works too
Drop a PDF or EPUB onto the window and it imports straight away. You do not need to find the upload button and browse for the file.
The Reader and Briefings
The reader is the native half of the suite. It fetches the briefing straight from the backend and renders it itself, so you are not reading an executive deck inside a web page — and the native modules work even when the wrapped app is not running.
Every book opens on its briefing
A book opens on the Executive Briefing & Thesis, in the deck's own shape:
- the strip that says Executive Synthesis & Global Thesis · Full Manuscript Distillation, with a hairline under it;
- the page title, Executive Briefing & Thesis;
- the Central Argument — the thesis as a pull quote on a teal panel;
- three numbers side by side — Time Saved, Original Reading Time and Distillation Ratio;
- the Primary Strategic Conclusions, one to a numbered row.
┌─ Header ──────────────────────────────────────────────────────┐ │ [Briefing │ Original] Continue at Chapter 12 ● Live │ │ [Listen ▾] [All audio] [Stop] [Chapters] [Pop out] │ ├─ Chapter bar ─────────────────────────────────────────────────┤ │ Overview │ 1. Foundations │ 2. Method │ 3. Findings │ … │ ├─ Reading column ────────────────┬─ Rail ─────────────────────┤ │ CHAPTER 15 OF 39 · ~402 words │ ┌─────────────────────────┐ │ │ ───────────────────────────── │ │ ① The rule of three │ │ │ The title of the chapter │ │ The briefing's spine │ │ │ │ └─────────────────────────┘ │ │ (1) First takeaway │ ┌─────────────────────────┐ │ │ (2) Second takeaway │ │ Glossary · key terms │ │ │ (3) Third takeaway │ └─────────────────────────┘ │ │ │ ┌─────────────────────────┐ │ │ "A 60-second recap, quoted." │ │ 🔊 60-second recap │ │ │ │ └─────────────────────────┘ │ │ chips: term · term · term │ [Play] │ └─────────────────────────────────┴────────────────────────────┘
The reader's four faces
- Cover — the briefing itself: thesis, summary, conclusions, and the band of figures at the top.
- Pillars — the three cards in the rail: the rule of three, the interactive glossary and the spoken recap. Each one shows the chapter actually on screen.
- Terms — a book's vocabulary as clickable chips and keyword tags, each opening a pop-up with the other chapters that use it.
- Overview — the band at the top of the reader: how many key terms and keywords the book has, its most-discussed term, and a word cloud of the words it keeps coming back to. Every word in the cloud is a way in.
What a chapter looks like
Each chapter is laid out the way the wrapped executive deck laid it out, with every block in its own place:
- a kicker — "CHAPTER 15 OF 39 · pp. Chapter 15 · ~402 words" — so you always know where you are;
- the chapter title, set large;
- the three key takeaways, each on its own tinted panel with a numbered badge;
- the three key terms as chips — hover one and the definition appears;
- the keyword tags as small blue chips;
- the glossary, the tips and the hints, each on its own panel;
- the 60-second recap, quoted, in a teal panel.
It is still one piece of text
The panels and badges are painted behind and beside the text by the text view, rather than pasted in as images. That is what lets them follow the system appearance — and it means ⌘F still finds "335 min", ⌘A still selects it and ⌘C still copies it.
Briefing or Original
The Briefing │ Original switch in the chapter row switches between the two faces of one book. Original shows the book's own words, one chapter to a page, in paragraphs — and the rail and the strip of key terms are put away, because they are the briefing's analysis of the book, not the book.
- Select any passage and press Add Note to write a note about those words. The note keeps the passage and the chapter.
- Show the passage on a note's row menu opens the book at that chapter, with the passage chosen and the eye sent to it.
- A book whose original file is missing says so — File ▸ Attach the Original… gives it back from your own disk, against the same book, without paying for another synthesis.
Your place is kept
- Every document keeps its own place. Reading a second book no longer loses your place in the first.
- A book opens on its briefing, and the header carries a Continue at Chapter 12 chip when there is somewhere to go back to. The place is offered, not imposed.
- The chapter is restored, and so is how far down it you had got — as a fraction of the chapter, so it still lands in the right place in a different window size.
- Pop out gives the reader a window of its own. It is not a picture of the reader: it is a second reader on the same book, and the two are kept in step.
Notes and Your Learning Store
Notes are plain AppKit: a list on the left, the note on the right, and a search box that filters as you type. They live in ~/Library/Application Support/SlothSuite/learning.json, next to the configuration, and nowhere else.
Writing a note
- New Note (or
⌘N) makes a note and puts the caret in it. - The note saves itself a moment after you stop typing, and again when you leave the module or quit the app — so nothing is lost between keystrokes and the disk.
- The row menu (⋯) opens the document the note belongs to, or deletes the note.
- A note made from the reader remembers its chapter, and opening it from the note list goes straight to that chapter rather than to the top.
- File ▸ Export Notes… (
⇧⌘E) writes every note to a Markdown file, grouped by the document it came from.
What the learning store holds
| Thing | Where it shows up |
|---|---|
| Favourites | The star on a library card; the Favourites filter chip; the top of the default sort. |
| Read state | The tick on a library card; the Unread filter chip; "Mark all read". |
| Notes | The Notes module; the reader's "Add Note"; the Review desk. |
| Reading position | "Continue at Chapter 12"; the "Ch 3 of 10" on a card; "Continue reading". |
Review — the spaced repetition desk
Review is spaced repetition over your own notes, and it is the module the sidebar had been holding a place for.
- Each note becomes a card. The first line is the prompt; the rest of the note is the answer, hidden until you ask to see it.
- Got it moves the note further out — a day, then three, then a week, then a fortnight, then a month.
- Again brings it back in ten minutes, so it is still there before you close the app.
- A note nobody has reviewed is due straight away.
- The header says how many are due and how many are waiting for another day, so an empty desk does not look like an empty brain.
Notes contain your own words
Everything in the learning store is plain JSON on your disk — no database, no encryption, no sync. If that matters to you, the file is trivially readable, and trivially deletable.
Listening to a Briefing
The briefing can be read aloud, and the shell is what plays it. The Mac service hands back WAV bytes and AudioPlayer plays them through AVAudioPlayer — with a queue, so one chapter does not cut off the next.
Three ways to listen
- Listen — reads the current chapter. It is a menu: Read this chapter, Read the whole briefing, and Voice.
- All audio — reads every chapter, one after another, in order.
- Chapters — opens a pop-out window with every chapter and its full spoken script on one scrollable page, each with its own Play button.
Stopping it
A voice that cannot be stopped is a voice that reads to the end of the book, so there is a stop wherever the voice can be started:
- a Stop button in the reader's header, greyed out when the suite is quiet and live the moment the voice starts;
- the sound card in the reader's rail, whose play button becomes a stop button while anything is being read;
- Stop Audio in the File menu, on
⌘., which works from anywhere.
The stop is live the moment the speech is asked for, not when the audio starts playing — so a long chapter can be called off before the Mac has finished making it. The dock badge follows the voice too: 🔊 while the Mac is speaking, 📋 when diagnostics are copied.
The voices
The books can be read in more than one voice, and the voices all run on this Mac.
- Phonon is Gradium's text-to-speech model, which Ollama runs locally. It has thirty-eight voices — plain narrators like Freya, Harper and Zoey, and character voices like Grub the Ogre, Fairy Godmother and Captain Silas Blackwater. Ollama speaks through an OpenAI-compatible endpoint, so the suite asks it for audio the same way it would ask a cloud service, except that the address is this machine's own and no key is involved.
- The Mac's own voice —
AVSpeechSynthesizer, no API key, no payment, and nothing leaves the machine. The suite falls back to it if Ollama is not running: a book read in the wrong voice is better than a book that is not read at all.
The voice is chosen in the reader's Listen ▸ Voice menu, or from the command palette ("Reading voice"). The current one is ticked, and the sidebar says which is in use.
Long chapters are split
A chapter is longer than Ollama will take in one request — the limit is 4096 characters — so the text is split at a paragraph, then at a sentence, then at a space, and the pieces are read one after another. A word is never cut in half. The audio is made a piece at a time and played as it arrives, so the first words of a chapter are heard in a few seconds rather than after the whole chapter has been made.
The Command Palette
Press ⌘K and a Spotlight-style palette appears. Type to filter, arrow keys to move, Return to run, Escape to dismiss.
The suite does a lot of things — switch module, import, export, restart, toggle web lookups, open the log — and burying them in a sidebar makes them hard to find. The palette is one keystroke away, and it searches more than just commands: the list is rebuilt on every keystroke, so it also searches your library and your notes as they are right now.
┌────────────────────────────────────────────────┐ │ Type a command… │ ├────────────────────────────────────────────────┤ │ ▸ Library │ │ Reader │ │ Notes │ │ Review │ │ App │ │ Add Document │ │ New Note │ │ Export as separate files… │ │ Read everything aloud │ │ Audio and Chapters… │ │ Use the Mac model for AI │ │ Turn web lookups off │ │ Restart the stack │ │ Copy diagnostics │ │ Keyboard shortcuts │ │ — library & note matches appear here too — │ └────────────────────────────────────────────────┘
Every shortcut, in one place
Press View ▸ Keyboard Shortcuts… inside the app for the same list in a sheet.
| Shortcut | What it does |
|---|---|
⌘K | Open the command palette |
⌘1 – ⌘5 | Switch between Library, Reader, Notes, Review and App |
⌘L | Focus the library search |
⌘F | Find in the open briefing |
⌘N | New note |
⌘O | Add a document |
⌘E | Export as separate files |
⇧⌘E | Export the notes as Markdown |
⌘S | Speak the open briefing |
⌘. | Stop reading aloud |
⌘\ | Show or hide the sidebar |
⌘R | Reload the app |
⌘D | Copy diagnostics |
Diagnostics, on demand
⌘D copies a diagnostics bundle to your clipboard: versions, ports, health, and a log tail. When something has gone wrong, that is the first thing to paste into a bug report.
Exporting a Briefing
Export as separate files… (⌘E) writes a folder, not one enormous Markdown file. The old export produced a single unreadable document — for a 66-chapter book, one file. The new one writes:
- an executive summary;
- one file per chapter;
- the audio scripts;
- the sources.
You pick the destination folder, and the exporter creates a subfolder named after the document inside it. Filenames are sanitised for the filesystem — slashes, colons, quotes and other illegal characters are replaced — and kept unique, so two chapters that share a title do not overwrite each other.
Choose a folder for the exported briefing. ┌─ Some Book ────────────────────────┐ │ 00 Executive Summary.md │ │ 01 Foundations.md │ │ 02 Method.md │ │ 03 Findings.md │ │ ... │ │ Audio Scripts.md │ │ Sources.md │ └────────────────────────────────────┘
Exporting your notes
File ▸ Export Notes… (⇧⌘E) is a separate thing, and worth knowing about: it writes every note you have written to one Markdown file, grouped by the document each one came from. Notes are the thing you wrote, so they are the thing worth getting out of the app.
Notes export format
The file starts with a heading and a count, then a section per document, then a section per note with its chapter label, its body and the date it was written.
Running the Self-test
The self-test asserts rather than merely observing, and it exits non-zero if anything fails — so it works as a build gate in CI or a pre-commit hook.
# Build, then run the self-test gate
./build.sh test
# Against a specific document
./build.sh test -- --fixture ~/Documents/some-paper.pdf
What it checks
- Configuration — that apps and services are configured, and that the stack script is readable.
- Ports — every port each service owns is actually open.
- Health endpoints — every app's health URL answers.
- The webview — that it is attached, that the page becomes ready, and that it has a URL and a title.
- The bridge — a full command round trip through the page's own handler.
- The library endpoint — that documents come back.
- The web-tool endpoints — that they are not 404.
- The file picker — that it opens and returns a URL.
- An upload — that an uploaded document actually reaches the briefing deck. This is the one that needs a fixture.
And the parts that are easy to break
- The voice — that the stop is greyed out when the suite is quiet, that it comes alive when the voice starts, and that pressing it makes the voice quiet.
- The voices — against the real Ollama on this machine: that a long text is split into pieces Ollama will accept, that the voices can be fetched, and that Ollama returns actual audio. If Ollama is not running the checks say so and the gate carries on.
- The book — that the library still has the original file, that the pages it serves have real words in them, that the original mode really puts the book's words on the page instead of the briefing's, that a note written on a passage keeps the passage, and that a note takes the reader back to the passage it was written about.
- The reader's behaviour — that a book opens on its briefing, that the briefing is there in the deck's shape, that the card's menu and a right-click on the card offer the same list, and that "Send to Notes" really does make a note out of a briefing.
- The notes module — a full end-to-end check, because the store can be perfectly healthy while the module is broken. It presses the real New Note button, types through the editor's own insertion path, waits for the auto-save to land on the disk, and confirms the note is still there after a trip through another module.
A run leaves your books exactly as it found them
The whole learning store — notes, places, favourites, read state, the "continue reading" pointer — is taken before the first check and put back after the last one, and the put-back is written to the disk before the process exits. The notes check cleans up after itself.
Rendering the interface
screencapture needs screen-recording permission, which a command-line agent does not have — so the shell can render itself. --snapshot <module>:<path> shows a module, renders the window to a PNG and exits. The modules are library, reader, notes, app and audio.
./build.sh
build/Sloth\ Suite.app/Contents/MacOS/SlothSuite --snapshot library:/tmp/library.png
# Render at the window's smallest useful size
build/Sloth\ Suite.app/Contents/MacOS/SlothSuite --snapshot reader:/tmp/reader.png --size 1000x680
Troubleshooting
"The stack script was not found"
The suite could not find ~/Projects/sloth-stack.sh. It looks in three places — that path, ~/Projects/SlothSuite/sloth-stack.sh and ~/Projects/SlothSuite/../sloth-stack.sh — and adopts whichever it finds, saving the corrected path back to the config so the fix survives the next launch. If none of them exist, the suite still starts; it just has nothing to start.
The status line says "down"
- Check ⌘D — the diagnostics bundle tells you which ports are open and what the health endpoint said.
- Run
./sloth-stack.sh statusto see what is listening. - Look at
~/Library/Logs/SlothSuite/stack/for the service's own output, andslothsuite.logfor the shell's. - The supervisor restarts a service that was answering and then stopped answering. If it is failing every time, the problem is in the service, not the suite.
Ollama is not answering
- Is the daemon running?
ollama serve, or check its own logs. - The Mac's own voice is the fallback, so a book will still be read aloud — in the Mac's voice instead of Phonon's.
- The sidebar's provider line tells you which provider is currently selected.
The Mac provider answers very slowly
That is expected. The on-device model takes roughly 45 seconds to answer on an Apple Silicon Mac, because it is a single resource and the helper asks it one question at a time. Ollama is the fast provider; the Mac model is the private one.
The Mac provider stops answering entirely
The helper can wedge — an abandoned request leaves the on-device model waiting for ever, while /mac/status still says "available". The suite watches for it: the helper is asked whether it is there every 45 seconds, and two failures in a row restart it. The port is checked before anything is stopped, so a different program that happened to be listening is left alone.
"mac-service could not be built (no FoundationModels SDK?)"
Expected on anything older than macOS 26. The build carries on without the helper, and the suite falls back to Ollama and the browser's own voices. Everything except the Apple on-device model and the Mac voice still works.
Gatekeeper blocks the app on another Mac
The bundle is ad-hoc signed, which is enough to launch locally but not enough for somebody else's machine. You need a Developer ID certificate and notarisation before it will open cleanly elsewhere. On your own Mac, right-click the app → Open → confirm, once.
The library is empty
- The library polls
/api/documentson the backend — if the backend is down, there is nothing to list. - Check the status line and the health endpoint.
- Documents still processing or queued show up as cards in a working state; only completed ones are "Ready".
The self-test fails on the upload check
That check needs a real document. Pass one: ./build.sh test -- --fixture ~/Documents/some-paper.pdf. Without a fixture the check reports that it could not find one rather than silently passing.
Resetting your data
Notes, favourites, read state and reading positions are one file: ~/Library/Application Support/SlothSuite/learning.json. Delete it and the suite starts with a clean desk. Configuration lives next to it in config.json, and is written back out with defaults if you remove it.
Credits & Links
Sloth Suite is open source, MIT licensed, and maintained by Sol AI and Anne Marie Lee.
- Source: github.com/TheSolai/slothsuite
- Issues: github.com/TheSolai/slothsuite/issues
- License: MIT
Built with
- AppKit — the native shell, the sidebar, the reader and the notes
- WebKit — one webview per wrapped app
- FoundationModels — Apple's on-device model, via the bundled
mac-service - AVSpeechSynthesizer &
AVAudioPlayer— the Mac's own voice, and the queue that plays it - Ollama — the local model daemon, and the Phonon voices
- Swift &
swiftc— a universal binary built straight from the command line
The wrapped app
- SlothRead — the synthesis engine: papers and books in, executive briefing decks out. Frontend on
127.0.0.1:8300, backend on127.0.0.1:8100. - mac-service — the bundled helper on
127.0.0.1:8199, exposing/mac/status,/mac/chatand/mac/speak.
One more thing
The suite never scrapes the DOM. A shim is injected at document start and the page implements a single handler, window.__slothSuiteHandle, which the shell calls and receives plain JSON back over the sloth message handler. A page that has not implemented a command yet falls back to the old DOM-based behaviour, so the shell keeps working against an older build.
Thank you
A native shell around local AI apps is a small idea with a lot of fiddly edges: port probing, appearance changes, a model that answers in 45 seconds, a voice that must be stoppable. Sloth Suite is the result of fixing all of that in public. If it saves you an afternoon of AppKit, that is the whole point.
💬 Join the Discussion
Share your thoughts. Requires a free GitHub account.