README
¶
recoil
Fast local operational memory for long-running agentic workflows.
Agents do not manage memory. They author intent. Recoil handles freshness, review, and lifecycle hygiene around that intent.
recoil is a single Go binary backed by SQLite FTS5. It gives agents a sourced, lifecycle-aware memory store for bounded projects — no cloud, no daemon, no LLM in the required write path. Coding agents are the first wedge, but the core loop is broader: make the right local evidence cheaper to retrieve than guessing.
Use it when you need:
- An agent-facing memory CLI that survives session boundaries, with deterministic writes and sourced provenance on every retrieved chunk.
- A way to capture durable project decisions (ADRs, preferences, constraints) and let stale ones be demoted rather than deleted.
- A local search layer over your project's markdown — README, docs, design notes — with hash-tracked freshness so changed files supersede old chunks.
- A practical continuity layer for non-coding projects too: research, writing, planning, customer/account context, and other long-running bounded work.
Contents
- Documentation
- Install
- Quick Start
- Why recoil
- Authorship Principle
- Commands at a Glance
- How It Works
- Lifecycle Model
- Mining Project Files
- Agent Hooks
- Eval Harness
- Profiles And MCP
- Embeddings and Hybrid Retrieval
- Status
- License
Documentation
MANUAL.md is the complete manual in one file — install, every command with its flags, the memory model, and agent wiring. It is what recoil.sh renders, so the published manual and the one in your clone are the same document. If you are an agent that just cloned this repo, read that file, then AGENTS.md for the code layout.
| For | Start here |
|---|---|
| Everyone, offline or online | MANUAL.md · recoil.sh |
| Operators / CLI users | Quick Start · Commands at a Glance |
| AI agents / integrations | Agent Hooks · Profiles And MCP |
| Contributors | AGENTS.md · CONTRIBUTING.md · CHANGELOG.md |
| Maintainers | RELEASING.md |
Install
Prebuilt binaries are attached to every release, with a checksums.txt covering each archive. FTS5 is compiled in.
macOS / Linux — one line. It picks the archive for your platform, verifies it against the release's checksums.txt, and installs recoil into /usr/local/bin or ~/.local/bin:
curl -fsSL https://recoil.sh/install | sh
Pin a release or choose the directory with sh -s -- --version v0.1.1 --bin-dir ~/bin. The script is install.sh in this repo.
By hand — download the archive for your platform from the releases page, verify it, and put recoil on your PATH:
TAG=$(curl -fsSL https://api.github.com/repos/1broseidon/recoil/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4)
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m); [ "$ARCH" = "aarch64" ] && ARCH=arm64
curl -fsSLO "https://github.com/1broseidon/recoil/releases/download/${TAG}/recoil_${TAG}_${OS}_${ARCH}.tar.gz"
curl -fsSLO "https://github.com/1broseidon/recoil/releases/download/${TAG}/checksums.txt"
grep "recoil_${TAG}_${OS}_${ARCH}.tar.gz" checksums.txt | shasum -a 256 -c -
tar xzf "recoil_${TAG}_${OS}_${ARCH}.tar.gz" && install -m755 recoil ~/.local/bin/recoil
Windows:
irm https://recoil.sh/install.ps1 | iex
To uninstall, run https://recoil.sh/uninstall.ps1 the same way. It keeps your memories by default; pass -Purge to also delete the database.
Go (requires CGO for SQLite FTS5):
CGO_CFLAGS="-DSQLITE_ENABLE_FTS5" go install github.com/1broseidon/recoil@latest
From source:
git clone https://github.com/1broseidon/recoil
cd recoil
make build # produces ./recoil
make install # installs to $GOPATH/bin
The Makefile sets CGO_ENABLED=1 and CGO_CFLAGS+=-DSQLITE_ENABLE_FTS5
automatically. FTS5 is required — there is no degraded fallback.
Quick Start
Initialize recoil inside a project:
cd my-project
recoil setup
recoil instruct codex
Wake an agent session with bounded layered context:
recoil wake --max-chars 1600
Record durable context during work, then close the session with a handoff:
recoil remember --agent codex \
"Use net/http with a 5s default timeout, not a third-party client."
recoil handoff --agent codex \
--decision "Kept net/http for the HTTP client." \
--next-step "Add timeout tests around gateway calls."
recoil decide --claim-key dependency.http-client \
"We use net/http with a 5s default timeout, not a third-party client."
recoil decide --claim-key dependency.cache.redis \
--stance rejects \
--subject Redis \
--holds-while "ops cost remains unjustified at current scale" \
--recheck "Has scale or cost picture changed?" \
"Decided against Redis for cache."
Search before assuming prior context:
recoil search "why http client"
recoil search "auth" --role adr --current
recoil check "add Redis for this cache"
recoil list --claim-key dependency.http-client --current
Mine project markdown so it becomes searchable with provenance:
recoil mine --dry-run
recoil mine
When something changes, supersede the old memory rather than deleting it:
recoil supersede mem_abc... \
"We switched to a 10s timeout after the gateway slowdown on 2026-04-12."
Why recoil
Coding agents have a recall problem at the moments where remembering would save the most time: after compaction, at session boundaries, when switching branches, when picking work back up days later. The usual fix is to dump context into a fresh conversation; the usual cost is paraphrase-drift, fabric- ated history, and slow startups.
recoil solves a narrower problem cheaply:
- Single primitive. A Go binary you can run from any shell, an agent hook, or a CI job. No service, no daemon, no API key.
- Sourced answers. Every retrieved chunk carries
source_path,source_ref, file hash, and lifecycle state. Agents can show where evidence came from. - Lifecycle, not deletion. Old decisions are superseded with links, not removed. Retrieval demotes stale guidance rather than burying it.
- Eval-gated retrieval. Ranking and filter behavior are pinned by a fixture suite that runs offline.
- Agent-readable by default. Output is frontmatter-plus-content;
--jsonuses a stable{version, kind, data}envelope;--minimalgives TSV rows.
If you've used cymbal for code navigation, recoil sits in the same lane for project memory: a fast, deterministic local CLI primitive that agents call instead of guessing.
Authorship Principle
Recoil draws a hard line between authorship and automation. The contract that defines what automation may and may not do is three lines:
Durable memory is authored by an agent or operator action. Automation may enrich, connect, publish, and review it. Automation may propose new memory, but proposals go to Review unless explicitly accepted.
This is what keeps the write path trustworthy. No LLM ever silently
summarizes a transcript into durable project memory. Mining harvests sourced
text from files you control. recoil remember / decide / handoff are
explicit authorship signals. Mining and refresh move authored memory
around — they do not invent it.
Commands at a Glance
# Bootstrap
recoil setup
recoil instruct codex
recoil init
recoil status
recoil config
# Write
recoil remember "Auth tokens live in the keyring" --agent codex
recoil handoff --agent codex --next-step "Verify auth token migration."
recoil add "Prefers vim keybindings"
recoil add "Auth tokens live in the keyring" --agent codex --role decision
recoil decide --claim-key auth.token-storage "Auth tokens live in the keyring"
recoil decide --claim-key dependency.cache.redis --validity rejected \
--holds-while "ops cost remains unjustified at current scale" \
--recheck "Has scale or cost picture changed?" \
"Decided against Redis for cache."
recoil decide --claim-key dependency.cache.redis --stance rejects --subject Redis \
"Redis remains rejected for this cache while ops cost is unjustified."
# Read
recoil search "why did we change auth"
recoil check "add Redis for this cache"
recoil search "what is current auth" --profiles auto
recoil search "dependency.sqlite-driver"
recoil wake --max-chars 1600
recoil wake --include-decisions
recoil list --role adr --validity active
recoil list --claim-key auth.token-storage --current
recoil show <memory-id>
# Lifecycle
recoil mark <memory-id> --validity rejected
recoil supersede <old-memory-id> "Replacement memory text"
recoil forget <memory-id> # tombstone (recoverable)
recoil forget <memory-id> --destroy
# Mining
recoil mine --dry-run
recoil mine docs/
recoil mine session-evidence --dry-run
# Profiles and MCP
recoil profile --entity "Caroline"
recoil mcp
# Session evidence (opt-in)
recoil config set session-evidence.enabled true
recoil session-evidence ingest --agent codex --session-id sess_a8f3 --file transcript.json
recoil session-evidence list
recoil session-evidence forget sess_a8f3
# Agent integration
recoil instruct claude-code
recoil hook remind
recoil hook install claude-code
recoil hook install opencode
recoil hook install codex
# Optional sidecars
recoil embed index
recoil embed search "background sync"
# Resilience
recoil backup
recoil backup --out /path/to/synced-folder --max 5
# Quality gates
recoil eval
recoil eval eval/fixtures.jsonl
recoil eval --suite workflows
recoil eval --suite cli-hard
recoil eval --suite workflows --out eval/results
recoil repair
Result-producing commands support --json for programmatic use; long-running
server/tray commands keep their runtime logs on stderr/stdout as plain text.
Scan commands also support --minimal for tab-separated rows.
How It Works
Storage. SQLite with FTS5 through github.com/mattn/go-sqlite3 (CGO).
WAL, busy timeout, foreign keys on, deterministic public IDs, deterministic
redaction before hashing/persistence. The core local memory path has no daemon:
the binary opens the DB, does its work, and exits.
Scope. Three explicit scopes:
| Scope | Selection | Use for |
|---|---|---|
| project | default (inside an initialized project) | Project-specific decisions, ADRs, notes |
| user | --user |
Cross-project preferences and personal facts |
| session | --session <id> |
One-session scratch memory |
Inside an initialized project (.recoil/project.json present), the common
path needs no flags: recoil wake, recoil search "...", recoil remember "...".
If the current directory is not inside an initialized project, commands warn
on stderr and use a local fallback scope.
Workflow shape. Coding projects usually store dependency decisions, ADRs, handoff notes, and mined project docs. Non-coding projects use the same primitive for bounded work: research preferences, writing constraints, customer/account context, travel planning, health/provider facts, or other durable local evidence. Recoil is not trying to become a general personal knowledge base; it works best when the scope is explicit and the evidence is useful for the next agent session.
Retrieval. search uses FTS5 over content plus role, claim_key, source
agent, source path, and source_ref, with conservative ranking boosts for
exact metadata matches and decision-like roles. Filters: --role,
--claim-key, --validity, --current, --historical, --since,
--before, --source-kind, --source, --agent. wake self-refreshes
changed tracked project files before returning context, then both wake and
search group results into product lanes: Current Decisions, Project Docs,
Recent Evidence, and Historical. Each result includes a short
why line explaining why it surfaced.
Output. Default is agent-readable frontmatter (the fields an agent needs
to cite) followed by content. --json returns a stable envelope:
{ "version": "0.1", "kind": "search_result", "data": { ... } }
When --json is active, command errors are emitted on stderr as:
{ "version": "0.1", "kind": "error", "error": { "code": "VALIDATION", "message": "..." } }
--minimal returns TSV rows for shell pipelines.
Exit codes. 0 success, 1 generic error, 2 validation, 3 not found,
4 upstream/database/provider failure, 5 failed precondition, 6 cancelled.
Lifecycle Model
The hard problem with memory isn't storing it — it's making sure stale evidence doesn't masquerade as current truth. recoil's answer is structured lifecycle on every memory:
validity:active,historical,rejected,superseded,stale, orunknown.claim_key: a stable handle for a claim family (e.g.dependency.sqlite-driver,auth.token-storage). Multiple memories can share a key over time; one is current.supersedes/superseded_by: explicit links between an old memory and its replacement.
search partitions results: active and unknown memories surface as current
guidance; rejected, superseded, stale, and historical matches appear in a
labeled history section. wake excludes historical states entirely so
startup context never presents old evidence as current.
Decision memories can carry optional predicates that explain when a decision
applies. Date-bound decisions use --valid-until <date> and are stored as the
canonical predicate kind valid_until. Recoil evaluates a narrow deterministic
set itself (valid_until, source_unchanged, claim_key_status,
package_version, tsconfig_value, and lint_config_value) and stores
semantic or external predicates as review prompts. Unknown predicate status is
not a warning by itself; recoil check only asks for review when the current
request maps to a rejected/superseded decision, a broken deterministic
predicate, multiple current decisions, or an external predicate that gates the
action.
Current decisions can also carry a stance and subject. That lets recoil check
distinguish "use Redis" from "avoid Redis" against the same remembered
decision:
recoil decide --claim-key dependency.cache.redis --stance rejects --subject Redis \
"Reject Redis for this cache because ops cost is not justified."
recoil check "use Redis for this cache"
recoil mark <memory-id> --stance rejects --subject Redis
recoil check "add Redis for this cache"
recoil check --claim-key dependency.cache.redis
recoil wake --include-decisions
# Mark a memory as rejected — keep the evidence, demote it
recoil mark mem_abc... --validity rejected
# Or supersede with a replacement and link both sides
recoil supersede mem_abc... "We switched back to net/http on 2026-04-12."
Old evidence is not automatically bad. It becomes dangerous when it looks like current truth. recoil preserves history; retrieval makes the current answer unmistakable.
Mining Project Files
recoil mine ingests project markdown and text files as sourced memory
chunks with source_path and source_ref line ranges. It skips hidden and
tooling directories (.git, .recoil, node_modules, vendor) by default,
and honors a .recoilignore file with gitignore-shaped patterns for
per-project skips.
recoil mine --dry-run
recoil mine
recoil mine docs/
Mined memories track file hashes in the sources table. Re-mining a changed
file stales chunks that are no longer present; project-wide re-mines stale
chunks for deleted tracked files. Provenance is preserved so an agent can
quote the source line range directly.
For directories that should not enter memory (test fixtures, vendored docs,
generated content), add them to .recoilignore. For directories that should
be visible-but-not-ingested (the agent should know the directory exists, but
its contents are not project guidance), declare a single index memory by
hand:
recoil add --role note --claim-key source.fixtures.eval-corpora \
--validity active \
"eval/corpora/ contains synthetic stress fixtures — not project guidance."
Session Evidence
Session Evidence is selected, redacted evidence from an agent session. It is not transcript hoarding: recoil keeps compact load-bearing slices such as user directives, explicit choices, rejected paths, completion summaries, and handoff notes. Tool payloads, chatter, and unconfirmed assistant speculation are skipped.
It is opt-in per project:
recoil config set session-evidence.enabled true
Runtime adapters can pipe transcript payloads into:
recoil session-evidence ingest --agent codex --session-id sess_a8f3 --file -
Supported hook installers also add a session-end capture command where the runtime exposes an end-of-session payload:
recoil hook install claude-code
recoil hook install codex
Ingest redacts before writing anything to disk, writes compact JSONL under the
user-local recoil state directory (the OS config dir, not the repo), and mines
those records with source_kind set to session_evidence, role: source,
and validity: unknown. Derived search helper traces are stored as non-guidance
role: trace. Evidence keeps session, native session id, branch (when known),
timestamp, and turn provenance, but it is a receipt of what a previous session
said/did — never current truth or durable guidance. Explicit decisions, docs,
and current user instructions outrank it. wake and structured search route
session evidence to Recent Evidence and cap it so recent sessions cannot crowd
out durable guidance. Bulk backfill excludes personal_fact evidence unless
--include-personal-facts is passed.
Operators can inspect or purge a session:
recoil session-evidence list
recoil session-evidence show sess_a8f3
recoil session-evidence forget sess_a8f3
forget for session evidence is a privacy purge: it removes the compact
evidence file and hard-deletes the mined memories for that session.
Agent Hooks
recoil hook remind prints a short memory primer that agent runtimes can
inject at session start. It is intentionally reminder-only: recoil does not
intercept tools or replace shell usage.
First-class installers are available for the agents recoil can manage safely:
recoil hook install claude-code # ~/.claude/settings.json
recoil hook install opencode # <user-config-dir>/opencode/plugins/
recoil hook install codex # ~/.codex/hooks.json
# Project-scoped variants
recoil hook install claude-code --scope project
recoil hook install opencode --scope project
recoil hook install codex --scope project
# Marked AGENTS.md fallback for Codex builds without native hook support
recoil hook install codex-agents
Installers are idempotent and uninstall removes only recoil-owned content, leaving unrelated hooks intact.
Eval Harness
recoil eval seeds a temporary database from eval/fixtures.jsonl, runs the
fixture cases through the same store and wake-layer code as the CLI, and
reports recall@k, MRR, empty-result accuracy, stale-demotion failures,
wake-safety failures, wrong-memory failures, scope-isolation failures, and
ranked-content failures, and latency.
recoil eval
recoil eval eval/fixtures.jsonl
recoil eval --suite embeddings --retrieval hybrid
recoil eval --suite workflows
recoil eval --suite session-evidence
recoil eval --suite decisions
recoil eval --suite workflows --out eval/results
The default eval is the fast deterministic gate. Workflow suites exercise
mined docs and selected session evidence for coding and non-coding continuity.
External academic benchmarks stay under bench/; large real-repo stress stays
under stress/.
The consolidation plan lives in docs/P_SERIES.md: product
workflow gates belong in recoil eval, academic comparisons stay in bench/,
and large real-repo stress stays in stress/.
Profiles And MCP
recoil profile --entity NAME builds a deterministic entity_profile memory
from current sourced evidence. recoil search uses profile retrieval in
--profiles auto mode by default, and skips profiles for exact-detail queries
such as commands, quotes, stack traces, or line numbers. Use
--profiles on|off to override that router.
recoil mcp runs a stdio Model Context Protocol server using the official
github.com/modelcontextprotocol/go-sdk/mcp Go SDK. It exposes
recoil_search, recoil_wake, and recoil_check tools read-only by default.
Those tools return text content plus structured {version, kind, data} MCP
content matching the CLI JSON envelope. Start with recoil mcp --allow-write
to expose recoil_add, recoil_remember, and recoil_handoff.
Embeddings and Hybrid Retrieval
Embeddings are an opt-in retrieval layer that fuses with FTS5 rather than replacing it. The core experience still runs through SQLite FTS5, structured metadata, lifecycle filters, and source freshness — embeddings just add a semantic side channel for paraphrase-heavy queries where keyword match alone falls short.
Two providers ship today:
local-hash-v1— deterministic local hash-based "embedding," no network calls, useful for schema/plumbing tests and offline use.openrouter— real embeddings via OpenRouter (default model:openai/text-embedding-3-small). RequiresOPENROUTER_API_KEY.
# Index with real embeddings
export OPENROUTER_API_KEY=sk-or-v1-...
recoil embed index --provider openrouter --model openai/text-embedding-3-small
# Hybrid search: FTS5 + cosine similarity, fused with Reciprocal Rank Fusion
recoil search "how do we store credentials" --hybrid
# Semantic-only search
recoil embed search "background remote sync" --provider openrouter
recoil search --hybrid is the production path: it runs FTS5 and embedding
similarity in parallel, fuses with RRF (k=60), and returns the top results.
All standard filters (--role, --claim-key, --validity, --current,
etc.) work identically on both legs. The bench harness measures the
retrieval-only impact in bench/RESULTS.md (R@5 0.972 → 0.981 on
LongMemEval_S, with the biggest gain on multi-evidence questions).
eval/embeddings.jsonl is intentionally separate from the default gate.
It contains paraphrase-heavy cases where plain FTS is expected to struggle,
so semantic and hybrid retrieval can be measured without weakening the core
FTS baseline.
Status
recoil is at v0. The shape is settled and the core CLI is stable enough to build agent workflows on, but expect rough edges and surface-level changes before v1.
Stable today:
- Storage, deterministic IDs, redaction, FTS5
- Scope model and project init
add/search/wake/list/show/forgetmark/supersede/decidelifecycle commands- Decision relevance/verdict checks (
recoil check) and decision-aware wake context - Project file mining with freshness via file hashes
.recoilignoreper-project skip patterns- Agent hook installers (Claude Code, OpenCode, Codex)
- Eval harness (14/14 on the bundled fixtures, MRR 1.0)
- Workflow eval suite for coding, non-coding, session-evidence continuity, decision relevance, and P3 verdict maturity
- Deterministic entity profiles (
recoil profile) with profile-aware search - Read-only MCP stdio server (
recoil mcp) - JSON envelope (
{version, kind, data}) - Agent contract and hooks (
recoil instruct,recoil hook)
Explicit non-goals for v0:
- No cloud sync in the core local memory path
- No daemon in the core local memory path
- No hosted dashboard
- No embeddings in the required path
- No LLM-based extraction in the default write path
- No automatic rewriting of older memories
Experimental:
- Optional embeddings sidecar (
recoil embed) - Eval-driven retrieval modes (
fts,semantic,hybrid)
License
MIT. See LICENSE.
Documentation
¶
There is no documentation for this package.
Directories
¶
| Path | Synopsis |
|---|---|
|
bench/main.go — External benchmark harness for recoil.
|
bench/main.go — External benchmark harness for recoil. |
|
internal
|
|
|
agentsessions
Package agentsessions discovers and reads on-disk session transcripts from other coding agents (Claude Code, Codex, ...) so their durable evidence can be backfilled into recoil's session-evidence tier.
|
Package agentsessions discovers and reads on-disk session transcripts from other coding agents (Claude Code, Codex, ...) so their durable evidence can be backfilled into recoil's session-evidence tier. |
|
stress is a non-benchmark stress-test harness for recoil.
|
stress is a non-benchmark stress-test harness for recoil. |