Guides
Quick Start
Installation
Use uv (recommended) or pip.
To use local MLX models on macOS Apple Silicon (no API keys needed):
uv tool install 'keep-skill[local]'
For all others:
uv tool install keep-skill
That's it! API providers for Voyage, OpenAI, Anthropic, and Gemini are included.
Provider Configuration
Hosted Service
Sign up at keepnotes.ai to get an API key — no local models, no database setup:
export KEEPNOTES_API_KEY=kn_...
keep put "test" # That's it — storage, search, and summarization handled
Works across all your tools (Claude Code, Kiro, Codex) with the same API key. Project isolation, media pipelines, and backups are managed for you.
API Providers
Set environment variables for your preferred providers:
| Provider | Env Variable | Get API Key | Embeddings | Summarization |
|---|---|---|---|---|
| Voyage AI | VOYAGE_API_KEY | dash.voyageai.com | ✓ | - |
| Anthropic | ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN* | console.anthropic.com | - | ✓ |
| OpenAI | OPENAI_API_KEY | platform.openai.com | ✓ | ✓ |
| Google Gemini | GEMINI_API_KEY | aistudio.google.com | ✓ | ✓ |
\* Anthropic Authentication Methods:
- API Key (
ANTHROPIC_API_KEY): Recommended. Get from console.anthropic.com. Format:sk-ant-api03-... - OAuth Token (
CLAUDE_CODE_OAUTH_TOKEN): For Claude Pro/Team subscribers. Generate viaclaude setup-token. Format:sk-ant-oat01-... - Note: OAuth tokens from
claude setup-tokenare primarily designed for Claude Code CLI authentication - Direct API access with OAuth tokens may have limitations or require additional configuration
- For production use with
keep, prefer using a standard API key from the Anthropic console
Simplest setup (single API key):
export OPENAI_API_KEY=... # Does both embeddings + summarization
# Or: GEMINI_API_KEY=... # Also does both
keep put "test" # Store auto-initializes on first use
Best quality (two API keys for optimal embeddings):
export VOYAGE_API_KEY=... # Embeddings (Anthropic's partner)
export ANTHROPIC_API_KEY=... # Summarization (cost-effective: claude-3-haiku)
# Or: CLAUDE_CODE_OAUTH_TOKEN # OAuth token alternative
keep put "test"
Ollama (Local LLM Server)
If Ollama is running locally with models pulled, keep auto-detects it — no configuration needed:
ollama pull llama3.2:3b # Any model works
keep put "test" # Auto-detected on first run
Keep picks the best available model: dedicated embedding models (e.g. nomic-embed-text) for embeddings, generative models (e.g. llama3.2) for summarization. Respects OLLAMA_HOST if set.
Local Providers (Apple Silicon)
For offline operation on macOS Apple Silicon without Ollama:
uv tool install 'keep-skill[local]'
keep put "test" # No API key needed
Claude Desktop Setup
For use in Claude Desktop, API-based providers can be used.
For OpenAI (handles both embeddings and summarization):
- Get an OpenAI API key at platform.openai.com
- Add to network allowlist:
api.openai.com - Set
OPENAI_API_KEYand use normally
Alternatively, for best quality embeddings with Anthropic summarization:
- Get API keys at dash.voyageai.com and console.anthropic.com
- Add to network allowlist:
api.voyageai.com,api.anthropic.com - Set both
VOYAGE_API_KEYandANTHROPIC_API_KEY
Basic Usage
# Index content (files, URLs, or inline text)
keep put "file://$(keep config tool)/docs/library/ancrenewisse.pdf"
keep put https://inguz.substack.com/p/keep -t topic=practice
keep put "Meeting notes from today" -t type=meeting
keep put "some content" --suggest-tags # Show tag suggestions from similar items
# Search (returns: id date summary)
keep find "authentication" --limit 5
keep find "auth" --since P7D # Last 7 days
# Retrieve (shows similar items by default)
keep get "file://$(keep config tool)/docs/library/ancrenewisse.pdf"
keep get https://inguz.substack.com/p/keep
keep get ID --meta # List meta items only
keep get ID --similar # List similar items only
# Tags
keep list --tag project=myapp # Find by tag
keep list --tags= # List all tag keys
keep tag-update ID --tag status=done # Update tags
Reading the Output
Commands produce output in a distinctive format. Here's what to expect.
Search results (keep find) show one line per result — id date summary:
now 2026-02-07 Finished reading MN61. The mirror teaching: ...
file:///.../library/mn61.html 2026-02-07 The Exhortation to Rāhula...
https://inguz.substack.com/p/keep 2026-02-07 Keep: A Reflective Memory...
file:///.../library/han_verse.txt 2026-02-07 Han Verse: Great is the matter...
Full output (keep get, keep now) uses YAML frontmatter with the document body below:
---
id: file:///.../library/mn61.html
tags: {_source: uri, _updated: 2026-02-07T15:14:28+00:00, topic: reflection, type: teaching}
similar:
- https://inguz.substack.com/p/keep (0.47) 2026-02-07 Keep: A Reflective Memory...
- now (0.45) 2026-02-07 Finished reading MN61. The mirror teachi...
- file:///.../library/han_verse.txt (0.44) 2026-02-07 Han Verse: Great is the matter...
meta/todo:
- %a1b2c3d4 Update auth docs for new flow
meta/learnings:
- %e5f6g7h8 JSON validation before deploy
prev:
- @V{1} 2026-02-07 Previous version summary...
---
The Exhortation to Rāhula at Mango Stone is a Buddhist sutra that teaches...
Key fields:
similar:— related items with similarity scores (0–1). Each ID can be passed tokeep getmeta/*:— contextual items from tag queries (open commitments, learnings, decisions)prev:/next:— version navigation.@V{1}means "one version back", usable with-V 1tags:— user tags and system tags (_created,_updated,_source, etc.)
Other output formats: --json for machine-readable JSON, --ids for bare IDs only.
Current Intentions
Track what you're working on:
keep now # Show current intentions
keep now "Working on auth bug" # Update intentions
keep now -V 1 # Previous intentions
keep now --history # All versions
keep reflect # Deep structured reflection
Version History
All documents retain history on update:
keep get ID # Current version (shows prev nav)
keep get ID -V 1 # Previous version
keep get ID --history # List all versions
Text updates use content-addressed IDs:
keep put "my note" # Creates ID from content hash
keep put "my note" -t done # Same ID, new version (tag change)
keep put "different note" # Different ID (new document)
Python API
For embedding keep into applications, see PYTHON-API.md.
Model Configuration
Customize models in ~/.keep/keep.toml:
[embedding]
name = "voyage"
model = "voyage-3.5-lite"
[summarization]
name = "anthropic"
model = "claude-3-haiku-20240307"
Media Description (optional)
When configured, images and audio files get model-generated descriptions alongside their extracted metadata, making them semantically searchable. Without this, media files are indexed with metadata only (EXIF, ID3 tags).
[media]
name = "mlx"
vision_model = "mlx-community/Qwen2-VL-2B-Instruct-4bit"
whisper_model = "mlx-community/whisper-large-v3-turbo"
Install media dependencies (Apple Silicon): pip install keep-skill[media]
Auto-detected if mlx-vlm or mlx-whisper is installed, or if Ollama has a vision model (e.g. llava).
Available Models
| Provider | Type | Models |
|---|---|---|
| Voyage | Embeddings | voyage-3.5-lite (default), voyage-3-large, voyage-code-3 |
| Anthropic | Summarization | claude-3-haiku-20240307 (default, $0.25/MTok), claude-3-5-haiku-20241022 |
| OpenAI | Embeddings | text-embedding-3-small (default), text-embedding-3-large |
| OpenAI | Summarization | gpt-4o-mini (default), gpt-4o |
| Gemini | Embeddings | text-embedding-004 (default) |
| Gemini | Summarization | gemini-3-flash-preview (default), gemini-3-pro-preview |
| Ollama | Embeddings | Any model; prefer nomic-embed-text, mxbai-embed-large |
| Ollama | Summarization | Any generative model (e.g. llama3.2, mistral, phi3) |
| Ollama | Media | Vision models: llava, moondream, bakllava (images only) |
| Local | Embeddings | all-MiniLM-L6-v2 (sentence-transformers) |
| Local | Summarization | MLX models (Apple Silicon only) |
| Local | Media | mlx-vlm for images, mlx-whisper for audio (Apple Silicon only) |
Tool Integrations
On first use, keep detects coding tools and installs a protocol block and hooks into their global configuration. This happens once and is tracked in keep.toml.
| Tool | Protocol Block | Hooks |
|---|---|---|
Claude Code (~/.claude/) | CLAUDE.md — reflective practice prompt | settings.json — session start, prompt submit, subagent, session end |
Kiro (~/.kiro/) | steering/keep.md — reflective practice prompt | hooks/*.kiro.hook — agent spawn, prompt submit, agent stop |
OpenAI Codex (~/.codex/) | AGENTS.md — reflective practice prompt | — |
| OpenClaw (cwd) | AGENTS.md — reflective practice prompt (if found in cwd) | Plugin — agent start, agent stop |
Hooks inject keep now context at key moments (session start, prompt submit) so the agent always has current intentions and relevant context. The protocol block teaches the reflective practice itself.
Run keep config to see integration status. Set KEEP_NO_SETUP=1 to skip auto-install.
Environment Variables
KEEP_STORE_PATH=/path/to/store # Override store location
KEEP_TAG_PROJECT=myapp # Auto-apply tags
KEEP_NO_SETUP=1 # Skip auto-install of tool integrations
OLLAMA_HOST=http://localhost:11434 # Ollama server URL (auto-detected)
OPENAI_API_KEY=sk-... # For OpenAI (embeddings + summarization)
GEMINI_API_KEY=... # For Gemini (embeddings + summarization)
VOYAGE_API_KEY=pa-... # For Voyage embeddings only
ANTHROPIC_API_KEY=sk-ant-... # For Anthropic summarization only
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-... # OAuth token alternative
Data Security
Encryption at Rest
Keep stores data in SQLite databases and ChromaDB files on disk. These are not encrypted by default.
If you store sensitive content (plans, credentials, reasoning traces), enable disk encryption:
| OS | Solution | How |
|---|---|---|
| macOS | FileVault | System Settings > Privacy & Security > FileVault |
| Linux | LUKS | Encrypt home directory or the partition containing ~/.keep/ |
| Windows | BitLocker | Settings > Privacy & security > Device encryption |
This is the recommended approach because it transparently covers both SQLite and ChromaDB's internal storage without application-level changes.
Troubleshooting
No embedding provider configured: Set an API key (e.g., VOYAGE_API_KEY) or install keep-skill[local].
Model download hangs: First use of local models downloads weights (~minutes). Cached in ~/.cache/.
ChromaDB errors: Delete ~/.keep/chroma/ to reset.
Slow local summarization: Large content is summarized in the background automatically.
Claude Code hooks need jq: The prompt-submit hook uses jq to extract context. Install with your package manager (e.g., brew install jq). Hooks are fail-safe without it, but prompt context won't be captured.
Next Steps
- REFERENCE.md — Complete CLI reference
- PYTHON-API.md — Python API for embedding keep in applications
- AGENT-GUIDE.md — Working session patterns
- ARCHITECTURE.md — System internals
- OPENCLAW-INTEGRATION.md — OpenClaw plugin setup
- SKILL.md — The reflective practice
Configuration
Show configuration and resolve paths.
Usage
keep config # Show all config
keep config file # Config file location
keep config tool # Package directory (SKILL.md location)
keep config docs # Documentation directory
keep config store # Store path
keep config openclaw-plugin # OpenClaw plugin directory
keep config providers # All provider config
keep config providers.embedding # Embedding provider name
Options
| Option | Description |
|---|---|
--reset-system-docs | Force reload system documents from bundled content |
-s, --store PATH | Override store directory |
Config file location
The config file is keep.toml inside the config directory. The config directory is resolved in this order:
KEEP_CONFIGenvironment variable — explicit path to config directory- Tree-walk — search from current directory up to
~for.keep/keep.toml - Default —
~/.keep/
The tree-walk enables project-local stores: place a .keep/keep.toml in your project root and keep will use it when you're in that directory tree.
Store path resolution
The store (where data lives) is resolved separately from config:
--storeCLI option — per-command overrideKEEP_STORE_PATHenvironment variablestore.pathin config file —[store]section ofkeep.toml- Config directory itself — backwards compatibility default
Config file format
[store]
version = 2
max_summary_length = 1000
[embedding]
name = "mlx" # or "voyage", "openai", "ollama", "sentence_transformers"
model = "all-MiniLM-L6-v2"
[summarization]
name = "mlx" # or "anthropic", "openai", "ollama"
model = "mlx-community/Llama-3.2-3B-Instruct-4bit"
[media]
name = "mlx" # or "ollama" (auto-detected)
vision_model = "mlx-community/Qwen2-VL-2B-Instruct-4bit"
whisper_model = "mlx-community/whisper-large-v3-turbo"
[document]
name = "composite"
[tags]
project = "my-project" # Default tags applied to all new items
owner = "alice"
Environment variables
KEEP_STORE_PATH=/path/to/store # Override store location
KEEP_CONFIG=/path/to/.keep # Override config directory
KEEP_TAG_PROJECT=myapp # Auto-apply tags (any KEEP_TAG_* variable)
KEEP_VERBOSE=1 # Debug logging to stderr
KEEP_NO_SETUP=1 # Skip auto-install of tool integrations
Config subpaths
| Path | Returns |
|---|---|
file | Config file path (~/.keep/keep.toml) |
tool | Package directory (where SKILL.md lives) |
docs | Documentation directory |
store | Store data path |
openclaw-plugin | OpenClaw plugin directory |
providers | All provider configuration |
providers.embedding | Embedding provider name |
providers.summarization | Summarization provider name |
providers.media | Media description provider name |
Subpath output is raw (unquoted) for shell scripting:
cat "$(keep config tool)/SKILL.md" # Read the practice guide
ls "$(keep config store)" # List store contents
Resetting system documents
System documents (.conversations, .domains, .tag/*, etc.) are bundled with keep and loaded on first use. If they've been modified or corrupted:
keep config --reset-system-docs # Reload all from bundled content
See Also
- QUICKSTART.md — Installation and provider setup
- REFERENCE.md — Quick reference index
keep put
Add or update a document in the store.
Usage
Three input modes, auto-detected:
keep put "my note" # Text mode (inline content)
keep put file:///path/to/doc.pdf # URI mode (fetch and index)
keep put https://example.com/page # URI mode (web content)
keep put - # Stdin mode (explicit)
echo "piped content" | keep put # Stdin mode (detected)
Options
| Option | Description |
|---|---|
-t, --tag KEY=VALUE | Tag as key=value (repeatable) |
-i, --id ID | Custom document ID (auto-generated for text/stdin) |
--summary TEXT | User-provided summary (skips auto-summarization) |
--suggest-tags | Show tag suggestions from similar items |
-s, --store PATH | Override store directory |
Text mode and content-addressed IDs
Text mode uses content-addressed IDs for automatic versioning:
keep put "my note" # Creates %a1b2c3d4e5f6
keep put "my note" -t done # Same ID, new version (tag change)
keep put "different note" # Different ID (new document)
Same content = same ID = enables versioning through tag changes.
Smart summary behavior
- Short content (under
max_summary_length, default 1000 chars): stored verbatim as its own summary - Long content: truncated placeholder stored immediately, real summary generated in background by
process-pending --summaryprovided: used as-is, skips auto-summarization
Tag suggestions
The --suggest-tags flag shows tags from similar existing items, helping maintain consistent tagging:
keep put "OAuth2 token handling" --suggest-tags
# Suggests: project=myapp (3 similar), topic=auth (5 similar)
Update behavior
When updating an existing document (same ID):
- Summary: replaced with new summary
- Tags: merged — existing tags preserved, new tags override on key collision
- Version: previous version archived automatically
Contextual summarization
When you provide tags during indexing, the summarizer uses context from related items to produce more relevant summaries.
- System finds similar items sharing your tags
- Items with more matching tags rank higher (+20% score boost per tag)
- Top related summaries are passed as context to the LLM
- Summary highlights relevance to that context
Tag changes trigger re-summarization:
keep put doc.pdf # Generic summary
keep put doc.pdf -t topic=auth # Re-queued for contextual summary
Supported formats
| Format | Extensions | Content extracted | Auto-tags |
|---|---|---|---|
| Text | .md, .txt, .py, .js, .json, .yaml, ... | Full text | — |
| Text from all pages | — | ||
| HTML | .html, .htm | Text (scripts/styles removed) | — |
| DOCX | .docx | Paragraphs + tables | author, title |
| PPTX | .pptx | Slides + notes | author, title |
| Audio | .mp3, .flac, .ogg, .wav, .aiff, .m4a, .wma | Structured metadata (+ transcription\*) | artist, album, genre, year, title |
| Images | .jpg, .png, .tiff, .webp | EXIF metadata (+ description\*) | dimensions, camera, date |
\* When a media description provider is configured ([media] in keep.toml), images get vision-model descriptions and audio files get speech-to-text transcription, appended to the extracted metadata. See QUICKSTART.md for setup.
Auto-extracted tags merge with user-provided tags. User tags win on collision:
keep put file:///path/to/song.mp3 # Auto-tags: artist, album, genre, year
keep put file:///path/to/song.mp3 -t genre="Nu Jazz" # Overrides auto-extracted genre
keep put file:///path/to/photo.jpg -t topic=vacation # Adds topic alongside auto camera/date
Indexing documents
Index important documents encountered during work:
keep put "https://docs.example.com/auth" -t topic=auth -t project=myapp
keep put "file:///path/to/design.pdf" -t type=reference -t topic=architecture
See Also
- TAGGING.md — Tag system, merge order, speech acts
- VERSIONING.md — How versioning works
- KEEP-GET.md — Retrieve indexed documents
- REFERENCE.md — Quick reference index
keep get
Retrieve item(s) by ID.
Usage
keep get ID # Current version with similar items
keep get ID1 ID2 ID3 # Multiple items (separated by ---)
keep get ID -V 1 # Previous version
keep get "ID@V{1}" # Same as -V 1 (version identifier syntax)
Options
| Option | Description |
|---|---|
-V, --version N | Get specific version (0=current, 1=previous) |
-H, --history | List all versions (default 10, use -n to override) |
-S, --similar | List similar items (default 10) |
-M, --meta | List meta items |
-t, --tag KEY=VALUE | Require tag (error if item doesn't match) |
-n, --limit N | Max items for --history, --similar, --meta (default 10) |
-s, --store PATH | Override store directory |
Default output
Single-item commands (get, now) default to full YAML frontmatter format:
---
id: %a1b2c3d4
tags: {project: myapp, topic: auth, type: learning}
similar:
- %e5f6a7b8 (0.89) 2026-01-14 Related authentication...
- %c9d0e1f2 (0.85) 2026-01-13 Token handling notes...
meta/learnings:
- %d3e4f5a6 Token refresh needs clock sync
prev:
- @V{1} 2026-01-14 Previous summary text...
---
Document summary here...
Multiple IDs
keep get doc:1 doc:2 doc:3 # Items separated by ---
keep --ids list -n 5 | xargs keep get # Pipe from list
Display modes
keep get ID --similar # Show similar items
keep get ID --similar -n 20 # Show 20 similar items
keep get ID --meta # Show meta items
keep get ID --meta -n 5 # Show 5 meta items per section
keep get ID --history # List all versions
Tag filtering
keep get ID -t project=myapp # Error if item doesn't have this tag
See Also
- VERSIONING.md — Version identifiers and history
- KEEP-FIND.md — Search for items by meaning
- META-DOCS.md — How meta sections work
- REFERENCE.md — Quick reference index
keep find
Find items by semantic similarity or full-text search.
Usage
keep find "authentication" # Semantic similarity search
keep find "auth" --text # Full-text search on summaries
keep find --id ID # Find items similar to an existing item
Options
| Option | Description |
|---|---|
--text | Use full-text search instead of semantic similarity |
--id ID | Find items similar to this ID (instead of text query) |
--include-self | Include the queried item (only with --id) |
-t, --tag KEY=VALUE | Filter by tag (repeatable, AND logic) |
-n, --limit N | Maximum results (default 10) |
--since DURATION | Only items updated since (see time filtering below) |
--history | Include archived versions of matching items |
-s, --store PATH | Override store directory |
Semantic vs full-text search
Semantic search (default) finds items by meaning using embeddings. "authentication" matches items about "login", "OAuth", "credentials" even if they don't contain the word "authentication".
Full-text search (--text) matches exact words in summaries. Faster but literal.
Similar-to-item search
Find items similar to an existing document:
keep find --id file:///path/to/doc.md # Similar to this document
keep find --id %a1b2c3d4 # Similar to this item
keep find --id %a1b2c3d4 --since P30D # Similar items from last 30 days
Tag filtering
Combine semantic search with tag filters (AND logic):
keep find "auth" -t project=myapp # Search within a project
keep find "auth" -t project -t topic=security # Multiple tags (AND)
Time filtering
The --since option accepts ISO 8601 durations or dates:
keep find "auth" --since P7D # Last 7 days
keep find "auth" --since P1W # Last week
keep find "auth" --since PT1H # Last hour
keep find "auth" --since P1DT12H # 1 day 12 hours
keep find "auth" --since 2026-01-15 # Since specific date
Including archived versions
keep find "auth" --history # Also search old versions of items
See Also
- KEEP-LIST.md — List and filter by tags
- KEEP-GET.md — Retrieve full item details
- TAGGING.md — Tag filtering patterns
- REFERENCE.md — Quick reference index
keep list
List recent items, filter by tags, or list tag keys and values.
Usage
keep list # Recent items (by update time)
keep list -n 20 # Show 20 most recent
keep list --sort accessed # Sort by last access time
Options
| Option | Description |
|---|---|
-n, --limit N | Maximum results (default 10) |
-t, --tag KEY=VALUE | Filter by tag (repeatable, AND logic) |
-T, --tags= | List all tag keys |
-T, --tags=KEY | List values for a specific tag key |
--sort ORDER | Sort by updated (default) or accessed |
--since DURATION | Only items updated since (ISO duration or date) |
--history | Include archived versions |
-s, --store PATH | Override store directory |
Tag filtering
keep list --tag project=myapp # Items with project=myapp
keep list --tag project # Items with any 'project' tag
keep list --tag foo --tag bar # Items with both tags (AND)
keep list --tag project --since P7D # Combine tag filter with recency
Listing tags
The --tags option (note: different from --tag) lists tag metadata:
keep list --tags= # List all distinct tag keys
keep list --tags=project # List all values for 'project' tag
Time filtering
keep list --since P3D # Last 3 days
keep list --since P1W # Last week
keep list --since PT1H # Last hour
keep list --since 2026-01-15 # Since specific date
Including versions
keep list --history # Include archived versions
Pipe composition
keep --ids list -n 5 | xargs keep get # Get details for recent items
keep --ids list --tag project=foo | xargs keep del # Bulk operations
See Also
- TAGGING.md — Tag system and filtering
- KEEP-FIND.md — Search by meaning
- KEEP-GET.md — Retrieve full item details
- REFERENCE.md — Quick reference index
keep now
Get or set the current working intentions.
The nowdoc is a singleton document that tracks what you're working on right now. It has full version history, so previous intentions are always accessible.
Usage
keep now # Show current intentions
keep now "Working on auth flow" # Update intentions
keep now "Auth work" -t project=myapp # Update with tag
echo "piped content" | keep now # Set from stdin
Options
| Option | Description |
|---|---|
--reset | Reset to default from system |
-V, --version N | Get specific version (0=current, 1=previous) |
-H, --history | List all versions |
-t, --tag KEY=VALUE | Set tag (with content) or filter (without content) |
-n, --limit N | Max similar/meta items to show (default 3) |
-s, --store PATH | Override store directory |
Tag behavior
Tags behave differently depending on mode:
- With content:
-tsets tags on the update - Without content:
-tfilters version history to find the most recent version with matching tags
keep now "Auth work" -t project=myapp # Sets project=myapp tag
keep now -t project=myapp # Finds recent version with that tag
Version navigation
The nowdoc retains full history. Each update creates a new version.
keep now -V 1 # Previous intentions
keep now -V 2 # Two versions ago
keep now --history # List all versions
Output includes prev: navigation for browsing version history. See VERSIONING.md for details.
Similar items and meta sections
When updating (keep now "..."), the output surfaces similar items and meta sections as occasions for reflection:
---
id: now
tags: {project: myapp, topic: auth}
similar:
- %a1b2c3d4 OAuth2 token refresh pattern...
meta/todo:
- %e5f6a7b8 validate redirect URIs
meta/learnings:
- %c9d0e1f2 Token refresh needs clock sync
---
Working on auth flow
keep move
When a string of work is complete, move the now history into a named note. Requires either -t (tag filter) or --only (cherry-pick the tip).
keep move "auth-string" -t project=myapp # Move matching versions
keep move "quick-note" --only # Move just the current version
Moving to an existing name appends incrementally. Use --from to reorganize between items. See KEEP-MOVE.md for details.
keep reflect
Deep structured reflection practice. Guides you through gathering context, examining actions, and updating intentions.
keep reflect
See Also
- KEEP-MOVE.md — Move now history into named items
- TAGGING.md — Tag system and speech-act tracking
- VERSIONING.md — Version history and navigation
- META-DOCS.md — How meta sections surface contextual items
- REFERENCE.md — Quick reference index
keep move
Move versions from now (or another note) into a named note.
As you update keep now throughout a session, a string of versions accumulates. keep move extracts selected versions into a named note. Requires either -t (tag filter) or --only (cherry-pick the tip).
Usage
keep move "auth-string" -t project=myapp # Move matching versions from now
keep move "auth-string" -t project=myapp # Incremental: appends more
keep move "quick-note" --only # Move just the current version
keep move "target" --from "source" -t topic=X # Reorganize between items
Options
| Option | Description |
|---|---|
-t, --tag KEY=VALUE | Only extract versions matching these tags (repeatable) |
--only | Move only the current (tip) version |
--from ITEM_ID | Source item to extract from (default: now) |
-s, --store PATH | Override store directory |
Required: at least one of -t or --only must be specified.
How it works
- Versions matching the filter are moved from the source to the named target
- Non-matching versions remain in the source, with gaps tolerated
- If the source is fully emptied and is
now, it resets to default content - The moved item gets
_saved_fromand_saved_atsystem tags
Note on URI-shaped target names: The target name is just a string ID — it can be anything, including a URI like https://example.com/doc or file:///path/to/file. However, if the target name looks like a URI, a subsequent keep put will re-fetch content from the URL and overwrite what was moved there. This is by design (the ID is its fetch source), but be aware that move can effectively create an item whose ID points to a different origin than its content. This is analogous to a redirect — the stored content came from now, but the ID says https://....
Cherry-picking with --only
--only moves just the current (tip) version, one at a time. This is the cherry-picker for reorganizing untagged items:
keep move "string-a" --only # Move tip to string-a
keep move "string-b" --only # Move next tip to string-b
keep move "string-a" --only # Append another to string-a
Combine with -t to only move the tip if it matches:
keep move "auth-log" --only -t topic=auth # Move tip only if tagged auth
Reorganizing with --from
Use --from to extract versions from any item, not just now:
# Over-grabbed? Pull specific versions out
keep move "auth-string" --from "big-dump" -t project=auth
keep move "docs-string" --from "big-dump" -t project=docs
# Cherry-pick one version from an existing item
keep move "highlights" --from "session-log" --only
Incremental move
Moving to an existing name appends the new versions on top of the existing history:
# Session 1
keep now "design discussion" -t project=alpha
keep now "decided on approach B" -t project=alpha
keep move "alpha-log" -t project=alpha
# Session 2
keep now "implemented approach B" -t project=alpha
keep now "tests passing" -t project=alpha
keep move "alpha-log" -t project=alpha # Appends to existing
keep get alpha-log --history # Shows all 4 versions
Tag-filtered move
When you work on multiple projects in one session, tag filtering lets you move each string separately:
keep now "auth: token refresh" -t project=auth
keep now "docs: update API guide" -t project=docs
keep now "auth: added tests" -t project=auth
keep move "auth-string" -t project=auth # Extracts 2 auth versions
keep now # Still has docs version
Version history
The moved item has full version history, navigable like any other item:
keep get string-name # Current (newest moved)
keep get string-name -V 1 # Previous version
keep get string-name --history # List all versions
See Also
- KEEP-NOW.md — The nowdoc and intentions tracking
- VERSIONING.md — Version history and navigation
- TAGGING.md — Tag system and filtering
- REFERENCE.md — Quick reference index
Tagging
Tags are key-value pairs attached to every item. They enable filtering, organization, and commitment tracking.
One value per key. Setting a tag overwrites any existing value for that key.
Setting tags
keep put "note" -t project=myapp -t topic=auth # On create
keep now "working on auth" -t project=myapp # On now update
keep tag-update ID --tag key=value # Add/update tag on existing item
keep tag-update ID --remove key # Remove a tag
keep tag-update ID1 ID2 --tag status=done # Tag multiple items
Tag merge order
When indexing documents, tags are merged in this order (later wins):
- Existing tags — preserved from previous version
- Config tags — from
[tags]section inkeep.toml - Environment tags — from
KEEP_TAG_*variables - User tags — passed via
-ton the command line
Environment variable tags
Set tags via environment variables with the KEEP_TAG_ prefix:
export KEEP_TAG_PROJECT=myapp
export KEEP_TAG_OWNER=alice
keep put "deployment note" # auto-tagged with project=myapp, owner=alice
Config-based default tags
Add a [tags] section to keep.toml:
[tags]
project = "my-project"
owner = "alice"
Tag filtering
The -t flag filters results on find, list, get, and now:
keep find "auth" -t project=myapp # Semantic search + tag filter
keep find "auth" -t project -t topic=auth # Multiple tags (AND logic)
keep list --tag project=myapp # List items with tag
keep list --tag project # Any item with 'project' tag
keep get ID -t project=myapp # Error if item doesn't match
keep now -t project=myapp # Find now version with tag
Listing tags
keep list --tags= # List all distinct tag keys
keep list --tags=project # List all values for 'project' tag
Organizing by project and topic
Two tags help organize work across boundaries:
| Tag | Scope | Examples |
|---|---|---|
project | Bounded work context | myapp, api-v2, migration |
topic | Cross-project subject area | auth, testing, performance |
# Project-specific knowledge
keep put "OAuth2 with PKCE chosen" -t project=myapp -t topic=auth
# Cross-project knowledge (topic only)
keep put "Token refresh needs clock sync" -t topic=auth
# Search within a project
keep find "authentication" -t project=myapp
# Search across projects by topic
keep find "authentication" -t topic=auth
For more on these conventions: keep get .tag/project and keep get .tag/topic.
For domain-specific organization patterns: keep get .domains.
Speech-act tags
Two tags make the commitment structure of work visible:
act — speech-act category
| Value | What it marks | Example |
|---|---|---|
commitment | A promise to act | "I'll fix auth by Friday" |
request | Asking someone to act | "Please review the PR" |
offer | Proposing to act | "I could refactor the cache" |
assertion | A claim of fact | "The tests pass on main" |
assessment | A judgment | "This approach is risky" |
declaration | Changing reality | "Released v2.0" |
status — lifecycle state
| Value | Meaning |
|---|---|
open | Active, unfulfilled |
fulfilled | Completed and satisfied |
declined | Not accepted |
withdrawn | Cancelled by originator |
renegotiated | Terms changed |
Usage
# Track a commitment
keep put "I'll fix the auth bug" -t act=commitment -t status=open -t project=myapp
# Query open commitments and requests
keep list -t act=commitment -t status=open
keep list -t act=request -t status=open
# Mark fulfilled
keep tag-update ID --tag status=fulfilled
# Record an assertion or assessment (no lifecycle)
keep put "The tests pass" -t act=assertion
keep put "This approach is risky" -t act=assessment -t topic=architecture
For full details: keep get .tag/act and keep get .tag/status.
System tags
Tags prefixed with _ are protected and auto-managed. Users cannot set them directly.
Implemented: _created, _updated, _updated_date, _accessed, _accessed_date, _content_type, _source
See SYSTEM-TAGS.md for complete reference.
Python API
kp.tag("doc:1", {"status": "reviewed"}) # Add/update tag
kp.tag("doc:1", {"obsolete": ""}) # Delete tag (empty string)
kp.query_tag("project", "myapp") # Exact key=value match
kp.query_tag("project") # Any doc with 'project' tag
kp.list_tags() # All distinct tag keys
kp.list_tags("project") # All values for 'project'
See PYTHON-API.md for complete Python API reference.
See Also
- SYSTEM-TAGS.md — Auto-managed system tags
- KEEP-LIST.md — List and filter by tags
- KEEP-FIND.md — Search with tag filters
- REFERENCE.md — Quick reference index
Meta-Docs
Meta-docs are system documents that surface contextually relevant items when you view your current context (keep now) or retrieve any item (keep get). They answer the question: what else should I be aware of right now?
How It Works
A meta-doc is a .meta/* system document containing tag queries. When you run keep now or keep get, each meta-doc's queries run against the store, and matching items appear in the output as named sections.
For example, when you run keep now while working on a project tagged project=myapp:
---
id: now
tags: {project: myapp, topic: auth}
similar:
- %a1b2 OAuth2 token refresh pattern
meta/todo:
- %c3d4 validate redirect URIs
- %e5f6 update auth docs for new flow
meta/learnings:
- %g7h8 JSON validation before deploy saves hours
---
Working on auth flow refactor
The meta/todo: section appeared because you previously captured commitments tagged with project=myapp:
keep put "validate redirect URIs" -t act=commitment -t status=open -t project=myapp
The meta/learnings: section appeared because you previously captured a learning tagged with the same project:
keep put "JSON validation before deploy saves hours" -t type=learning -t project=myapp
Bundled Meta-Docs
keep ships with five meta-docs:
.meta/todo` — Open Loops
Surfaces unresolved commitments, requests, offers, and blocked work. These are things someone said they'd do, or asked for, that aren't resolved yet.
Queries:
act=commitment status=openact=request status=openact=offer status=openstatus=blocked
Context keys: project=, topic=
.meta/learnings` — Experiential Priming
Surfaces past learnings, breakdowns, and gotchas. Before starting work, check what went wrong or was hard-won last time.
Queries:
type=learningtype=breakdowntype=gotcha
Context keys: project=, topic=
.meta/genre` — Same Genre
Groups media items by genre. Only activates for items that have a genre tag.
Prerequisites: genre=*
Context keys: genre=
.meta/artist` — Same Artist
Groups media items by artist/creator. Only activates for items that have an artist tag.
Prerequisites: artist=*
Context keys: artist=
.meta/album` — Same Album
Groups tracks from the same release. Only activates for items that have an album tag.
Prerequisites: album=*
Context keys: album=
Query Structure
A meta-doc file contains prose (for humans and LLMs) plus structured lines:
- Query lines like
act=commitment status=open— eachkey=valuepair is an AND filter; multiple query lines are OR'd together - Context-match lines like
project=— a bare key whose value is filled from the current item's tags - Prerequisite lines like
genre=*— the current item must have this tag or the entire meta-doc is skipped
Context matching is what makes meta-docs contextual. If the current item has project=myapp, then the query act=commitment status=open combined with context key project= becomes act=commitment status=open project=myapp. This scopes results to the current project.
If the current item has no matching tag for a context key, the query runs without that filter.
Prerequisites act as gates. A meta-doc with genre=* only activates for items that have a genre tag — items without one skip the meta-doc entirely. This lets you create meta-docs that only apply to certain kinds of items (e.g., media files tagged with genre, artist, album) without polluting results for unrelated items.
A meta-doc with only prerequisites and context keys (no query lines) becomes a pure group-by: it finds items sharing the same tag value as the current item. For example, genre=* + genre= means "find other items with the same genre as this one."
Ranking
Results are ranked by a combination of:
- Embedding similarity to the current item — so semantically related items rank higher
- Recency decay — recent items get a boost
Each meta-doc returns up to 3 items. Meta sections with no matches are omitted from the output.
Viewing Meta-Docs
You can read the meta-doc definitions themselves:
keep get .meta/todo # See the todo meta-doc's queries and description
keep get .meta/learnings # See the learnings meta-doc's queries
Feeding the Loop
Meta-docs only surface what you put in. The tags that matter:
# Commitments and requests (surface in meta/todo)
keep put "I'll fix the login bug" -t act=commitment -t status=open -t project=myapp
keep put "Can you review the PR?" -t act=request -t status=open -t project=myapp
# Resolve when done
keep put "Login bug fixed" -t act=commitment -t status=fulfilled -t project=myapp
# Learnings and breakdowns (surface in meta/learnings)
keep put "Always check token expiry before refresh" -t type=learning -t topic=auth
keep put "Assumed UTC, server was local time" -t type=breakdown -t project=myapp
# Gotchas (surface in meta/learnings)
keep put "CI cache invalidation needs manual clear after dep change" -t type=gotcha -t topic=ci
Media Library
The media meta-docs (genre, artist, album) surface related media automatically. Tag your audio files when ingesting them:
keep put ~/Music/OK_Computer/01_Airbag.flac -t artist=Radiohead -t album="OK Computer" -t genre=rock
keep put ~/Music/OK_Computer/02_Paranoid_Android.flac -t artist=Radiohead -t album="OK Computer" -t genre=rock
keep put ~/Music/Kid_A/01_Everything.flac -t artist=Radiohead -t album="Kid A" -t genre=electronic
Now when you keep get any of these items, you'll see:
meta/artist:— other tracks by Radioheadmeta/album:— other tracks from the same albummeta/genre:— other rock (or electronic) items
Items without media tags won't see these sections at all — the genre=* prerequisite ensures they're skipped.
The tag taxonomy is documented in system docs:
keep get .tag/act # Speech-act categories
keep get .tag/status # Lifecycle status values
keep get .tag/type # Content type values
keep get .tag/project # Project tag conventions
keep get .tag/topic # Topic tag conventions
See Also
- SKILL.md — The reflective practice
- REFERENCE.md — Complete CLI reference
- AGENT-GUIDE.md — Working session patterns
Versioning
All documents retain history on update. Previous versions are archived automatically.
Strings
Every note is a string — a linear chain of versions that grows each time you update it.
Your working context (now) accumulates a string of intentions as you work.
When a string of work is complete, keep move extracts matching versions by tag
and strings them into a named note.
This isn't version control (no branches, no merges) and it isn't an append-only log
(you can reorganize). It's the temporal dimension of how knowledge evolved — and
unlike other memory stores, you can re-string it by meaning.
Version identifiers
Append @V{N} to any ID to specify a version by offset:
ID@V{0}— current versionID@V{1}— previous versionID@V{2}— two versions ago
keep get "%a1b2c3d4@V{1}" # Previous version
keep get "now@V{2}" # Two versions ago of nowdoc
Accessing versions
keep get ID -V 1 # Previous version (equivalent to @V{1})
keep get ID --history # List all versions
keep now -V 1 # Previous intentions
keep now --history # List all nowdoc versions
History output
--history shows versions as summary lines with version identifiers:
now 2026-01-16 Current summary...
now@V{1} 2026-01-15 Previous summary...
now@V{2} 2026-01-14 Older summary...
With --ids, outputs version identifiers for piping:
keep --ids now --history
# now@V{0}
# now@V{1}
# now@V{2}
Version navigation in output
When viewing a single item (keep get, keep now), the output includes prev: and next: navigation:
---
id: %a1b2c3d4
prev:
- @V{1} 2026-01-14 Previous summary text...
---
Current summary here...
Content-addressed IDs
Text-mode updates use content-addressed IDs for versioning:
keep put "my note" # Creates %a1b2c3d4e5f6
keep put "my note" -t done # Same ID, new version (tag change)
keep put "different note" # Different ID (new document)
Same content = same ID = enables versioning via tag changes.
Deleting and reverting
keep del has revert semantics when versions exist:
keep del ID # If versions exist: reverts to previous version
# If no versions: removes item entirely
Pipe composition
keep --ids now --history | xargs -I{} keep get "{}" # Get all versions
diff <(keep get doc:1) <(keep get "doc:1@V{1}") # Diff current vs previous
Python API
kp.get_version(id, offset=1) # Previous version
kp.get_version(id, offset=2) # Two versions ago
kp.list_versions(id) # All archived versions (newest first)
See PYTHON-API.md for complete Python API reference.
See Also
- KEEP-GET.md — Retrieving items and versions
- KEEP-NOW.md — Nowdoc version history
- REFERENCE.md — Quick reference index
Output Format
When you run keep get or keep now, items are displayed in YAML frontmatter format. Each section carries meaning.
Annotated Example
---
id: %a1b2c3d4 # 1. Identity
tags: {project: myapp, topic: auth, act: commitment, status: open} # 2. Tags
score: 0.823 # 3. Relevance
similar: # 4. Similar items
- %e5f6a7b8 (0.89) 2026-01-14 OAuth2 token refresh pattern...
- %c9d0e1f2 (0.85) 2026-01-13 Token handling notes...
- .tag/act (0.45) 2026-01-10 Speech-act categories...
meta/todo: # 5. Meta sections
- %d3e4f5a6 validate redirect URIs
- %b7c8d9e0 update auth docs for new flow
meta/learnings:
- %f1a2b3c4 Token refresh needs clock sync
prev: # 6. Version navigation
- @V{1} 2026-01-14 Previous version of this item...
- @V{2} 2026-01-13 Older version...
---
I'll fix the auth bug by Friday # 7. Content
Sections
1. id: — Identity
The document's unique identifier.
| Format | Meaning |
|---|---|
%a1b2c3d4 | Content-addressed (text mode, hash of content) |
file:///path/to/doc | Local file URI |
https://example.com | Web URL |
now | The nowdoc (current intentions) |
.conversations | System document (dotted prefix) |
.tag/act | Tag description document |
When viewing an old version, a suffix appears: id: %a1b2c3d4@V{1}
2. tags: — Metadata
Key-value pairs in YAML flow format. One value per key.
tags: {project: myapp, topic: auth, act: commitment, status: open}
Tags you set are shown here. System tags (prefixed _) are hidden from display but accessible via --json.
Key tag patterns:
project/topic— organize by bounded work vs cross-cutting subject. See TAGGING.md.act/status— speech-act tracking (commitment, request, assertion + lifecycle). See TAGGING.md.type— content classification (learning, breakdown, reference, teaching)
3. score: — Relevance
Appears on search results (keep find). A similarity score between 0 and 1, with recency decay applied.
Higher = more relevant. Scores above 0.7 are strong matches. This field is absent when viewing items directly (not via search).
4. similar: — Semantic neighbors
Items semantically close to this one, ranked by similarity. Each line:
- ID (score) date summary...
- ID — the similar item's identifier (use with
keep get) - (score) — cosine similarity (0–1)
- date — last updated date
- summary — truncated summary
Similar items are occasions for reflection: what else do I know about this? Control with -n (limit) or --no-similar to suppress.
5. meta/*: — Contextual sections
Meta-docs surface items matching tag-based queries relevant to what you're viewing. Each section name maps to a meta-doc:
| Section | Surfaces | Source |
|---|---|---|
meta/todo: | Open requests and commitments | .meta/todo |
meta/learnings: | Relevant learnings | .meta/learnings |
meta/decisions: | Related decisions | .meta/decisions |
These are dynamically resolved — the same item shows different meta sections depending on its tags. For example, an item tagged project=myapp surfaces todos and learnings also tagged with that project.
See META-DOCS.md for how meta-docs work and how to create custom ones.
6. prev: / next: — Version navigation
Navigate through the item's version history.
prev:
- @V{1} 2026-01-14 Previous version summary...
- @V{2} 2026-01-13 Older version summary...
@V{N}— version offset (1=previous, 2=two ago). Use withkeep get "ID@V{1}"orkeep get ID -V 1.- Only appears on single-item display (
keep get,keep now). next:appears when viewing an old version, pointing toward newer versions.
See VERSIONING.md for full versioning details.
7. Content (after ---)
The item's summary, below the closing ---. For short content this is the full text; for long documents it's a generated summary.
Output Formats
The frontmatter format is one of four output modes:
| Flag | Format | Use case |
|---|---|---|
| (default) | Summary line or frontmatter | Frontmatter for single items, summary lines for lists |
--ids | Versioned IDs only | Piping to other commands |
--full | YAML frontmatter (forced) | When you want full details on list/find results |
--json | JSON | Programmatic access |
See REFERENCE.md for examples of each format.
See Also
- TAGGING.md — Tag system and conventions
- VERSIONING.md — Version history and navigation
- META-DOCS.md — How meta sections are resolved
- SYSTEM-TAGS.md — Hidden system tags
- REFERENCE.md — Quick reference index
CLI Reference
Purpose: Persistent memory for documents with semantic search.
Default store: ~/.keep/ in user home (auto-created)
Commands
| Command | Description | Docs |
|---|---|---|
keep now | Get or set current working intentions | KEEP-NOW.md |
keep put | Add or update a document | KEEP-PUT.md |
keep get | Retrieve item(s) by ID | KEEP-GET.md |
keep find | Search by meaning or text | KEEP-FIND.md |
keep list | List recent items, filter by tags | KEEP-LIST.md |
keep config | Show configuration and paths | KEEP-CONFIG.md |
keep move | Move versions into a named item (-t or --only required) | KEEP-MOVE.md |
keep reflect | Structured reflection practice | KEEP-NOW.md |
keep del | Remove item or revert to previous version | — |
keep tag-update | Add, update, or remove tags | TAGGING.md |
keep reindex | Rebuild search index | — |
Global Flags
keep --json <cmd> # Output as JSON
keep --ids <cmd> # Output only versioned IDs (for piping)
keep --full <cmd> # Output full YAML frontmatter
keep -v <cmd> # Enable debug logging to stderr
Output Formats
Three output formats, consistent across all commands:
Default: Summary Lines
One line per item: id date summary
%a1b2c3d4 2026-01-14 URI detection should use proper scheme validation...
file:///path/doc 2026-01-15 Document about authentication patterns...
With --ids: Versioned IDs Only
%a1b2c3d4@V{0}
file:///path/doc@V{0}
With --full: YAML Frontmatter
Full details with tags, similar items, and version navigation:
---
id: %a1b2c3d4
tags: {project: myapp, status: reviewed}
similar:
- %e5f6a7b8@V{0} (0.89) 2026-01-14 Related authentication...
meta/todo:
- %c9d0e1f2 Update auth docs for new flow
score: 0.823
prev:
- @V{1} 2026-01-14 Previous summary text...
---
Document summary here...
Note: keep get and keep now default to full format since they display a single item.
With --json: JSON Output
{"id": "...", "summary": "...", "tags": {...}, "score": 0.823}
Version numbers are offsets: @V{0} = current, @V{1} = previous, @V{2} = two versions ago.
Output width: Summaries are truncated to fit the terminal. When stdout is not a TTY (e.g., piped through hooks), output uses 200 columns for wider summaries.
Pipe Composition
keep --ids find "auth" | xargs keep get # Get full details for all matches
keep --ids list -n 5 | xargs keep get # Get details for recent items
keep --ids list --tag project=foo | xargs keep tag-update --tag status=done
keep --json --ids find "query" # JSON array: ["id@V{0}", ...]
# Version history composition
keep --ids now --history | xargs -I{} keep get "{}" # Get all versions
diff <(keep get doc:1) <(keep get "doc:1@V{1}") # Diff current vs previous
Quick CLI
# Current intentions
keep now # Show current intentions
keep now "What's important now" # Update intentions
keep reflect # Structured reflection practice
keep move "name" -t project=foo # Move matching versions from now
keep move "name" --only # Move just the current version
keep move "name" --from "source" -t X # Reorganize between items
# Add or update
keep put "inline text" -t topic=auth # Text mode
keep put file:///path/to/doc.pdf # URI mode
keep put "note" --suggest-tags # Show tag suggestions
# Retrieve
keep get ID # Current version
keep get ID -V 1 # Previous version
keep get ID --history # List all versions
# Search
keep find "query" # Semantic search
keep find "query" --text # Full-text search
keep find "query" --since P7D # Last 7 days
# List and filter
keep list # Recent items
keep list --tag project=myapp # Filter by tag
keep list --tags= # List all tag keys
# Modify
keep tag-update ID --tag key=value # Add/update tag
keep tag-update ID --remove key # Remove tag
keep del ID # Remove item or revert to previous version
# Maintenance
keep reindex # Rebuild search index
keep reindex -y # Skip confirmation
Python API
See PYTHON-API.md for complete Python API reference.
from keep import Keeper
kp = Keeper()
kp.remember("note", tags={"project": "myapp"})
results = kp.find("authentication", limit=5)
When to Use
put/update()— when referencing any file/URL worth rememberingput/remember()— capture conversation insights, decisions, notesfind— before searching filesystem; may already be indexedfind --since— filter to recent items when recency matters
Topics
- OUTPUT.md — How to read the frontmatter output
- TAGGING.md — Tags, speech acts, project/topic organization
- VERSIONING.md — Document versioning and history
- META-DOCS.md — Contextual feedback via meta sections
- SYSTEM-TAGS.md — Auto-managed system tags
More
- AGENT-GUIDE.md — Working session patterns
- QUICKSTART.md — Installation and setup
- PYTHON-API.md — Python API reference
- ARCHITECTURE.md — How it works under the hood
.domains— Domain organization patterns (keep get .domains).conversations— Conversation framework (keep get .conversations)
Python API
The CLI (keep put, keep find, etc.) is the primary interface. The Python API is for embedding keep into applications.
Installation
pip install keep-skill
See QUICKSTART.md for provider configuration (API keys or local models).
Quick Start
from keep import Keeper
kp = Keeper() # Uses ~/.keep/ by default
# Index documents
kp.update("file:///path/to/doc.md", tags={"project": "myapp"})
kp.update("https://example.com/page", tags={"topic": "reference"})
kp.remember("Important insight about auth patterns")
# Search
results = kp.find("authentication", limit=5)
for r in results:
print(f"[{r.score:.2f}] {r.id}: {r.summary}")
# Retrieve
item = kp.get("file:///path/to/doc.md")
print(item.summary)
print(item.tags)
API Reference
Imports
from keep import Keeper, Item
from keep.document_store import VersionInfo # for version history
Initialization
# Default store (~/.keep/)
kp = Keeper()
# Custom store location
kp = Keeper(store_path="/path/to/store")
Core Operations
Indexing
# Index from URI (file:// or https://)
kp.update(uri, tags={}, summary=None) → Item
# Index inline content
kp.remember(content, summary=None, tags={}) → Item
# Notes:
# - If summary provided, skips auto-summarization
# - remember() uses content verbatim if short (≤max_summary_length)
# - User tags (domain, topic, etc.) provide context for summarization
Search
# Semantic search
kp.find(query, limit=10, since=None) → list[Item]
# Find similar to a document
kp.find_similar(uri, limit=10, since=None) → list[Item]
# Similar items using stored embedding
kp.get_similar_for_display(id, limit=3) → list[Item]
# Tag-based query
kp.query_tag(key, value=None, since=None) → list[Item]
# Full-text search
kp.query_fulltext(query, since=None) → list[Item]
# Time filtering (all search methods support since)
kp.find("auth", since="P7D") # Last 7 days
kp.find("auth", since="P1W") # Last week
kp.find("auth", since="PT1H") # Last hour
kp.find("auth", since="2026-01-15") # Since date
Item Access
# Get by ID
kp.get(id) → Item | None
# Check existence
kp.exists(id) → bool
# List recent items
kp.list_recent(limit=10, sort="updated") → list[Item]
# sort options: "updated" (default), "created", "accessed"
# List collections
kp.list_collections() → list[str]
Tags
# Update tags only (no re-processing)
kp.tag(id, tags={}) → Item | None
# Delete tag by setting empty value
kp.tag("doc:1", {"obsolete": ""}) # Removes 'obsolete' tag
# List tag keys or values
kp.list_tags(key=None) → list[str]
kp.list_tags() # All tag keys
kp.list_tags("project") # All values for 'project' key
Version History
# Get previous version
kp.get_version(id, offset=1) → Item | None
# offset: 0=current, 1=previous, 2=two versions ago
# List all versions
kp.list_versions(id, limit=10) → list[VersionInfo]
# Get navigation metadata
kp.get_version_nav(id) → dict
# Returns: {"prev": [...], "next": [...]}
Current Intentions (Now)
# Get current intentions (auto-creates if missing)
kp.get_now() → Item
# Set current intentions
kp.set_now(content, tags={}) → Item
Deletion
# Delete item (reverts to previous version if history exists)
kp.delete(id) → Item | None
# Complete removal (if no history)
# If history exists, archives current version and restores previous
Data Types
Item
Fields:
id(str) — Document identifiersummary(str) — Human-readable summarytags(dict[str, str]) — Key-value tagsscore(float | None) — Similarity score (search results only)
Properties (from tags):
item.created→ datetime | Noneitem.updated→ datetime | Noneitem.accessed→ datetime | None
VersionInfo
Fields for version history listings:
version(int) — Version offset (0=current, 1=previous, etc.)summary(str) — Summary of this versionupdated(datetime | None) — When this version was created
Tags
Tag Merge Order
When indexing, tags merge in priority order (later wins):
- Existing tags (from previous version)
- Config tags (from
[tags]in keep.toml) - Environment tags (from
KEEP_TAG_*variables) - User tags (passed to
update(),remember(), ortag())
Tag Rules
- One value per key: Setting a tag overwrites existing value
- System tags (prefixed
_) cannot be set by users - Empty string deletes:
kp.tag(id, {"key": ""})removes the tag
Environment Tags
import os
os.environ["KEEP_TAG_PROJECT"] = "myapp"
os.environ["KEEP_TAG_OWNER"] = "alice"
kp.remember("note") # Auto-tagged with project=myapp, owner=alice
Config Tags
Add to keep.toml:
[tags]
project = "my-project"
owner = "alice"
Recommended Tags
See TAGGING.md for details on:
project— Bounded work contexttopic— Cross-project subject areaact— Speech-act category (commitment, request, assertion, etc.)status— Lifecycle state (open, fulfilled, declined, etc.)
System Tags (Read-Only)
Protected tags (prefix _) managed automatically:
_created— ISO timestamp_updated— ISO timestamp_updated_date— Date only (YYYY-MM-DD)_accessed— ISO timestamp_accessed_date— Date only (YYYY-MM-DD)_content_type— MIME type_source— Origin (inline, file, http)
Query system tags:
kp.query_tag("_updated_date", "2026-01-30")
kp.query_tag("_source", "inline")
See SYSTEM-TAGS.md for complete reference.
Version Identifiers
Append @V{N} to specify version by offset:
ID@V{0}— Current versionID@V{1}— Previous versionID@V{2}— Two versions ago
item = kp.get("doc:1@V{1}") # Get previous version
versions = kp.list_versions("doc:1")
Collections
Separate stores for different contexts:
import os
# Work context
os.environ["KEEP_COLLECTION"] = "work"
kp_work = Keeper()
kp_work.set_now("Working on auth")
# Personal context
os.environ["KEEP_COLLECTION"] = "personal"
kp_personal = Keeper()
kp_personal.set_now("Personal notes")
Collections are completely separate. For overlays within a single store, use tags instead.
Error Handling
from keep import Keeper
try:
kp = Keeper()
item = kp.get("nonexistent")
if item is None:
print("Item not found")
except Exception as e:
print(f"Error: {e}")
Common errors:
- Missing provider configuration (no API key or local models)
- Invalid URI format
- Embedding provider changes (auto-migrated, use
kp.reindex()if needed)
See Also
- QUICKSTART.md — Installation and setup
- REFERENCE.md — CLI reference
- AGENT-GUIDE.md — Working session patterns
- ARCHITECTURE.md — System internals
Architecture
What is keep?
keep is a reflective memory system providing persistent storage with vector similarity search. It's designed as an agent skill for OpenClaw and other agentic environments, enabling agents to remember information across sessions over time.
Think of it as: ChromaDB + embeddings + summarization + tagging wrapped in a simple API.
Published by Hugh Pyle, "inguz ᛜ outcomes", under the MIT license.
Contributions are welcome; code is conversation, "right speech" is encouraged.
Core Concept
Every stored item has:
- ID: URI or custom identifier
- Summary: Human-readable text (stored, searchable)
- Embedding: Vector representation (for semantic search)
- Tags: Key-value metadata (for filtering)
- Timestamps: Created/updated (auto-managed)
- Version History: Previous versions archived automatically on update
The original document content is not stored — only the summary and embedding.
Architecture
┌─────────────────────────────────────────────────────────────┐
│ API Layer (api.py) │
│ - Keeper class │
│ - High-level operations: update(), remember(), find() │
│ - Version management: get_version(), list_versions() │
└──────────────────┬──────────────────────────────────────────┘
│
┌──────────┼──────────┬──────────┬──────────┬───────────┐
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌────────┐ ┌─────────┐ ┌────────┐ ┌────────┐ ┌─────────┐ ┌─────────┐
│Document│ │Embedding│ │Summary │ │Media │ │Vector │ │Document │
│Provider│ │Provider │ │Provider│ │Descr.* │ │Store │ │Store │
└────────┘ └─────────┘ └────────┘ └────────┘ └─────────┘ └─────────┘
│ │ │ │ │ │
fetch() embed() summarize() describe() vectors/ summaries/
from URI text→vec text→summary media→text search versions
Components
api.py — Main facade
Keeperclass- Coordinates providers and store
- Implements query operations with recency decay
store.py — Vector persistence
ChromaStorewraps ChromaDB- Handles vector storage, similarity search, metadata queries
- Versioned embeddings:
{id}@v{version}for history
document_store.py — Document persistence
DocumentStorewraps SQLite- Stores summaries, tags, timestamps
- Version history: archives previous versions on update
providers/ — Pluggable services
- Document: Fetch content from URIs (file://, https://)
- Embedding: Generate vectors (sentence-transformers, OpenAI, Ollama, MLX)
- Summarization: Generate summaries (truncate, LLM-based)
- Registry: Factory for lazy-loading providers
config.py — Configuration
- Detects available providers (platform, API keys, Ollama)
- Persists choices in
keep.toml - Auto-creates on first use
types.py — Data model
Item: Immutable result type- System tag protection (prefix:
_)
Data Flow
Indexing: update(uri) or remember(content)
URI or content
│
▼
┌─────────────────┐
│ Fetch/Use input │ ← DocumentProvider (for URIs only)
└────────┬────────┘
│ raw bytes
▼
┌─────────────────┐
│ Content Regular-│ ← Extract text from HTML/PDF
│ ization │ (scripts/styles removed)
└────────┬────────┘
│ clean text
▼
┌─────────────────┐
│ Media Enrichment│ ← Optional: vision description (images)
│ (if configured) │ or transcription (audio) appended
└────────┬────────┘
│ enriched text
┌────┴────┬─────────────┐
│ │ │
▼ ▼ ▼
embed() summarize() tags (from args)
│ │ │
└────┬────┴─────────────┘
│
┌────┴────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ DocumentStore │ │ ChromaStore │
│ upsert() │ │ upsert() │
│ - summary │ │ - embedding │
│ - tags │ │ - summary │
│ - timestamps │ │ - tags │
│ - archive prev │ │ - version embed │
└─────────────────┘ └─────────────────┘
Versioning on update:
- DocumentStore archives current version before updating
- ChromaStore adds versioned embedding (
{id}@v{N}) if content changed - Same content (hash match) skips duplicate embedding
Retrieval: find(query)
query text
│
▼
embed() ← EmbeddingProvider
│
│ query vector
▼
┌───────────────────┐
│ ChromaStore │
│ query_embedding() │ ← L2 distance search
└─────────┬─────────┘
│
▼ results with distance scores
┌──────────────┐
│ Apply decay │ ← Recency weighting (ACT-R style)
│ score × 0.5^(days/half_life)
└──────┬───────┘
│
▼
list[Item] (sorted by effective score)
Delete / Revert: delete(id) or revert(id)
delete(id)
│
▼
version_count(id)
│
├── 0 versions → full delete from both stores
│
└── N versions → revert to previous
│
├─ get archived embedding from ChromaDB (id@vN)
├─ restore_latest_version() in DocumentStore
│ (promote latest version row to current, delete version row)
├─ upsert restored embedding as current in ChromaDB
└─ delete versioned entry (id@vN) from ChromaDB
Key Design Decisions
1. Schema as Data
- System configuration stored as documents in the store
- Enables agents to query and update behavior
- (Not yet implemented: routing, guidance documents)
2. Lazy Provider Loading
- Providers registered at first use, not import time
- Avoids crashes when optional dependencies missing
- Better error messages about what's needed
3. Separation of Concerns
- Store is provider-agnostic (only knows about vectors/metadata)
- Providers are store-agnostic (only know about text→vectors)
- Easy to swap implementations
4. No Original Content Storage
- Reduces storage size
- Forces meaningful summarization
- URIs can be re-fetched if needed
5. Immutable Items
Itemis frozen dataclass- Updates via
update()return new Item - Prevents accidental mutation bugs
6. System Tag Protection
- Tags prefixed with
_are system-managed - Source tags filtered before storage
- Prevents user override of timestamps, etc.
7. Document Versioning
- All documents retain history automatically on update
- Previous versions archived in SQLite
document_versionstable - Content-addressed IDs for text updates enable versioning via tag changes
- Embeddings stored for all versions (enables temporal search)
- No auto-pruning: history preserved indefinitely
8. Version-Based Addressing
- Versions addressed by offset from current: 0=current, 1=previous, 2=two ago
- CLI uses
@V{N}syntax for shell composition:keep get "doc:1@V{1}" - Display format (v0, v1, v2) matches retrieval offset (
-V 0,-V 1,-V 2) - Offset computation assumes
list_versions()returns newest-first ordering - Security: literal ID lookup before
@V{N}parsing prevents confusion attacks
Storage Layout
store_path/
├── keep.toml # Provider configuration
├── chroma/ # ChromaDB persistence (vectors + metadata)
│ └── [collection]/ # One collection = one namespace
│ ├── embeddings
│ ├── metadata
│ └── documents
├── document_store.db # SQLite store (summaries, tags, versions)
│ ├── documents # Current version of each document
│ └── document_versions # Archived previous versions
└── embedding_cache.db # SQLite cache for embeddings
Provider Types
Embedding Providers
Generate vector representations for semantic search.
- voyage: API-based, Anthropic's recommended partner (VOYAGE_API_KEY)
- openai: API-based, high quality (OPENAI_API_KEY)
- gemini: API-based, Google (GEMINI_API_KEY)
- ollama: Local server, auto-detected, any model (OLLAMA_HOST)
- sentence-transformers: Local, CPU/GPU, no API key
- MLX: Apple Silicon optimized, local, no API key
Dimension determined by model. Must be consistent across indexing and queries.
Summarization Providers
Generate human-readable summaries from content.
- anthropic: LLM-based, cost-effective option (ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN)
- openai: LLM-based, high quality (OPENAI_API_KEY)
- gemini: LLM-based, Google (GEMINI_API_KEY)
- ollama: LLM-based, local server, auto-detected (OLLAMA_HOST)
- MLX: LLM-based, local, no API key
- truncate: Simple text truncation (fallback)
- passthrough: Store content as-is (with length limit)
Contextual Summarization:
When documents have user tags (domain, topic, project, etc.), the summarizer
receives context from related items. This produces summaries that highlight
relevance to the tagged context rather than generic descriptions.
How it works:
- When processing pending summaries, the system checks for user tags
- Finds similar items that share any of those tags (OR-union)
- Boosts scores for items sharing multiple tags (+20% per additional match)
- Top 5 related summaries are passed as context to the LLM
- The summary reflects what's relevant to that context
Example: Indexing a medieval text with domain=practice produces a summary
highlighting its relevance to contemplative practice, not just "a 13th-century
guide for anchoresses."
Tag changes trigger re-summarization: When user tags are added, removed, or
changed on an existing document, it's re-queued for contextual summarization
even if content is unchanged. The existing summary is preserved until the new
one is ready.
Non-LLM providers (truncate, first_paragraph, passthrough) ignore context.
Document Providers
Fetch content from URIs with content regularization.
- composite: Handles file://, https:// (default)
- Extensible for s3://, gs://, etc.
Content Regularization:
- PDF: text extracted via pypdf
- HTML: text extracted via BeautifulSoup (scripts/styles removed)
- DOCX/PPTX: text + tables/slides extracted via python-docx/python-pptx; auto-tags: author, title
- Audio (MP3, FLAC, OGG, WAV, AIFF, M4A, WMA): metadata via tinytag; auto-tags: artist, album, genre, year
- Images (JPEG, PNG, TIFF, WEBP): EXIF metadata via Pillow; auto-tags: dimensions, camera, date
- Other formats: treated as plain text
Provider-extracted tags merge with user tags (user wins on collision). This ensures both embedding and summarization receive clean text.
Media Description Providers (optional)
Generate text descriptions from media files, enriching metadata-only content.
- mlx: Apple Silicon — vision (mlx-vlm) + audio transcription (mlx-whisper)
- ollama: Local server — vision models only (llava, moondream, bakllava)
Media description runs in Keeper.update() between fetch and upsert. Descriptions are appended to the metadata content before embedding/summarization, making media files semantically searchable by their visual or audio content.
Design points:
- Only triggered for non-text content types (image/, audio/)
- Lazy sub-provider loading: MLX composite only loads VLM for first image, whisper for first audio
- GPU-locked via
LockedMediaDescriber(same file-lock pattern as summarization) - Graceful degradation: errors never block indexing; no provider = metadata-only (unchanged behavior)
- Optional dependency:
pip install keep-skill[media]for MLX models
Extension Points
New Provider
- Implement Protocol from providers/base.py
- Register with
get_registry().register_X("name", YourClass) - Reference by name in config
New Store Backend
- Current: ChromaDB
- Future: Could extract Protocol from
ChromaStore - Candidates: PostgreSQL+pgvector, SQLite+faiss
New Query Types
- Add methods to
Keeper - Delegate to
ChromaStoreor implement in API layer
Performance Characteristics
Indexing
- Embedding: ~50-200ms per item (local models)
- Summarization: ~100ms-2s per item (depends on provider)
- Storage: ~10ms per item
Querying
- Semantic search: ~10-50ms for 10k items
- Tag queries: ~1-10ms
- Full-text search: ~10-100ms
Caching
- Embedding cache avoids re-computing for repeated queries
- Persists across sessions in SQLite
Scaling
- ChromaDB handles ~100k items comfortably
- Larger datasets may benefit from PostgreSQL backend
- Embedding dimension affects memory (384d vs 1536d)
Failure Modes
Missing Dependencies
- Registry provides clear error about which provider failed
- Lists available alternatives
- Lazy loading prevents import-time crashes
URI Fetch Failures
update()raisesIOErrorfor unreachable URIs- Original error preserved in exception chain
Invalid Config
- Config auto-created with detected defaults
- Validation on load with clear error messages
Store Inconsistency
- On startup, a background thread checks for mismatches between ChromaDB and SQLite
- Missing search index entries or orphaned vectors are reconciled automatically
- This runs as a daemon thread and does not block normal operation
Store Corruption
- ChromaDB is resilient (SQLite-backed)
- Embedding cache can be deleted and rebuilt
- No critical data loss if store is backed up
Testing Strategy
Unit Tests: tests/test_core.py
- Data types (Item, filtering)
- Context dataclasses
- No external dependencies
Document Store Tests: tests/test_document_store.py
- SQLite persistence
- Version history (archive, retrieval, navigation)
- Schema migration
Integration Tests: tests/test_integration.py
- End-to-end: remember → find
- Multiple collections
- Recency decay
- Embedding cache
Provider Tests: (TODO)
- Each provider independently
- Graceful degradation when unavailable
Future Work
Planned (in later/)
- Relationships: Link items with typed edges
- Advanced Tagging: LLM-based tag generation
- Hierarchical Context: Topic summaries, working context
Under Consideration
- Multi-store facade (private/shared routing)
- Batch operations for performance
- Incremental indexing (track changes)
- Export/import for backup
- Web UI for exploration
Agent Guide
Patterns for using the reflective memory store effectively in working sessions.
For the practice (why and when), see ../SKILL.md.
For CLI reference, see REFERENCE.md. Be sure you understand the output format — every item surfaces similar items and meta sections you can navigate with keep get.
The Practice
This guide assumes familiarity with the reflective practice in SKILL.md. The key points:
Reflect before acting: Check your current work context and intentions.
- What kind of conversation is this? (Action? Possibility? Clarification?)
- What do I already know?
keep now # Current intentions
keep find "this situation" # Prior knowledge
While acting: Is this leading to harm? If yes: give it up.
Reflect after acting: What happened? What did I learn?
keep put "what I learned" -t type=learning
Periodically: Run a full structured reflection:
keep reflect
This cycle — reflect, act, reflect — is the mirror teaching. Memory isn't storage; it's how you develop skillful judgment.
Working Session Pattern
Use the nowdoc as a scratchpad to track where you are in the work. This isn't enforced structure — it's a convention that helps you (and future agents) maintain perspective.
# 1. Starting work — check context and intentions
keep now # What am I working on?
# 2. Update context as work evolves (tag by project and topic)
keep now "Diagnosing flaky test in auth module" -t project=myapp -t topic=testing
keep now "Found timing issue" -t project=myapp
# 3. Check previous context if needed
keep now -V 1 # Previous version
keep now --history # List all versions
keep now -t project=myapp # Find recent now with project tag
# 4. Record learnings (cross-project knowledge uses topic only)
keep put "Flaky timing fix: mock time instead of real assertions" -t topic=testing -t type=learning
Key insight: The store remembers across sessions; working memory doesn't. When you resume, read context first. All updates create version history automatically.
Agent Handoff
Starting a session:
keep now # Current intentions with version history
keep now --history # How intentions evolved
keep find "recent work" --since P1D # Last 24 hours
Ending a session:
keep now "Completed OAuth2 flow. Token refresh working. Next: add tests." -t topic=auth
keep move "auth-string" -t project=myapp # Archive this string of work
Strings
As you work, keep now accumulates a string of versions — a trace of how intentions evolved. keep move lets you name and archive that string, making room for what's next. It requires -t (tag filter) or --only (tip only) to prevent accidental grab-all moves.
Snapshot before pivoting. When the conversation shifts topic, move what you have so far before moving on:
keep move "auth-string" -t project=myapp # Archive the auth string
keep now "Starting on database migration" # Fresh context for new work
Incremental archival. Move to the same name repeatedly — versions append, building a running log across sessions:
# Session 1
keep move "design-log" -t project=myapp
# Session 2 (more work on same project)
keep move "design-log" -t project=myapp # Appends new versions
End-of-session archive. When a string of work is complete:
keep move "auth-string" -t project=myapp
Tag-filtered extraction. When a session mixes multiple projects, extract just the string you want:
keep move "frontend-work" -t project=frontend # Leaves backend versions in now
The moved item is a full versioned document — browse with keep get name --history, navigate with -V 1, -V 2, etc.
Index Important Documents
Whenever you encounter documents important to the task, index them:
keep put "https://docs.example.com/auth" -t topic=auth -t project=myapp
keep put "file:///path/to/design.pdf" -t type=reference -t topic=architecture
Ask: what is this? Why is it important? Tag appropriately. Documents indexed during work become navigable knowledge.
Breakdowns as Learning
When the normal flow is interrupted — expected response doesn't come, ambiguity surfaces — an assumption has been revealed. First: complete the immediate conversation. Then record:
keep put "Assumed user wanted full rewrite. Actually: minimal patch." -t type=breakdown
Breakdowns are how agents learn.
Tracking Commitments
Use speech-act tags to make the commitment structure of work visible:
# Track promises
keep put "I'll fix the auth bug" -t act=commitment -t status=open -t project=myapp
# Track requests
keep put "Please review the PR" -t act=request -t status=open
# Query open work
keep list -t act=commitment -t status=open
# Close the loop
keep tag-update ID --tag status=fulfilled
See TAGGING.md for the full speech-act framework.
Data Model
An item has:
- A unique identifier (URI, content hash, or system ID)
- Timestamps (
_created,_updated) - A summary of the content
- Tags (
{key: value, ...}) - Version history (previous versions archived automatically)
The full original document is not stored. Summaries are contextual — tags shape how new items are understood. See KEEP-PUT.md.
System Documents
Bundled system docs provide patterns and conventions, accessible via keep get:
| ID | What it provides |
|---|---|
.domains | Domain-specific organization patterns |
.conversations | Conversation framework (action, possibility, clarification) |
.tag/act | Speech-act categories |
.tag/status | Lifecycle states |
.tag/project | Project tag conventions |
.tag/topic | Topic tag conventions |
See Also
- REFERENCE.md — Quick reference index
- OUTPUT.md — How to read the frontmatter output
- TAGGING.md — Tags, speech acts, project/topic
- VERSIONING.md — Document versioning
- QUICKSTART.md — Installation and setup
OpenClaw Integration
How to install and configure keep as an OpenClaw plugin.
Install keep
uv tool install keep-skill # API providers included
# Or: uv tool install 'keep-skill[local]' # Local models, no API keys needed
Install the Plugin
openclaw plugins install -l $(keep config openclaw-plugin)
openclaw plugins enable keep
openclaw gateway restart
This installs the lightweight plugin from keep's package data directory.
What Gets Installed
Protocol block — AGENTS.md in your OpenClaw workspace gets the keep protocol block appended automatically (on any keep command, if AGENTS.md exists in the current directory).
Plugin hooks:
| Hook | Event | What it does |
|---|---|---|
before_agent_start | Agent turn begins | Runs keep now -n 10, injects output as prepended context |
after_agent_stop | Agent turn ends | Runs keep now 'Session ended' to update intentions |
The agent starts each turn knowing its current intentions, similar items, open commitments, and recent learnings.
Reinstall / Upgrade
After upgrading keep, reinstall the plugin:
openclaw plugins install -l $(keep config openclaw-plugin)
openclaw gateway restart
The plugin source lives at $(keep config openclaw-plugin) — this resolves to the openclaw-plugin/ directory inside the installed keep package.
Optional: Daily Reflection Cron
For automatic deep reflection, create a cron job:
openclaw cron add \
--name keep-reflect \
--cron "0 21 * * *" \
--session isolated \
--system-event "Reflect on this day with \`keep reflect\`. Follow the practice."
This runs in an isolated session at 9pm daily. Delivery is silent — the value is in what gets written to the store.
Provider Configuration
keep auto-detects AI providers from environment variables. Set one and go:
export OPENAI_API_KEY=... # Simplest (handles both embeddings + summarization)
# Or: GEMINI_API_KEY=... # Also does both
# Or: VOYAGE_API_KEY=... and ANTHROPIC_API_KEY=... # Separate services
If Ollama is running locally, it's auto-detected with no configuration needed.
For local-only operation (no API keys): uv tool install 'keep-skill[local]'
See QUICKSTART.md for full provider options, model configuration, and troubleshooting.