clipmem
A searchable, local-only clipboard history for macOS, with a JSON-first CLI designed so agents — OpenClaw and others — can recall things you've copied.
clipmem watches the macOS system clipboard (NSPasteboard), archives every observed state into a local SQLite database, and exposes retrieval, restore, deletion, and policy commands (recall, recent, timeline, search, get, restore, export, forget, purge, settings) that return either human-readable text or structured JSON.
Contents
- Requirements
- Install
- Quick start
- Run in the background (LaunchAgent)
- Recalling and searching
- Restore, Deletion, and Policy
- Output formats
- Using with OpenClaw
- Privacy
- Uninstall / Full cleanup
- Limitations
- How it works
- Native macOS menu bar app
- Development
- License
Requirements
- macOS. Clipboard capture uses
NSPasteboardviaobjc2/objc2-app-kitand is gated behindcfg(target_os = "macos"). The database and search layers compile on other Unixes for development, but capture will not. - Apple Silicon for the prebuilt Homebrew package (target
aarch64-apple-darwin). Intel Macs are supported viacargo install. - Rust 1.87+ for building from source (
rust-versioninCargo.toml).
Install
Homebrew CLI only (Apple Silicon):
Homebrew CLI + menu bar app (Apple Silicon):
The cask depends on the CLI formula and runs clipmem setup after installation so background capture starts immediately.
Cargo (Apple Silicon or Intel):
Build from source:
# or install the current checkout into ~/.local/bin
The CLI-only Homebrew, Cargo, and source paths produce the same clipmem binary.
Quick start
Initialize the archive and start background capture:
Check service state or run the watcher in the foreground if you want to debug interactively:
Recall what you copied, or list recent items:
Check SQLite / FTS5 diagnostics if something looks off:
Run in the background
clipmem setup is the canonical CLI onboarding path. It performs one foreground capture to seed the archive, then starts background capture using the most natural provider for the active install. The Homebrew cask for the menu bar app runs this setup automatically after installation.
The current Homebrew formula installs the clipmem binary but does not expose a Homebrew service stanza, so clipmem setup and clipmem service start manage a direct per-user LaunchAgent (io.openclaw.clipmem.watch) even for Homebrew installs. If a future formula adds a service stanza, the same commands will prefer brew services automatically.
Common service commands:
The compatibility wrappers ./scripts/install_launchagent.sh and ./scripts/uninstall_launchagent.sh still exist, but they now delegate to clipmem setup and clipmem service uninstall.
For a full wipe including the database, see Uninstall / Full cleanup.
Recalling and searching
Start with the command that returns the least structure needed for the job:
clipmem recall— best-first answer plus a few alternatives. The primary agent-facing retrieval command.clipmem recent— recent unique clipboard states, deduplicated by snapshot.clipmem timeline— chronological capture events, including repeated copies of the same content.clipmem search— direct lexical matching over stored text.clipmem get <snapshot-id>— nested item/representation detail for a single snapshot.clipmem restore <snapshot-id>— restore the full stored snapshot back onto the clipboard.clipmem export <snapshot-id> --item <n> --uti <uti> --out <path> [--force]— raw bytes for binary payloads.clipmem forget <snapshot-id>— hard-delete one snapshot and its capture history.clipmem purge --older-than <duration> [--dry-run]— prune old snapshots bylast_observed_at.clipmem settings ...— inspect or change persistent pause / retention / ignore-list policy.clipmem settings api-key-filter on— skip clipboard snapshots that look like API keys or tokens.
Common recipes
# Best-first answer
# Recent unique items from one app
# Paginated chronological history
# Lexical search, forcing a mode
# Detail + raw bytes
# Replace an existing regular file explicitly
# Deletion and policy
Shared retrieval filters
search, recent, timeline, and recall all accept the same filters. get and export accept them as guards against the explicitly targeted snapshot.
Time:
--since <RFC3339>— captures at or after this timestamp (e.g.2026-04-16T09:00:00Z).--until <RFC3339>— captures at or before this timestamp.--hours <N>— last N hours, unless--sinceis also provided (then--sincewins).
Source:
--app <name>— case-insensitive substring match on the recorded frontmost app name.--bundle-id <id>— case-insensitive exact match on bundle identifier, e.g.com.apple.Safari.
Content shape:
--kind text|html|rtf|url|file|image|pdf|binary|other(filemeans file URLs;othermeans mixed or empty snapshots).--has-text,--has-url,--has-file-url,--has-image,--has-pdf— presence flags are additive with AND semantics.
Size:
--min-bytes <N>/--max-bytes <N>.
Pagination
Every list command accepts --limit (bounded 1–250, default 10) plus an opaque --cursor returned as next_cursor in a prior response:
Cursors are opaque and tied to the active query, mode, and filters.
Restore, Deletion, and Policy
clipmem stores the full per-item representation set, not just best text. That lets restore put a stored snapshot back onto the macOS clipboard as a whole snapshot instead of approximating it as plain text:
You can also remove data without wiping the whole archive:
Persistent capture policy lives in SQLite and is managed through clipmem settings:
Ignore matching is exact, case-insensitive bundle-id matching. Retention ages snapshots by last_observed_at, not their original insertion time. The API-key filter is opt-in and skips the entire snapshot before any preview text, search text, or blob payloads are written, but it is heuristic and tuned to avoid false positives rather than catch every possible secret.
Search modes
search and recall accept --mode auto|fts|literal. Auto mode picks FTS or literal per query — you rarely need to override. It prefers literal matching for URLs, paths, bundle ids, dotted identifiers, and shell-like fragments (more reliable for punctuation-heavy clipboard data); plain-text queries can still use FTS first.
recall extras
On top of the shared retrieval filters, recall supports:
--format md|json|toon(default:md)--limit— ranked candidates to consider (default 5)--full— expand the best candidate text instead of the compact form--quote— force quoted best-text output--min-score <0.0-1.0>— minimum normalized match score before a query stands on its own--prefer-recent— bias ranking toward recency--prefer-app <name>— bias toward matching app or bundle id--hours <N>— window for the recent fallback when a query is weak
Output formats
Human by default, machine-readable on request:
# Human-readable terminal output (default for search/recent/timeline/get)
# Machine-readable JSON for scripts and agents
Supported formats, by command:
search,recent,timeline,get:text(default),json,jsonl,md,toon(nottoonforget, which returns nested detail).recall:md(default),json,toon.restore,forget, andpurge: text summary by default,jsonwhen requested.settings show,settings ignore list, andservice status:textby default,jsonwhen requested.capture-onceanddoctor:--jsononly (alias for structured output).export: writes raw bytes to--out; creates a new file by default, accepts--forceonly for replacing an existing regular file, and can also return a JSON write report when requested.
--json on search, recent, timeline, get, restore, forget, purge, export, capture-once, and doctor is a compatibility alias for --format json.
Action JSON output is designed for native frontends and scripts:
restore --format jsonreturns whether the snapshot was restored, the restored item count, the restored representation count, and the snapshot id.forget --format jsonreturns the deleted snapshot id, event count, item count, representation count, and byte count.purge --format jsonreturns age cutoff, dry-run state, and deleted-or-would-delete counts.export --format jsonreturns destination path, byte count, UTI, item index, snapshot id, and representation hash after the file is written.
--format toon is a compact skim view, not a near-lossless mirror of the JSON payload. TOON keeps scalar columns that are easy for agents and humans to scan quickly; if you need URLs, file paths, text fragments, representation UTIs, or other richer fields, use --format json or clipmem get.
JSON envelope
search, recent, timeline, and recall return a stable top-level envelope:
schema_versioncommandgenerated_atapplied_filterstruncatednext_cursorresults
Flattened text fields
get --format json and retrieval rows surface flat text fields so agents don't have to walk the nested representation tree:
best_text,best_text_utitext_fragmentsurls,file_pathshtml_text,rtf_texttext_summary
search and recall additionally include:
why_matchedmatched_fields
The full nested items[].representations[] structure is still present on get for deep inspection and export workflows.
TOON skim output
TOON is intentionally narrower than JSON:
search/recentrows keep scalar identifiers, timestamps, app info,display_text, counts, bytes, score, andwhy_matched.timelinerows keep scalar event metadata plusdisplay_text.recallkeeps scalar best-match metadata and candidate rows, but does not inline heavyweight fields as JSON strings.
Use --format json when you need urls, file_paths, text_fragments, best_text_uti, html_text, rtf_text, matched_fields, or any nested representation detail.
Script-friendly by design
- stdout contains only the requested command output
- stderr contains diagnostics only
- no interactive prompts anywhere
- list commands use bounded defaults and opaque cursor pagination
Exit codes:
0— success2— invalid args3— not found4— unsupported format5— database error6— platform error1— uncategorized runtime failure
Using with OpenClaw
Install the packaged skill via the binary rather than by copying files:
By default this installs into the active OpenClaw workspace:
~/.openclaw/workspace/skills/clipboard-memory
The workspace root is resolved from openclaw config get agents.defaults.workspace, falling back to ~/.openclaw/workspace.
To install into the shared OpenClaw skills directory instead:
# writes to ~/.openclaw/skills/clipboard-memory
Or to a specific destination:
Other useful commands:
What the skill ships
SKILL.md
references/commands.md
references/json-schema.md
references/examples.md
references/setup-check.md
references/troubleshooting.md
scripts/check-setup.sh
The packaged skill points OpenClaw at the JSON-first retrieval commands:
clipmem recall "<query>" --format jsonclipmem recall --prefer-recent --hours 24 --format jsonclipmem timeline ... --format jsonwhen chronology mattersclipmem search ... --format jsonwhen direct lexical matching mattersclipmem get <snapshot-id> --format jsonwhen deeper nested detail is needed
Troubleshooting
OpenClaw must be able to run clipmem from its own environment, not just from your interactive shell. If you installed clipmem into ~/.local/bin, make sure that directory is on the PATH seen by OpenClaw. If sandboxing is active, the binary may also need to be available inside the sandbox image or container.
Start with:
For empty results, sandbox visibility, or binary-only (image / PDF) snapshots where exact text isn't available, see extras/openclaw/clipboard-memory/references/troubleshooting.md.
Example OpenClaw prompts once installed
- "Find that ffmpeg command I copied yesterday."
- "Search my clipboard history for the SQL migration with WAL mode."
- "What was the URL I copied from Safari about objc2
NSPasteboard?" - "Show me the full clipboard entry for snapshot 128."
Skill packaging (reference)
skills/clipboard-memory/— the canonical cross-agent skill source thatskills.shdiscovers from the repo root.extras/openclaw/clipboard-memory/— the OpenClaw-native package installed byclipmem agents openclaw install-skill.extras/agent-skills/clipboard-memory/— a portable packaging mirror for generic agent-skill runtimes.
For compatibility, ./scripts/install_openclaw_skill.sh still exists as a thin wrapper around clipmem agents openclaw install-skill --shared --force.
Privacy
- All clipboard data stays on your machine.
clipmemmakes no network calls and sends no telemetry. - The database lives at
~/Library/Application Support/clipmem/clipmem.sqlite3. Logs are in the same directory underlogs/. - Permissions: directory
0700, database and logs0600— enforced at runtime insrc/db.rs::harden_path_permissionsand by the managed service setup flow. - The database is not encrypted. Rely on FileVault (or similar disk encryption) for at-rest protection.
- Blob payloads are stored so
clipmem restorecan faithfully rehydrate images, RTF, URLs, file URLs, PDFs, and other clipboard representations. - You can pause capture persistently with
clipmem settings pause on. - You can opt into a precision-biased secret guard with
clipmem settings api-key-filter on; matching snapshots are skipped entirely instead of being redacted. - You can exclude exact bundle ids with
clipmem settings ignore add <bundle-id>. - You can prune automatically with
clipmem settings retention <duration|forever>or manually withclipmem purge --older-than <duration>.
Uninstall / Full cleanup
# stop and remove the managed background service
# remove the OpenClaw skill (if installed)
# delete the database and logs
# remove the binary
||
# if installed via Homebrew
||
Limitations
- Text, HTML, URLs, file URLs, RTF, JSON, and XML are indexed when a reasonable text form is available. Images, PDFs, and opaque binary types are stored as blobs but are not OCR'd.
- The watcher polls
NSPasteboard.changeCounton a short interval. It is best-effort — if the clipboard changes multiple times within the poll window (400ms by default), intermediate states can be missed. - The frontmost app is recorded as a practical hint, not a guaranteed pasteboard owner.
- RTF and HTML text extraction is intentionally lightweight.
- Search is great for commands, code, URLs, notes, logs, and copied prose. It is not semantic search.
--mode autois the default; use--mode ftsfor strict FTS5 queries or--mode literalfor exact substring matching. clipmem get --format jsonomits raw blob bytes (flattened text fields are included). Useclipmem exportto recover binary payloads.clipmem restoreis macOS-only and writes the stored snapshot back to the live system clipboard.--format toonis only supported for skim output (search,recent,timeline,recall), not for nestedgetdetail. It intentionally omits heavyweight fields instead of embedding them as JSON blobs.
How it works
For each observed clipboard state, clipmem stores:
- the whole clipboard snapshot
- each pasteboard item inside that snapshot
- every representation type the item exposes (identified by UTI — Uniform Type Identifier, e.g.
public.png,public.url) - raw bytes for every representation
- decoded text when the representation is text-like, plus strict UTF-8/UTF-16 recovery for some byte-only payloads
- a searchable text projection that powers default retrieval
- the frontmost application at capture time as a best-effort source hint
Identical clipboard snapshots are deduplicated by SHA-256 fingerprint, so copying the same thing ten times does not create ten full blob copies — it creates one snapshot and ten capture_events.
Default search uses SQLite's built-in full-text search (FTS5) when that fits the query, and falls back to literal substring matching for wildcard-like input, invalid FTS syntax, or zero FTS hits.
Schema
snapshots— deduplicated clipboard statessnapshot_items— items inside a snapshotitem_representations— one row per item/type pair, with raw blob storagecapture_events— each time a snapshot was observedsnapshots_fts— FTS5 external-content index oversnapshots.search_text
Native macOS menu bar app
The repo includes a native SwiftUI menu bar frontend at macos/ClipmemMenuBar/. It is a Mac app, not Electron, Tauri, or a webview. The app keeps the Rust CLI as the source of truth by shelling out to clipmem asynchronously and decoding JSON responses from the same commands used by scripts and agents.
Install the signed app with Homebrew:
The cask depends on the clipmem formula, runs clipmem setup after installation, and enables launch at login by default. You can disable launch at login in the app's Settings window.
Build and launch the development app from the repo root:
The script runs cargo build, builds the app with xcodebuild, sets CLIPMEM_BINARY_PATH for the launched app, and opens the built .app from macos/ClipmemMenuBar/DerivedData.
You can also build or test the app directly:
The app uses explicit SwiftUI scenes:
MenuBarExtrafor compact status, setup/service actions, recent items, quick recall, settings, and quit.WindowGroup("History", id: "history")for the full browsing surface with sidebar modes, filters, result list, detail, and inspector actions.Window("Quick Recall", id: "quick-recall")for keyboard-first recall/search/recent/timeline access.Settingsfor binary path, database path, defaults, hotkey, ignored bundle ids, retention, pause, API-key filtering, and privacy controls.
Backend binary discovery order:
CLIPMEM_BINARY_PATH- user preference override in the app settings
- repo-local
target/debug/clipmem - repo-local
target/release/clipmem /opt/homebrew/bin/clipmem/usr/local/bin/clipmem~/.cargo/bin/clipmem~/.local/bin/clipmem
The app passes --db <path> to the backend when the database override setting is set. Otherwise it uses the CLI default database path.
The menu bar app self-excludes its own bundle id (io.openclaw.clipmem.menubar) on first launch when the database/settings layer is available. Ignore-list, pause, API-key filtering, and retention are still the existing SQLite-backed clipmem settings policy, so there is no separate Swift-side config database to keep in sync.
Known rough edges in the first native frontend pass:
- Global hotkey support is Option-Shift-V with a reset-to-default preference. Arbitrary hotkey recording is not implemented yet.
- Image representations can be exported for preview/action workflows, but PDFs and opaque binaries are shown through metadata and export actions rather than inline rendering.
- The app restores clipboard snapshots but does not auto-paste into the previous application.
- Search is lexical/rule-based and the UI intentionally does not describe it as semantic or AI search.
Development
Project layout:
src/— Rust source. Capture is gated behindcfg(target_os = "macos"); database, search, and tests compile cross-platform.macos/ClipmemMenuBar/— native SwiftUI menu bar app and Swift Testing suite.extras/launchd/— LaunchAgent plist template.skills/clipboard-memory/— canonical public skill content for repo-based installers.extras/openclaw/clipboard-memory/— packaged OpenClaw-native skill content.extras/agent-skills/clipboard-memory/— portable skill package mirror for non-OpenClaw runtimes.scripts/install_launchagent.sh/scripts/uninstall_launchagent.sh— compatibility wrappers aroundclipmem setupandclipmem service uninstall.scripts/install_openclaw_skill.sh— compatibility wrapper aroundclipmem agents openclaw install-skill.scripts/build_and_run_menubar.sh— builds Rust, builds the native app, and launches it against the repo-local backend binary.
Build and test (Rust 1.87+):
The code is written to make extension straightforward — export commands, embeddings, OCR, richer source-app heuristics, or fuller HTML parsing all fit the existing shape.
See RELEASING.md for the release workflow.
License
clipmem is released under the MIT License. See LICENSE.