Local search across your files, repositories and AI agent sessions, built to give your agents the best context for the job.
- Find anything: press Control+Shift anywhere on your Mac and search your documents, folders, code and past agent sessions by meaning or by exact words.
- Superpowers for your agents: agents connect over MCP and get the few most relevant files and passages instead of grepping your disk, which saves time and tokens. Each agent is scoped to what it's working on (by default, the repo it runs in).
- Know who to ask: git blame shows who owns what and who has the most domain knowledge, plus what each teammate has been working on.
- Find knowledge gaps: areas where most of the code was written by people no longer active in the repo, how many active people still know it, and who to ask now.
Everything runs on your Mac. Text is embedded with Qwen3-Embedding-0.6B and stored in SQLite (with a trigram full-text index for exact matches) plus turbovec vector indexes. AI-written summaries and insights are optional (built-in Qwen 3.5 9B, LM Studio, Claude Code or Codex).
curl -fsSL https://pensieve.tools/install.sh | shOn an Apple Silicon Mac, the script downloads the latest Pensieve.dmg and copies the app to Applications. Then it installs the pensieve backend with the installer inside the app. You do not need git, the Xcode tools or Python. The app starts when you run the script in a terminal. When an agent runs it, the script installs only.
Then connect your agents:
pensieve integrate recommendedThis command finds the agents on your Mac (Claude Code, Codex, Cursor, Gemini CLI, Qwen Code, Windsurf, Claude Desktop and VS Code) and gives each one Pensieve as an MCP server.
Then set up Pensieve. Pensieve indexes nothing until you finish setup, also after a reset. When the app opens, it shows the steps:
- Read what Pensieve does.
- Choose how you use your Mac: software development, AI coding agents, documents and office work, or research and notes.
- Let Pensieve look for sources in the folders that those choices need. macOS can ask you to allow access to some folders. Pensieve reads no folder before this step.
- Choose what to index from what it found. Add folders or repos that it did not find.
- Choose whether Pensieve reads images, video and audio by what they show or say. These are off by default. Then Pensieve finds those files by name.
- Choose an engine for insights, or turn insights on later.
- Start indexing.
An agent can do the same setup in its chat. Until setup is done, every MCP connection also gets the setup_guide, setup_scan, setup_add_source and setup_start tools. setup_guide tells the agent which questions to ask, in order. From a terminal:
pensieve setup # list the agent sessions, folders, repos and apps found, with their sizes
pensieve setup recommended # index the ones marked [x]
pensieve setup agents/claude repos//Users/me/code/app # or exactly these
pensieve setup recommended images audio # and read images and sound by contentThe first index uses a lot of energy: the embedding model runs until it finishes, about 500 code files a minute on Apple Silicon. Settings → Sources shows what is syncing now.
On Linux and Intel Macs, the script installs uv and the backend from source. Then it starts the backend and opens the Map in your browser.
Or by hand:
uv tool install git+https://github.com/TedHaley/pensieve # the backend: `pensieve`
git clone https://github.com/TedHaley/pensieve && pensieve/mac/build.sh --install # the Mac app (needs the Xcode tools)On first run Pensieve downloads the embedding model (~1.2 GB), then indexes in the background. Search and the map fill in as it goes.
Press Control+Shift (tap both, release) to open it, or Control+Shift+Space. The tap needs Accessibility permission (System Settings → Privacy & Security → Accessibility → Pensieve); the Space combo works without it.
To open Pensieve with ⌘Space instead of Spotlight, choose ⌘Space in the menu bar under Search Shortcut, or in Settings. Pensieve turns off the Spotlight shortcut while it runs. To give ⌘Space back to Spotlight, choose another shortcut. When Pensieve quits, Spotlight gets ⌘Space back too.
Pensieve starts at login by default. To stop this, turn off Launch at Login in the menu bar.
| Query | Finds |
|---|---|
pricing model |
anything about that, by meaning |
"rate limit" |
items containing those exact words (case-insensitive) |
"rate limit" retries |
contains the phrase, ranked by the meaning of the rest |
-draft / -"old notes" |
excludes a word or phrase |
kind:file |
documents and code (kind:doc documents only, kind:code code only) |
kind:session |
agent sessions only |
kind:folder invoice export |
folders whose path and contents match, e.g. src/billing/invoice_export; a repo is a folder too |
kind:repo |
repositories (on its own: all of them) |
kind:person payment retries |
people who wrote the code about it; a plain search for a name also lists the person first |
by:sam terraform |
only what that person wrote (code by git blame, documents by author) |
ext:pdf |
one file type |
in:my-app |
path or repo contains this |
A plain search also puts a folder first when its path names every word (person build). Under an empty field, Options lists all of these.
The panel also finds apps, System Settings panes, the individual settings in each pane, and quick toggles (Dark Mode, Wi‑Fi, Mute, Lock Screen, Sleep). These results come from the Mac app only, so they never show on the map. A strong name match shows first as the Top Hit, and Tab completes its name. On a toggle, ⌘Return opens its pane in System Settings and does not change the setting. The panel ranks the apps and settings that you open frequently higher.
Under On This Mac, the panel also finds documents and folders anywhere on the Mac by name. These matches come from the Spotlight index, so Pensieve does not index them and they do not show on the map. The list leaves out code: source files and files in a git repo are found through the code index, which also searches their content. A file that Pensieve already indexes shows only in its usual group.
The panel calculates and converts units: 2^10, 15% of 240, 80 + 15%, sqrt(2), 3 ft in cm, 72 f to c, 2 cups in ml. Return copies the result.
Return opens · ⌘Return reveals in Finder · ⌥Return shows it on the map · ⌘C copies the path · ⌘O opens the Map · Esc closes.
Code results open at the matching line in your editor if you set editor to vscode, cursor or zed.
| Map | Documents, code and agent sessions on one 3D map, laid out by meaning. Filter by type (Documents · Code · Agent sessions) or by folder; repos show as folders with a code badge. Color by type, folder, author, recency or topic. Inside a repo: git blame per line, owners, who knows about X, overlaps, and knowledge gaps. |
| Insights | Activity and topics, AI themes and open threads per repo or folder; your footprint in each repo, unexplored areas next to your work and who owns them, what each teammate shipped in the last 90 days, and knowledge gaps. |
| Settings | Everything below, plus copy-paste commands for connecting agents. |
Agents are only as good as the context they're given, so each agent connection works within a scope: a slice of the index, such as the repo it's working in.
pensieve integrate recommended connects every agent on your Mac. For Claude Code, it runs the first command below. --scope user makes Pensieve available in every project. Without it, claude mcp add connects only the folder you run it in.
claude mcp add --scope user pensieve -- pensieve mcp # stdio; scope "auto" = the repo the agent was started in
claude mcp add pensieve -- pensieve mcp --scope payments
claude mcp add --transport http pensieve "http://127.0.0.1:8765/mcp?scope=payments"- auto (the default for
pensieve mcp) uses the agent's working directory: the first named scope that includes that repo, otherwise just that repo with its worktrees and the agent sessions run in it. Outside any repo it means everything. In a repo that Pensieve does not index, search tells the agent to use grep. It does not return code from your other repos. HTTP connections can't see the agent's folder, so name a scope there (no?scope= everything). - Named scopes are lists of sources, e.g.
payments = repos/~/code/billing + repos/~/code/ledger + files/~/Documents/specs. Create them in Settings → Scopes or with thesave_scopetool. Everything stays in one index; a scope only filters, so nothing is embedded twice. - Every tool respects the scope: search, read, similar, who_knows, list_repos, team, unexplored. An item outside it is refused.
- The search panel and the Map use
default_scope(empty = everything).
For agents that only speak stdio, the JSON config is {"mcpServers": {"pensieve": {"command": "pensieve", "args": ["mcp"]}}}. pensieve mcp starts the backend if it isn't running.
Tools: search, read, similar, overview, who_knows, knowledge_gaps, list_repos, team, unexplored, open, current_scope, index_path, report_feedback, stale. Tool descriptions are sent to the agent on every turn, so these fourteen are all an agent sees by default. stale lists git worktrees with no commit and no agent session in 14 days, so that your agent can offer to clean them up. Each has a verdict: "remove" (clean, and its commits are merged or pushed, with the command) or "review" (it has work that exists nowhere else). Pensieve only reports: the agent asks you before it removes anything, and the index drops a removed worktree on its next sweep. With the session-start hook, a session in a repo with stale worktrees gets a one-line hint to offer this at a natural pause. overview shows where things are in a repo: its size, languages, folders two levels deep, the README's start, its dependencies and its largest files, then its topics (files grouped by meaning) with their files and authors. It reads the index, not the files. index_path indexes a folder when you ask the agent to: a git repo, a folder of repos, a folder of agent sessions, or documents. Pensieve finds out which kind it is. Connect with ?tools=all (HTTP) or pensieve mcp --all-tools to add the ones that manage Pensieve itself: show_search, show_visualizer, status, list_scopes, save_scope, delete_scope, list_sources, set_source, add_folder, remove_folder, get_settings, update_settings, reindex. Until setup is done, every connection also gets the setup tools: setup_guide, setup_scan, setup_add_source, setup_start.
Agents grep by habit, even with Pensieve connected. Optional add-ons fix that (Settings → Agents → Agent add-ons, or the command line):
pensieve integrate # what's available and what's installed
pensieve integrate recommended # for each agent on this Mac: the MCP server, the instructions block and
# the pensieve-search skill; for Claude Code also both hooks
pensieve integrate cursor-mcp codex # only these
pensieve integrate pensieve-teammates pensieve-plan
pensieve integrate claude --remove # removes exactly what was added- MCP server (
claude-mcp,codex-mcp,cursor-mcp,gemini-mcp,qwen-mcp,windsurf-mcp,claude-desktop-mcp,vscode-mcp): adds Pensieve to the agent's MCP config for every project. For Claude Code, it runsclaude mcp add --scope user. In other config files, Pensieve changes only its own entry. Restart an open agent to load the server. - Instructions block (
instructions.md): a short "search before you grep" section in~/.claude/CLAUDE.md,~/.codex/AGENTS.md,~/.gemini/GEMINI.mdor~/.qwen/QWEN.md, kept between<!-- pensieve:start -->markers. For other agents (Cursor, Windsurf, …), paste it into the agent's rules. - Skills go once into
~/.agents/skills, the user-level skills folder that Codex, Gemini CLI, Cursor, VS Code, Windsurf and other agents read. Claude Code and Qwen Code read only their own folder, so they get a link to that copy in~/.claude/skillsor~/.qwen/skills. Pensieve makes no folder for an agent that is not installed. If an agent's skills folder is a link to a folder that does not exist, Pensieve leaves it and tells you. pensieve-searchskill (recommended): query styles, when to fall back to grep, finding similar code before writing new code, recalling past sessions.pensieve-teammates: suggests who knows an area from git blame and drafts the question for you. It never messages anyone.pensieve-plan: plans for new code list prior art to reuse, earlier attempts, people to loop in and knowledge risks.claude-hook(recommended): aPostToolUsehook in~/.claude/settings.json. After each Edit or Write, it sends the changed path to Pensieve. The agent's next search then always finds the new code, even when the file system event comes late. The hook adds about 50 ms to each edit.claude-repo-check(recommended): aSessionStarthook. When a session starts in a repo that Pensieve does not index, Claude asks you once whether to index it. If you say yes, Claude runspensieve repo index. If you say no, Claude runspensieve repo skipand uses grep in that repo. Pensieve keeps the answer as the repo's switch in Settings → Sources, so Claude does not ask again. To change it, use that switch orpensieve repo index.claude-prompt-search: aUserPromptSubmithook. When you send a prompt in a repo that Pensieve indexes, Pensieve searches the prompt and adds its 5 best code matches to it, as thesearchtool shows them. Claude then often starts on the right file without a search of its own. The hook adds about 300 ms and 400 tokens to each prompt. Slash commands, prompts of 1 or 2 words and folders that Pensieve does not index get nothing.
bench/ measures it with Claude Code itself. Tasks are generated from your indexed code (a chunk is described in plain words, or by a name it defines, an error string from it, or a mix), and Claude Sonnet runs headless with and without Pensieve; the answer is the file. These are the 72 held-out tasks across four repos (504 to 34,847 files), each run twice, both setups in one batch, with Pensieve 0.5.6:
| Grep, Glob, Read | With Pensieve + instructions block | |
|---|---|---|
| Tasks solved | 123 / 144 (85%) | 131 / 144 (91%) |
| Tool calls per lookup | 3.44 | 1.97 (−43%) |
| Cost per lookup | $0.050 | $0.041 (−18%) |
| Tokens per lookup | 67k | 57k (−16%) |
| Time per lookup | 10.9 s | 8.6 s (−21%) |
Pensieve solved as many or more tasks and cost less in every repo: 15% less in the 504-file repo, 6–14% less in the 1,783 and 2,383-file repos, and 28% less in the 34,847-file monorepo, where it also needed 47% fewer tool calls. In 82 of the 144 runs the agent answered after a single search. Pensieve's search alone puts the right file first for 94% of names, 88% of error strings, 74% of name-and-description mixes and 58% of descriptions (a single grep: 54%, 92%, 0%, 0%).
Costs are Claude Code's reported API costs. They depend on how warm Claude's prompt cache is, so compare them only within one batch. Run it on your repos:
uv run python bench/agent_search.py gen --repo ~/code/myrepo -n 40 --tasks tasks-v2.jsonl
uv run python bench/exact_tasks.py # adds name / error-string / mixed queries
uv run python bench/agent_search.py find --repo all --tasks tasks-v2.jsonl --styles query,desc,mixed,ident,literal
uv run python bench/agent_search.py run --repo ~/code/myrepo --tasks tasks-v2.jsonl --arms grep,guidedEverything is grouped into Sources (Settings → Sources, or the list_sources / set_source MCP tools). You can switch off a whole category or a single item; switching a source off also removes what it had indexed.
| Category | Items | Default |
|---|---|---|
| AI agent sessions | Claude Code (~/.claude/projects, with its memory files), Codex (~/.codex/sessions), Qwen Code (~/.qwen/projects) |
on |
| Files & folders | ~/Documents, ~/Desktop, ~/Downloads, plus any folder you add. Content of text, Markdown, RTF, Word, PowerPoint, Excel, Pages, Keynote, Numbers, PDF, mail (.eml), subtitles, data files and code; images, video and audio by what they show or say (if you switch them on) or by name; other files by name; folders by name and what they contain. Each document's author comes from its metadata (PDF, Office, Spotlight) or its owner. |
on |
| Git repositories | Repos your agents worked in; repos found under your home folder (4 levels deep) and inside your folders; each repo on its own | on |
| Apps | Obsidian vaults; Apple Notes (exported to Markdown in ~/.pensieve, asks for permission) |
Obsidian on, Notes off |
Repos are indexed as they are on disk: committed, staged, modified and untracked files, with uncommitted lines credited to you in blame. Linked worktrees (including .claude/worktrees) are indexed too: files that differ from the main checkout show up tagged with their branch, and disappear when the worktree is removed. Clones of the same remote count as one repo. Repos your agents work in, and repos you add, are indexed at any size. A repo that only the sweep found, with more than 5,000 tracked files and no agent use, is listed in Sources for you to switch on instead of being indexed on its own.
Staying current: Pensieve watches your folders, repos, worktrees and agent transcripts (FSEvents). It indexes a change about half a second after the last save. A search indexes the changes that are waiting before it runs, so a search right after an edit finds the new text. Each code and document result is also compared with the file on disk:
- If the file was deleted, the result is removed.
- If the file changed, Pensieve indexes it again and repeats the search. If it cannot (for example, during a long first index), the result is marked as edited since indexed, and a code result gets the line where its text is now.
A session that is still being written updates every 30 seconds. A full sweep every 5 minutes catches anything missed.
Speed: a search takes about 0.1 to 0.2 seconds, also in a 35,000-file repo and while Pensieve indexes, because indexing pauses for a moment whenever a search runs. A first index runs as fast as the embedding model allows: git blame runs in parallel through a commit-graph that Pensieve keeps in ~/.pensieve/git (it never writes to your repo), and the database writes run on their own thread. On an Apple Silicon Mac, a 35,000-file repo takes a little over an hour.
The map shows only what Pensieve reads the content of. Each source has a map switch next to its on/off switch. A source that is off the map stays searchable. In Settings → Sources → File types, you choose for each type of file:
- Read content, Name only or Skip.
- Whether files of this type show on the map.
Images, video and audio can be read by what they show or say. Switch them on in Settings → Models → What the search model reads. They need the EmbeddingGemma 2 search model. When you switch one on, Pensieve downloads its part of the model and shows the progress. Until the download is done, those files are found by name.
| Kind | What Pensieve reads | Download | Speed on an M4 Max |
|---|---|---|---|
| Images | PNG, JPEG, GIF, WebP, BMP, TIFF and HEIC, in your folders and git repos | 335 MB | about 8 images a second |
| Video | What it shows, 32 seconds at a time. With audio on, also what is said in it. | 335 MB (the same as images) | about 6 minutes for an hour of video |
| Audio | What is said or heard, 30 seconds at a time | 611 MB | about 30 seconds for an hour of sound |
Each 30- or 32-second window of a video or a recording is a search hit with its time, for example meeting.m4a 12:30-13:00. The Mac app decodes video and sound with macOS (AVFoundation). A backend without the app needs PyAV (pip install av).
Other files that have no text are found by name only and never show on the map. Folders never show on the map.
Long documents are split into pieces of chunk_chars characters (1,500 by default). Pensieve reads at most max_chunks pieces of each document (2,000 by default, about 1,000 pages; 0 reads all). The Sources page shows a chart of how long your documents are, and how many are longer than the limit. When you change either setting, Pensieve reads your documents again.
Files that look like secrets (password-manager emergency kits, keys, .env, credentials) are never indexed; see exclude_files.
Everything Pensieve stores lives in ~/.pensieve. Delete that folder to start over.
Change them in the Map window (Settings), from an agent connected with --all-tools (update_settings), or in ~/.pensieve/settings.json.
| Setting | Default |
|---|---|
folders |
~/Documents, ~/Desktop, ~/Downloads (manage in Sources) |
disabled |
sources switched off, e.g. agents/codex, files/~/Downloads, repos/discover (manage in Sources) |
off_map |
sources that are searchable but not on the map, with the same ids as disabled (manage in Sources) |
file_types |
for each file type, index (content, name or off) and map (manage in Sources → File types) |
chunk_chars, max_chunks |
1500, 2000 (0 = no limit) |
sweep_roots, sweep_depth |
~, 4 |
max_repo_files |
5000 (found repos with no agent use only) |
repos |
extra repos, always indexed |
exclude |
folder names never descended into (node_modules, Library, …) |
exclude_files |
filename globs never indexed (secrets) |
scopes, default_scope |
named index slices for agents (manage in Scopes); the panel's scope |
appearance |
system (or light, dark) |
embed_model |
the search model for the index (manage in Settings → Models); empty: the index's own |
ai, builtin_model |
auto (not chosen yet), mlx-community/Qwen3.5-9B-MLX-4bit |
cli_command, cli_privacy |
the command for ai: cli, and cloud or local |
hotkey |
ctrl+shift (or a combo like cmd+shift+space) |
editor |
default, vscode, cursor or zed |
llm_url, llm_model |
http://localhost:1234/v1, qwen/qwen3.5-9b |
Pensieve uses two models. You choose both at setup, and change them in Settings → Models.
The search model turns text into vectors, so that search finds things by meaning. It always runs on your Mac.
| Model | Notes |
|---|---|
EmbeddingGemma 2 (google/embeddinggemma-2) |
The default for a new index. Its 270M-parameter text part runs on MLX. About twice as fast to index as Qwen3 and 25% smaller, with the same search quality in our tests (bench/STRATEGIES.md). Pensieve downloads only its text part: 550 MB the first time. |
Qwen3 Embedding 0.6B (Qwen/Qwen3-Embedding-0.6B) |
The model of every index made before 0.6.9. A 1.2 GB download the first time. |
An index keeps the model it was built with: an update does not change it. If you choose another model, Pensieve warns you first. Then it embeds the text it already holds with the new model, in the background. It does not read your files or run git blame again, and search keeps working with the old model. When the new vectors are ready, the backend restarts and uses them.
Search, maps, activity, keyword topics, your footprint in each repo and teammates' commits all work without AI. Summaries, topic names and the written insights need a model, and you choose which one writes them (Pensieve asks on first run; change it in Settings → Models or with update_settings {"ai": ...}):
| Engine | Where it runs | Notes |
|---|---|---|
Built-in: Qwen 3.5 9B (builtin) |
This Mac (MLX) | Apple Silicon, 16 GB memory. Downloads ~5 GB on first use, or reuses LM Studio's MLX copy if you have one. |
LM Studio / local server (server) |
This Mac | Any OpenAI-compatible server at llm_url running llm_model. |
Command line (cli) |
Where the tool runs | An AI command-line tool you already use, set in cli_command. Templates: Claude Code, Codex, Qwen Code, Gemini CLI and Ollama. Pensieve runs the command once per job, in an empty folder. The prompt goes on stdin, or in place of {prompt}; {out} is a file for the answer. The command must answer without using tools. No per-chunk summaries, to keep calls low. |
Off (none) |
Everything except the AI-written parts. |
pensieve # backend + Map in the browser (http://localhost:8765)
pensieve --no-open # backend only (what the Mac app runs)
pensieve mcp # MCP over stdio (--all-tools to include the settings tools)
pensieve integrate # agent add-ons: instructions block, skills and the edit hook
pensieve search "retry failed webhook delivery" --kind code # what an agent's search returns, scoped to this repoFlags: --port, --data (index location), --repos PATH… (extra repos for this run), --llm-url, --llm-model.
On Apple Silicon, the embedding model runs on MLX. On other machines, it runs on sentence-transformers and PyTorch (CUDA or CPU), which makes the install about 650 MB larger. The backend and the Map work on Linux too; the search panel app is macOS only.
Report a bug or an idea in Settings → Feedback, or on GitHub issues. Agents report too: when a Pensieve tool fails or a search misses code that the agent then finds another way, the agent calls the MCP tool report_feedback. Reports stay on your Mac, in ~/.pensieve/feedback.jsonl. Settings → Feedback lists them, and Send to GitHub opens a filled-in issue that you read before you post it.
git clone https://github.com/TedHaley/pensieve && cd pensieve
uv run pensieve # backend
mac/build.sh && open mac/build/Pensieve.appTo support another agent's transcripts, add a parser in pensieve/parsers.py and a line in SOURCES in pensieve/config.py.