clipmem
clipmem is a small Rust CLI for macOS that archives observed clipboard states into SQLite, builds a searchable local clipboard history, and gives you a command that OpenClaw can call whenever it needs to recall something you copied.
It captures the current NSPasteboard contents, stores every representation it can read for every pasteboard item, deduplicates identical clipboard snapshots by SHA-256 fingerprint, and records each observed state as its own event.
What it stores
For each observed clipboard state, clipmem stores:
- the whole clipboard snapshot
- each pasteboard item inside that snapshot
- every representation type exposed by that item
- 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 for recognized text-like representations that powers automatic FTS-or-literal lookup
- the frontmost application at capture time as a best-effort source hint
The database schema separates deduplicated snapshots from per-observation capture_events, so the same content copied ten times does not create ten full blob copies.
The local archive is treated as sensitive state: the tool and installer tighten archive, log, and LaunchAgent file permissions to the current user on platforms that support POSIX modes.
Current behaviour
- 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 fully stored as blobs but are not OCR’d.
- Default search is automatic: it uses SQLite FTS5 when that fits the query and falls back to literal substring matching for wildcard-like input, invalid FTS syntax, or zero FTS hits.
- The watcher polls
NSPasteboard.changeCounton a short interval. - The watcher is best-effort: if the clipboard changes multiple times between polls, intermediate states can be missed.
- The frontmost app is recorded as a practical hint, not a guaranteed pasteboard owner.
Project layout
src/– Rust sourceextras/launchd/– LaunchAgent templateextras/openclaw/clipboard_memory/– OpenClaw skill stubscripts/install_launchagent.sh– install and load the watcher as a user LaunchAgentscripts/uninstall_launchagent.sh– remove the LaunchAgentscripts/install_openclaw_skill.sh– copy the skill into~/.openclaw/skills
Install
Published installs:
Or, if you already have Rust installed:
The Homebrew package is intended for Apple Silicon Macs. On Intel Macs, prefer:
Build from source:
Or install the current checkout into ~/.local/bin:
That gives you:
Quick start
Capture the current clipboard once:
Start the watcher in the foreground:
Skip storing the clipboard state that already exists when a watcher starts:
Search the archive:
Show recent unique clipboard states from the last 24 hours:
Inspect one stored snapshot:
Export a stored raw representation:
Check SQLite / FTS5 diagnostics:
LaunchAgent install
The easiest route is:
By default that will:
- install the Rust binary into
~/.local/bin - create
~/Library/Application Support/clipmem - write
~/Library/LaunchAgents/io.openclaw.clipmem.watch.plist - configure the LaunchAgent to start with
--skip-initial - load and kickstart the user LaunchAgent
Useful environment variables for the script:
CLIPMEM_INSTALL_ROOT– defaults to~/.localCLIPMEM_DB_PATH– defaults to~/Library/Application Support/clipmem/clipmem.sqlite3CLIPMEM_INTERVAL_MS– defaults to350
To remove the LaunchAgent and plist:
OpenClaw
Install the bundled skill stub:
That copies:
extras/openclaw/clipboard_memory/SKILL.md
into:
~/.openclaw/skills/clipboard_memory
The skill tells OpenClaw to use:
clipmem search "<query>" --jsonclipmem recent --hours 24 --jsonclipmem get <snapshot-id> --json
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.
After copying the skill, reload skills or restart OpenClaw if it does not appear immediately.
Schema notes
The key tables are:
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
Limitations worth knowing
- Binary payloads are stored exactly, but only recognized text-like payloads are indexed.
clipmem get --jsonintentionally omits raw blob bytes. Useclipmem exportto recover stored binary payloads.- RTF and HTML text extraction is intentionally lightweight and best-effort.
- Search is great for commands, code, URLs, notes, logs and copied prose. Use
--mode autofor the default FTS-or-literal behavior,--mode ftsfor strict FTS5 queries, or--mode literalfor exact substring matching. It is not semantic search. - Explicit
--mode ftskeeps SQLite FTS5 semantics. In automatic mode, punctuation-heavy inputs may fall back to literal search. - This project is written to be easy to extend: adding export commands, embeddings, OCR, source-app heuristics or richer HTML parsing is straightforward.
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.”
Development notes
The code is split so the database, search and tests compile cross-platform, while the actual capture implementation is behind cfg(target_os = "macos").
There are a couple of unit tests for the database layer and text extraction helpers. On a Mac with Rust installed, run:
Release automation
Releases are tag-driven. Pushing a semver tag like v0.1.0 triggers:
- validation that the git tag matches
Cargo.toml - test and package verification
- crates.io publish
- GitHub Release asset generation via
cargo-dist - Homebrew tap updates for
tristanmanchester/homebrew-tap
Repo-side release files:
.github/workflows/ci.yml– push and pull request validation.github/workflows/release.yml– generated bycargo-distfor tag-driven releases.github/workflows/publish-crate.yml– reusable bootstrap publish job for crates.iodist-workspace.toml–cargo-distrelease configuration
Bootstrap for 0.1.0:
- Create the tap repo
tristanmanchester/homebrew-tap. - Add the GitHub Actions secret
HOMEBREW_TAP_TOKENwith write access to that repo. - Add a temporary
CARGO_REGISTRY_TOKENsecret for the first crates.io publish. - Push tag
v0.1.0.
After 0.1.0 is live on crates.io, migrate the crates publish step from the temporary CARGO_REGISTRY_TOKEN secret to crates.io Trusted Publishing with rust-lang/crates-io-auth-action@v1, then remove the long-lived secret.