Local-first macOS AI assistant with voice, screen, text, memory, and Discord integrations.
Donna is an ambient assistant that runs from one command, stores its working state on your Mac, and only reaches out when a feature needs an external service: Discord for messages, Gemini for the brain, and optional Composio or Ollama integrations for tools and local model work.
Visual proof is the one missing repo asset: add a 20-30 second GIF here showing
donna run, the menubar, a voice turn, or a Discord DM exchange.
I wanted an assistant that lives close to the machine instead of pretending the cloud owns all context. Donna keeps config, secrets, transcripts, history, and memory under ~/.donna/, uses native macOS capabilities where they fit, and exposes the same brain through voice, screen, text, and scheduled briefing workflows.
The thesis is simple: personal AI should be useful across surfaces, but the durable context should stay local by default.
Donna ships four connected modes:
- Briefing: pulls Discord
#ideasinto structured briefings on a schedule, then posts to Discord or email. - Voice: press Option-Space, speak, and hear a reply. STT runs locally with Whisper, TTS uses macOS
say, and Gemini handles reasoning. - Screen: passively captures, deduplicates, OCRs, describes, redacts, stores, and batch-summarizes screen context.
- Text: DM Donna's Discord bot from desktop or phone. Replies are markdown-friendly, owner-allowlisted, chunked for Discord, and backed by persistent SQLite memory.
Shared capabilities include a skill registry, Composio dynamic tool routing, local summarization and transformation through Ollama, context files, launchd automation, macOS notifications, and runtime health from a menubar icon.
See CAPABILITIES.md for the shipped feature matrix and current rough edges.
flowchart LR
User[User] --> Surface[macOS surfaces]
Surface --> Voice[Voice mode]
Surface --> Screen[Screen mode]
Surface --> Text[Text mode]
Surface --> Briefing[Briefing mode]
Voice --> Brain[Shared brain loop]
Text --> Brain
Briefing --> Brain
Screen --> Store[(Local SQLite + transcripts)]
Screen --> Brain
Brain --> Skills[Skill registry]
Brain --> Gemini[Gemini API]
Brain --> Memory[(SQLite + FTS5 memory)]
Skills --> Native[Native skills]
Skills --> Composio[Composio tools]
Skills --> Ollama[Ollama local helpers]
Briefing --> Discord[Discord]
Text --> Discord
Screen --> Discord
Brain --> History[(~/.donna history)]
Layer map:
surface/ pynput hotkey + rumps menubar
voice/ sounddevice mic capture, faster-whisper STT, macOS say TTS
screen/ capture + Apple Vision OCR + Ollama describe + cleanup + summarize
text/ discord.py DM listener + shared brain turns + Discord chunking
brain/ provider protocol, Gemini provider, conversation, dispatcher
skills/ auto-discovering registry + native skills + Composio adapter
memory/ SQLite + FTS5 storage and retrieval
runtime/ DonnaHost orchestration and per-mode health
Requires macOS and Python 3.11+.
./setup.shThis creates a venv at ~/.donna/venv/, installs Donna, and places a wrapper at /opt/homebrew/bin/donna so it works from any directory.
For development:
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"Then configure and verify:
donna setup
donna doctordonna setup asks for your Discord bot token, Gemini API key, channel IDs, output choices, briefing interval, and optional email credentials. It creates ~/.donna/.env, ~/.donna/config.toml, and context templates.
Full setup for every mode is in SETUP.md.
donna run # all enabled modes
donna run --voice --no-screen --no-text # voice only
donna run --text --no-voice --no-screen # text only
donna brief --dry-run # preview a briefing without posting
donna brief # run a briefing and dispatch it
donna brief --since 24h # review a specific time window
donna screen summarize # batch summarize unsummarized screen transcripts
donna status # show config and state summary
donna doctor # diagnose setup, Discord, and GeminiDaemon commands:
donna daemon install # install and start the launchd daemon
donna daemon status # show if running, PID, pending jobs
donna daemon logs # tail the daemon log
donna daemon uninstall # stop and remove the daemonHistory and memory:
donna history # last 10 briefing runs
donna history --all # all briefing runs
donna raw 1 # raw messages + briefing for the latest run
donna memory list # inspect text-mode memory
donna memory search "query" # search saved memoriesDonna keeps its personal runtime data under ~/.donna/:
~/.donna/
.env # secrets: Discord, Gemini, email
config.toml # mode configuration
context/*.md # persona, work, goals, style, interests
history/ # briefing provenance
voice.log # runtime log
screen.db # screen transcripts and summaries
screen/shots/ # in-flight PNGs, cleaned after processing
screen/transcripts/ # retained screen transcript markdown
memory.db # text-mode memory
composio_skills_cache.json # cached tool catalog and schemas
Nothing leaves the machine except outbound calls required by the mode you enabled.
Donna reads a Discord #ideas channel, turns new messages into a structured briefing, and posts to #briefings or email. It supports dry runs, custom time windows, saved history, raw replay, launchd scheduling, and macOS notifications for urgent items.
Personal context comes from ~/.donna/context/*.md:
persona.md: who you are, background, how you thinkwork.md: current projects and recent shipped workinterests.md: recurring themesgoals.md: this week, month, and quarterstyle.md: briefing tone and notification sensitivity
Run donna context to see token counts per file.
Voice mode gives Donna a menubar-hosted push-to-talk surface. Hold Option-Space, speak, release, and Donna runs the shared brain loop with local STT and macOS TTS.
The voice brain can answer general questions, open apps and URLs, call registered skills, route Composio tools, summarize or transform local files with Ollama helpers, and use your configured personal context.
Screen mode captures on an interval, filters sensitive windows before capture, performs Apple Vision OCR, redacts likely secrets after OCR, asks a local Ollama vision model for scene descriptions, stores transcripts in SQLite, and summarizes unsummarized batches on demand.
The summarizer can post batches to Discord and marks each transcript as summarized, so retries only pick up remaining work.
Text mode lets you DM Donna's Discord bot. It uses the same brain and skill registry as voice mode, but replies in Discord-friendly markdown and remembers explicit facts through SQLite + FTS5.
Only the configured owner ID can talk to the bot. Other users, bot messages, non-DM channels, empty messages, and unsupported attachments are dropped or handled cleanly.
Donna keeps the active tool catalog small so model calls stay useful:
- Heavy-use tools: frequently used Composio slugs, such as Gmail send and read actions, are registered directly.
- Composio meta-tools:
composio_search_tools,composio_execute, andcomposio_get_tool_schemalet the brain find and call less common tools dynamically. - Native and memory skills:
ask,open_app,self_aware,read_file,local_summarize,local_transform,memory_save, andmemory_search.
Add a new native skill by dropping a file in src/donna/skills/<domain>/<name>.py that subclasses Skill. The registry's discover() method picks it up automatically.
Invalid Discord bot token: Check DISCORD_BOT_TOKEN in ~/.donna/.env.
Donna cannot access this channel: Check bot permissions and channel IDs.
Gemini rejected the request: Check GEMINI_API_KEY, the configured model, and billing in Google AI Studio.
No new ideas to brief: No messages were found after the saved watermark. Use donna brief --since 24h --dry-run to review a time window without touching state.
Bot drafts an email but says "I can't send drafts": Composio toolkits over 30 tools used to silently truncate the catalog, dropping gmail_send_email and gmail_send_draft. Reinstall if you're on an older build.
Bot drafted or created something but failed with 403 PERMISSION_DENIED: Composio's OAuth grant for that toolkit does not include the scope needed for the action. Re-link the toolkit with composio link gmail and grant the additional permission in the browser flow.
See SETUP.md#troubleshooting for the full list.
pytest tests/ -v
ruff check src/ tests/
ruff format src/ tests/Already applied on GitHub:
- Description:
Local-first macOS AI assistant with voice, screen, text, memory, and Discord integrations. - Topics:
ai-assistant,agents,macos,python,local-first,ocr,discord,sqlite,llm,automation
Remaining public-facing assets:
- Homepage: project page or demo video URL
- README visual: replace the placeholder note near the top with a short GIF or screenshot