recoil

command module
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 22, 2026 License: MIT Imports: 2 Imported by: 0

README

recoil

GitHub Stars Go Reference Latest Release

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

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; --json uses a stable {version, kind, data} envelope; --minimal gives 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, or unknown.
  • 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). Requires OPENROUTER_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 / forget
  • mark / supersede / decide lifecycle commands
  • Decision relevance/verdict checks (recoil check) and decision-aware wake context
  • Project file mining with freshness via file hashes
  • .recoilignore per-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

The Go Gopher

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL