recall
A macOS command snippet manager with semantic search.
Save terminal commands, search them with natural language, copy to clipboard in one keystroke.
Installation
- Download the latest DMG release
- Move
recall.appto/Applications - Launch — the model downloads once (~120 MB, one-time)
- Add the CLI to your shell path:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
Quick Start
App (⌘⇧R anywhere): Opens the window with fuzzy filter. Press + Add to save a new command.
CLI:
recall "switch kube context to staging"
# → copies: kubectl config use-context staging
Features
Command Library
The Commands tab lists all saved commands.
- Filter bar — fuzzy filter by description, command text, or tags (⌘F)
- Sort — Recently Added / Most Used / Recently Used / A–Z
- Tag chips — click a tag to filter by it; multiple tags = AND filter
- Copy — copies command to clipboard, increments use count
- Edit / Delete — edit in the modal or delete with 5-second undo toast
- Keyboard nav — ↑↓ to select, Enter to copy, ⌘E to edit, Delete to delete
Template Variables
Commands can contain {variable} placeholders. In the CLI, recall prompts for each value. In the app, the variable is highlighted inline.
kubectl config use-context {cluster}
./deploy.sh --env {environment} --version {version}
Semantic Search
The Semantic Search tab runs a full AI-powered search using the same model as the CLI.
- Finds commands by intent even if the wording differs
- Score shown as percentage (cosine similarity × 100)
- Same
RECALL_MIN_SCOREthreshold applies
Shell History Import
Click From Shell History in the header to scan ~/.zsh_history and ~/.bash_history.
- Top 50 most-used commands are shown, sorted by frequency
- Trivial single-token commands (ls, cd, etc.) are pre-filtered
- Select any subset and click Import — duplicates are skipped
Import / Export (JSON)
Export dumps all commands to recall-commands.json.
Import JSON merges a JSON file into the library — existing commands (matched by exact command string) are skipped.
JSON format:
[
{
"description": "Pretty git log",
"command": "git log --oneline --graph --decorate",
"aliases": "git log graph"
}
]
Global Hotkey
⌘⇧R (anywhere on macOS) brings the recall window to the front.
Duplicate Detection
When adding or editing a command, if the new command is semantically similar (>90% cosine similarity) to an existing one, a warning is shown. You can dismiss and save anyway.
PATH Setup Banner
If ~/.local/bin is not in your PATH, recall shows a setup banner with the shell command to add it. Dismiss permanently with ✕.
CLI Reference
The recall binary is installed to ~/.local/bin/recall by the app.
Usage
recall <search query>
recall --warmup
Commands
recall <query>
Embeds <query> using the local AI model, finds the closest saved command by cosine similarity, and copies it to clipboard.
recall "clean docker"
# → copies: docker system prune -af
# → prints: Copied (89% match): Clean up all Docker resources
Interactive mode (when stdin and stdout are a TTY):
- If multiple results score above the threshold, shows a numbered picker
- Press
1–5to select, or Enter for the top result
Template variables: If a command contains {placeholders}, recall prompts for values:
recall "deploy to env"
# Command: ./deploy.sh --env {environment}
# environment: prod
# → copies: ./deploy.sh --env prod
Exit codes:
0— command found and copied1— no match above threshold
recall --warmup
Pre-loads the AI model into the OS page cache without running a search. Add to .zshrc to reduce cold-start latency on subsequent recall calls:
# ~/.zshrc
(recall --warmup &>/dev/null &)
Environment Variables
| Variable | Default | Description |
|---|---|---|
RECALL_MIN_SCORE | 0.50 | Minimum cosine similarity (0–1) to count as a match |
Data Location
The SQLite database is at ~/Library/Application Support/recall/recall.db (macOS).
Keyboard Shortcuts
Global (anywhere on macOS)
| Shortcut | Action |
|---|---|
| ⌘⇧R | Bring recall window to front |
App — Commands Tab
| Shortcut | Action |
|---|---|
| ⌘F | Focus the filter input |
| ⌘N | Open “Add command” modal |
| ↑ / ↓ | Move selection up/down |
| Enter | Copy selected command |
| ⌘E | Edit selected command |
| Delete / Backspace | Delete selected command (5s undo) |
Add/Edit Modal
| Shortcut | Action |
|---|---|
| Enter (in textarea) | Submit form |
| Escape | Close modal |
Semantic Search Tab
| Shortcut | Action |
|---|---|
| Enter (in search input) | Run search |
Tauri Commands (API)
These are the IPC commands callable from the frontend via invoke().
All commands return Result<T, String> on the Rust side; errors are thrown as strings in JS.
list_commands
invoke<Command[]>("list_commands", { sort?: SortOrder })
Returns all saved commands in the specified order.
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
sort | "recently_added" | "most_used" | "recently_used" | "alphabetical" | "recently_added" | Sort order |
Returns: Command[]
add_command
invoke<Command>("add_command", { payload: CommandPayload })
Creates a new command and generates its embedding.
Parameters:
| Name | Type | Description |
|---|---|---|
payload.description | string | Human-readable label |
payload.command | string | The shell command |
payload.aliases | string | Space/comma-separated tags |
Returns: Command (the newly created row)
Errors: "AI model still loading — try again in a moment." if model not ready.
update_command
invoke<Command>("update_command", { id: number, payload: CommandPayload })
Updates an existing command and regenerates its embedding.
Returns: Command (updated row)
delete_command
invoke<void>("delete_command", { id: number })
Deletes a command and its embedding (cascade).
record_copy
invoke<void>("record_copy", { id: number })
Increments use_count and sets last_used for the given command. Called on every clipboard copy.
search_commands
invoke<SearchResult[]>("search_commands", { query: string, limit: number })
Runs semantic search over all stored embeddings.
Parameters:
| Name | Type | Description |
|---|---|---|
query | string | Natural language query |
limit | number | Max results to return (before score filter) |
Returns: SearchResult[] — sorted by score descending, filtered by RECALL_MIN_SCORE (default 0.50).
find_similar
invoke<string | null>("find_similar", { payload: CommandPayload, exclude_id?: number })
Finds an existing command semantically similar (>90%) to the given payload. Used for duplicate detection in the add/edit modal.
Returns: The matching command string, or null if none found or model not ready.
model_status
invoke<"downloading" | "loading" | "ready">("model_status")
Returns the current state of the AI model:
"downloading"— first run, fetching ~120 MB"loading"— files present, ONNX initializing"ready"— ready for inference
embedder_ready
invoke<boolean>("embedder_ready")
Shorthand boolean for model_status() === "ready".
export_commands
invoke<string>("export_commands")
Returns all commands as pretty-printed JSON (array of CommandPayload-compatible objects).
import_commands
invoke<number>("import_commands", { json: string })
Parses a JSON array of commands and inserts those not already present (matched by exact command string).
Returns: Count of newly inserted commands.
get_history_suggestions
invoke<HistorySuggestion[]>("get_history_suggestions", { limit: number })
Parses ~/.zsh_history and ~/.bash_history, counts command frequency, and returns the top limit entries.
Returns:
interface HistorySuggestion {
command: string;
count: number;
}
import_from_history
invoke<number>("import_from_history", { commands: string[] })
Inserts the given command strings as new recall commands (description = command). Skips exact duplicates.
Returns: Count of newly inserted commands.
check_cli_in_path
invoke<boolean>("check_cli_in_path")
Returns true if ~/.local/bin is present in the PATH environment variable.
Type Definitions
interface Command {
id: number;
description: string;
command: string;
aliases: string;
created_at: number; // Unix timestamp
updated_at: number;
use_count: number;
last_used: number | null;
}
interface CommandPayload {
description: string;
command: string;
aliases: string;
}
interface SearchResult {
command: Command;
score: number; // 0–1 cosine similarity
}
Database Schema
SQLite database at ~/Library/Application Support/recall/recall.db.
commands
| Column | Type | Description |
|---|---|---|
id | INTEGER PK | Auto-increment |
description | TEXT NOT NULL | Human-readable label |
command | TEXT NOT NULL | Shell command |
aliases | TEXT NOT NULL DEFAULT ‘’ | Space/comma-separated tags |
created_at | INTEGER NOT NULL | Unix timestamp (seconds) |
updated_at | INTEGER NOT NULL | Unix timestamp (seconds) |
use_count | INTEGER NOT NULL DEFAULT 0 | Times copied |
last_used | INTEGER | Unix timestamp of last copy, or NULL |
embeddings
| Column | Type | Description |
|---|---|---|
command_id | INTEGER PK FK→commands(id) | Cascade delete |
vector | BLOB NOT NULL | 384-dim float32 vector, little-endian |
Migrations
Migrations run at app startup via idempotent ALTER TABLE ... ADD COLUMN (silently ignores duplicate column errors):
ALTER TABLE commands ADD COLUMN use_count INTEGER NOT NULL DEFAULT 0;
ALTER TABLE commands ADD COLUMN last_used INTEGER;
Architecture
Overview
┌─────────────────────────────────────────────────────────┐
│ recall.app (Tauri 2) │
│ │
│ ┌───────────────────┐ ┌────────────────────────┐ │
│ │ React frontend │ IPC │ Rust backend │ │
│ │ (Vite + TS) │◄────►│ (src-tauri/src/lib.rs)│ │
│ └───────────────────┘ └──────────┬─────────────┘ │
│ │ │
│ ┌─────────▼──────────┐ │
│ │ recall-core │ │
│ │ ┌────────────────┐ │ │
│ │ │ db (rusqlite) │ │ │
│ │ ├────────────────┤ │ │
│ │ │ embedder │ │ │
│ │ │ (fastembed) │ │ │
│ │ ├────────────────┤ │ │
│ │ │ search │ │ │
│ │ │ (cosine sim) │ │ │
│ │ ├────────────────┤ │ │
│ │ │ history │ │ │
│ │ │ (zsh/bash) │ │ │
│ │ └────────────────┘ │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ recall CLI (cmdcpy binary) │
│ ~/.local/bin/recall │
│ → loads recall-core, queries same DB │
└────────────────────────────────────────┘
Crate Layout
| Crate | Path | Purpose |
|---|---|---|
recall-core | recall-core/ | Shared library: DB, embedder, search, history |
recall (lib) | src-tauri/ | Tauri app backend — exposes IPC commands |
recall (bin) | cmdcpy/ | CLI binary installed to ~/.local/bin/recall |
AI Model
Uses fastembed-rs with AllMiniLML6V2 (384-dimensional embeddings). Model files are cached in ~/.cache/fastembed/ after the one-time ~120 MB download.
Embeddings are stored as raw f32 LE bytes in the embeddings.vector BLOB. Search is linear cosine similarity scan — fast for typical library sizes (<10k commands).
IPC
Frontend calls Rust via @tauri-apps/api/core invoke(). All commands are registered in tauri::generate_handler![]. Payloads serialize with serde_json.
State
AppState holds:
conn: Mutex<Connection>— single SQLite connection (serialized writes)embedder: Arc<Mutex<Option<Embedder>>>—Nonewhile model is loading; set from background thread