Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

recall

A macOS command snippet manager with semantic search.

Save terminal commands, search them with natural language, copy to clipboard in one keystroke.

Installation

  1. Download the latest DMG release
  2. Move recall.app to /Applications
  3. Launch — the model downloads once (~120 MB, one-time)
  4. 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}

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_SCORE threshold 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 15 to 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 copied
  • 1 — 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

VariableDefaultDescription
RECALL_MIN_SCORE0.50Minimum 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)

ShortcutAction
⌘⇧RBring recall window to front

App — Commands Tab

ShortcutAction
⌘FFocus the filter input
⌘NOpen “Add command” modal
↑ / ↓Move selection up/down
EnterCopy selected command
⌘EEdit selected command
Delete / BackspaceDelete selected command (5s undo)

Add/Edit Modal

ShortcutAction
Enter (in textarea)Submit form
EscapeClose modal

Semantic Search Tab

ShortcutAction
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:

NameTypeDefaultDescription
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:

NameTypeDescription
payload.descriptionstringHuman-readable label
payload.commandstringThe shell command
payload.aliasesstringSpace/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:

NameTypeDescription
querystringNatural language query
limitnumberMax 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

ColumnTypeDescription
idINTEGER PKAuto-increment
descriptionTEXT NOT NULLHuman-readable label
commandTEXT NOT NULLShell command
aliasesTEXT NOT NULL DEFAULT ‘’Space/comma-separated tags
created_atINTEGER NOT NULLUnix timestamp (seconds)
updated_atINTEGER NOT NULLUnix timestamp (seconds)
use_countINTEGER NOT NULL DEFAULT 0Times copied
last_usedINTEGERUnix timestamp of last copy, or NULL

embeddings

ColumnTypeDescription
command_idINTEGER PK FK→commands(id)Cascade delete
vectorBLOB NOT NULL384-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

CratePathPurpose
recall-corerecall-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>>>None while model is loading; set from background thread