macLLM is a local AI agent for macOS, launched via a hotkey. It uses tools to answer questions, search the web, and work with your files. For example:
- "Can a Cessna C172S fly over Mt Kilimanjaro?" — searches the web and reasons on facts
- "Make a table of my families passport numbers" — searches your indexed notes or files, e.g. Obsidian
- "Check @~/Documents/proposal.md vs. @company.com/proposal.html — reads and processes files and URLs
- "/emojis Crazy drunken ski party" — picks relevant emojis
- "Summarize @clipboard and append to my note @~/Notes/skiholiday2026.md"
macLLM is:
- Open source (Apache 2.0)
- Very easily extensible. It's written in 100% Python and highly modular
- Has a native macOS Cocoa UI written in Python (via PyObjC)
- Agentic — uses smolagents with tool calling
- Model-agnostic via LiteLLM (supports OpenAI, Gemini, Anthropic, and more)
macLLM uses the uv package manager. Install it first, then run:
uv run -m macllmuv handles all dependencies automatically.
macLLM requires API keys for the LLM you want to use. Right now supported providers are:
- Google - Gemini flash is default
- OpenAI - good for complex tasks
- Inception Labs - ultra fast diffusion models)
Create a
.envfile in the project root or export environment variables:
| Variable | Required for | Provider |
|---|---|---|
GEMINI_API_KEY |
Default (/normal) |
Google Gemini |
INCEPTION_API_KEY |
/fast speed |
Inception Labs (Mercury) |
OPENAI_API_KEY |
/slow / /think speed |
OpenAI (GPT-5) |
BRAVE_API_KEY |
Web search tool | Brave Search |
At minimum, set GEMINI_API_KEY to use the default model. Add BRAVE_API_KEY to enable web search.
Press the hotkey (default: ⌥ Space / option-space) to open the window, then type a query:
Capital of France?
After a moment you get the reply. From there you can:
- Press Escape to close the window (the hotkey also toggles it).
- Press ⬆ to browse the reply, then ⌘C to copy it.
- Type a follow-up query — macLLM keeps conversation context.
- Press ⌘N to start a new conversation (clears context).
macLLM is an AI agent, not just a chat interface. Each query is handled by an agent that can reason through multi-step problems and call tools autonomously. The agent decides which tools to use based on your query — you don't need to tell it explicitly.
For example, asking "Can a Cessna C172 fly over Mt Kilimanjaro?" will cause the agent to look up the aircraft's service ceiling and the mountain's elevation, then reason about the answer.
The agent has access to the following tools:
| Tool | Description |
|---|---|
| web_search | Searches the web via Brave Search. The agent can issue multiple queries per request. |
| search_notes | Semantic search across your indexed notes. |
| read_note | Reads the full content of an indexed note. |
| note_append | Appends text to an existing note. |
| note_create | Creates a new note with the given content. |
| note_modify | Replaces the content of an existing note (with automatic backup). |
| note_move | Moves or renames a note within indexed folders. |
| note_delete | Deletes a note (with automatic backup). |
| note_resolve_path | Resolves a mount-relative note path to its absolute filesystem path. |
| list_folder | Lists notes and subfolders in a specific folder. |
| find_folder | Searches for folders by name (case-insensitive substring) across all mounts. |
| view_folder_structure | Shows the full folder tree of all indexed mounts. |
| folder_create | Creates a new subfolder inside an indexed directory. |
| folder_delete | Deletes a subfolder and its contents (with automatic backup). |
| get_current_time | Returns the current date and time. |
Tools are called automatically by the agent. While the agent is working, the UI shows its current plan and tool calls in the status bar.
macLLM can index folders of notes (e.g. Obsidian vaults) and search them semantically. When you ask something like "check my notes for..." the agent uses search_notes to find relevant notes and read_note to read them.
To set up indexing, add named mount points under [index_dirs] in ~/.config/macllm/config.toml:
[index_dirs]
Notes = "~/Notes"
Work = "~/Work/Docs"Each entry maps a logical mount name to a directory. The agent sees short mount-relative paths like Notes/todo.md instead of full absolute paths.
This recursively indexes all .txt and .md files. The index rebuilds automatically every 5 minutes, or you can type /reindex to trigger it manually.
When typing @ followed by 3+ characters, autocomplete suggests matching notes from the index. Selecting one inserts it as context for the conversation.
When the agent needs current information, it uses the web_search tool backed by the Brave Search API. This happens automatically — just ask a question that requires up-to-date information, like opening hours, recent events, or current statistics.
Requires BRAVE_API_KEY to be set.
Agents can execute shell commands via the run_command tool, restricted by three layers:
- Command whitelist — only pre-approved executables (e.g.
ls,git,grep) run without asking. Unknown commands show an inline prompt: [R]un once, [D]eny, or [A]lways allow. - Directory grants — commands can only read/write directories the user has mentioned with
@(e.g.@~/docs/notes) or configured as defaults. Shortcuts@home,@desktop,@downloads,@documentsare available. - Kernel sandbox — every command runs under macOS Seatbelt (
sandbox-exec), enforced at the kernel level. Even if a command tries to escape, the OS blocks it.~/.ssh,~/.aws, and~/.gnupgare always denied.
The whitelist and default directories are configured in ~/.config/macllm/config.toml under [shell].
macLLM can access your local macOS calendars via EventKit. Ask it to find events, create meetings, check for conflicts, or find free time slots. It handles timezone conversions automatically — just say "schedule a meeting in Frankfurt at 5pm local time" and it figures out the rest.
macLLM can also work with your local Things database. It can list and search your to-dos and projects, inspect items by ID, create new to-dos and projects, move them, and mark them complete or canceled. Reads come from the local Things database, while writes go through the official Things URL scheme.
To use write actions, enable Things URLs in Things settings so an auth token is present.
Tags start with @ and attach external data as context for the conversation:
| Tag | Description |
|---|---|
@clipboard |
Current clipboard content (text or image) |
@window |
Screenshot of a desktop window (click to select) |
@selection |
Screenshot of a selected screen area |
@<path> |
Any file — path must start with / or ~ |
@<url> |
Web page content — must start with http:// or https:// |
Tags can be used inline: "translate @clipboard into French" or "summarize the slide @window".
Context items persist for the entire conversation and are shown as pills in the top bar. Starting a new conversation (⌘N) clears them.
Quoted forms like @"~/My Notes/file.md" are supported for paths with spaces.
Speed levels select different models for the tradeoff between speed and capability:
| Command | Speed | Model |
|---|---|---|
| (default) | Normal | gemini/gemini-3-flash-preview (Gemini) |
/fast |
Fast | openai/mercury (Inception Labs) |
/slow or /think |
Slow | gpt-5 (OpenAI) |
There are two ways to set the speed:
- In-line command: Prefix your query, e.g.
/slow Explain quantum entanglement in detail. - Keyboard shortcut: Press ⌘1 (Fast), ⌘2 (Normal), or ⌘3 (Slow/Think) at any time. This changes the speed for all subsequent queries in the conversation.
The current speed level and model are shown in the top-right corner of the window.
Commands that start with / come from two places:
- Skills — If your input begins with
/nameandnamematches a loaded skill, the skill body replaces that prefix. Any text after the first space is appended as anARGUMENTS:block. Skills are markdown files under directories listed inskills_dirsinconfig/config.toml(defaults:config/skillsin the repo and~/.config/macllm/skills). The samenamein a later directory overrides an earlier one. - Tag plugins — After skill expansion, the text is scanned for
@…and/…tokens. Plugins own specific prefixes (e.g. speed and reindex). They rewrite the prompt and may change speed, attach context, or run side effects.
Example skill expansion:
/fix My Canadian Moose is Braun.
expands to instructions that correct spelling and grammar only, so the model can reply with corrected text.
These ship in config/skills/shortcuts.md:
| Command | Description |
|---|---|
/fix |
Fix spelling and grammar only; reply with corrected text |
/emoji |
Suggest one relevant emoji |
/emojis |
Suggest several relevant emojis |
| Command | Description |
|---|---|
/reload |
Reload merged config and skill files; may trigger index refresh |
/fast |
Fast model tier (see Speed Levels) |
/slow / /think |
Slow / thinking tier |
/reindex |
Request a rebuild of the file index |
Add or edit *.md skill files under ~/.config/macllm/skills/ (or another path you add to skills_dirs), then use /reload or restart.
Both / and @ use an autocomplete popup:
- Typing
/suggests skill commands (including/reload) and plugin-registered slash prefixes. - Typing
@suggests tag prefixes and dynamic items such as indexed files. - Enter inserts the selection as a pill; Tab inserts as editable raw text.
- macLLM maintains a full conversation history in the main text area.
- Context attached via tags persists for the current conversation.
- ⌘N starts a new conversation and clears context.
- Press ⬆ when the cursor is on the first line to browse previous messages.
- ⌘C copies the highlighted message; Return inserts it back for editing; Escape exits browsing.
uv run -m macllm [options]
| Option | Description |
|---|---|
--debug |
Enable debug logging to the terminal |
--debuglitellm |
Enable verbose LiteLLM debug logging |
--version |
Print version and exit |
--show-window |
Open the window immediately on startup |
--query <text> |
Auto-submit a query (implies --show-window) |
--screenshot <path> |
After --query completes, capture the window to the given path and exit |
Apache 2.0
