- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| docs/superpowers | ||
| reflect | ||
| systemd | ||
| tests | ||
| .gitignore | ||
| .python-version | ||
| AGENTS.md | ||
| icon.png | ||
| icon.svg | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| reflect.toml | ||
| uv.lock | ||
Reflect
Autonomous nightly extraction of "lessons learned" from Opencode session history, served to future sessions via an MCP server. Sessions are required to judge every lesson they read as useful or not useful, and that judgement — not mere retrieval — is what raises or lowers a lesson's strength. Lessons climb to core memories when repeatedly judged useful and decay when never needed again.
Cost note: Reflect deliberately spends tokens to build and maintain memory — the nightly worker reads session transcripts and an LLM summarizes them, and future sessions can consume lessons. Only run it where token cost is low, e.g. with local LLMs (like llama.cpp) rather than a metered hosted model.
Prerequisites
Setup
Pick the section that matches your deployment. All three share the first two steps (clone + config); they diverge at step 3.
1. Single machine (local)
Everything runs on one computer. The nightly worker reads that machine's opencode.db and
writes to a local lessons.db. Future opencode sessions on the same machine read lessons back
via a local MCP server.
# 1. Clone and install
git clone <repo-url> reflect
cd reflect
uv sync # installs dependencies into .venv
# 2. Configure
cp reflect.toml ~/.config/reflect/reflect.toml
# (edit: nothing mode-specific needed; defaults are local)
# 3. Register the read-only MCP server so opencode sessions can query lessons
opencode mcp add reflect -- uv run --project <path-to-reflect> reflect mcp
# 4. Enable the nightly timer
systemctl --user link <path-to-reflect>/systemd/reflect-nightly.service
systemctl --user link <path-to-reflect>/systemd/reflect-nightly.timer
systemctl --user enable --now reflect-nightly.timer
# 5. Verify a run end-to-end before trusting it
uv run reflect run --dry-run
uv run reflect run
2. Hub
The hub owns the shared lessons.db and serves two MCP servers over HTTP so client machines
can connect. The hub also runs the nightly extraction for its own sessions.
# 1. Clone and install (same as local)
git clone <repo-url> reflect
cd reflect
uv sync
# 2. Configure — set mode = "hub"
cp reflect.toml ~/.config/reflect/reflect.toml
# Edit ~/.config/reflect/reflect.toml:
# mode = "hub"
# (optionally set mcp_host to a non-loopback address if clients are remote)
# 3. Register the hub's own MCP server for this machine's opencode sessions
opencode mcp add reflect --url http://127.0.0.1:4010/mcp
# 4. Enable the nightly timer (extracts lessons from the hub's own sessions)
systemctl --user link <path-to-reflect>/systemd/reflect-nightly.service
systemctl --user link <path-to-reflect>/systemd/reflect-nightly.timer
systemctl --user enable --now reflect-nightly.timer
# 5. Start the two MCP servers that clients connect to
# (edit the %h paths in the .service files to match your layout first)
systemctl --user link <path-to-reflect>/systemd/reflect-mcp-read.service
systemctl --user link <path-to-reflect>/systemd/reflect-mcp-worker.service
systemctl --user enable --now reflect-mcp-read.service reflect-mcp-worker.service
The hub's read server listens on mcp_host:mcp_port (default 0.0.0.0:4010).
The worker server listens on mcp_host:mcp_worker_port (default 0.0.0.0:4011).
Clients connect to the worker URL for writes and the read URL for queries.
The hub must be on a trusted network — there is no authentication.
3. Client
A client (spoke) has no local lessons.db. It reads its own opencode.db, runs the nightly
worker against the hub's worker MCP server, and registers the hub's read server so its own
opencode sessions can query the shared lessons.
# 1. Clone and install (same as local)
git clone <repo-url> reflect
cd reflect
uv sync
# 2. Configure — set mode = "client" and point at the hub
cp reflect.toml ~/.config/reflect/reflect.toml
# Edit ~/.config/reflect/reflect.toml:
# mode = "client"
# hub_url = "http://<hub-hostname>:4011/mcp"
# 3. Register the hub's read server for this machine's opencode sessions
opencode mcp add reflect --url http://<hub-hostname>:4010/mcp
# 4. Enable the nightly timer (runs the extraction driver against the hub)
systemctl --user link <path-to-reflect>/systemd/reflect-nightly.service
systemctl --user link <path-to-reflect>/systemd/reflect-nightly.timer
systemctl --user enable --now reflect-nightly.timer
# 5. Verify
uv run reflect run --dry-run
uv run reflect run
Clients do not need the MCP systemd services — the hub serves those. Clients only need the
nightly timer (which drives the extraction) and the opencode mcp add entry (so sessions can read
lessons).
Configuration
Config is a TOML file. Resolution order (first hit wins):
--config <path>passed toreflect run|mcp|lifecycle|statusREFLECT_CONFIGenvironment variable (path to a file)~/.config/reflect/reflect.tomlif it exists- Built-in defaults
Every option lives under a [core] section — flat top-level keys are silently ignored. To
create your config, copy the shipped sample and uncomment what you need:
cp reflect.toml ~/.config/reflect/reflect.toml # then edit
Invalid or unknown keys are ignored and fall back to defaults.
| Key | Default | Purpose |
|---|---|---|
opencode_db |
~/.local/share/opencode/opencode.db |
session history (read-only) |
lessons_db |
~/.local/share/reflect/lessons.db |
lesson store (created on first run; clients ignore this) |
worker_model |
"" |
model used by the nightly agent (empty = opencode's default) |
worker_agent |
reflect-worker |
reserved agent name (self-excluded from extraction) |
exclude_agents |
["reflect-worker"] |
agents never processed |
disabled_projects |
[] |
project names hidden from search |
core_threshold |
80.0 |
strength at which a lesson becomes a core memory |
archive_threshold |
15.0 |
strength below which a lesson is archived |
base_strength |
30.0 |
strength of a newly created lesson |
decay_per_day |
1.0 |
daily strength decay |
reoccurrence_bonus |
10.0 |
strength gain when a lesson re-occurs |
retrieval_bump |
1.0 |
strength gain on each search retrieval (stopgap so retrieved lessons survive until judged) |
useful_bonus |
10.0 |
strength gain when an agent judges a lesson useful (rate_lessons) |
not_useful_penalty |
15.0 |
strength loss when an agent judges a lesson not useful (rate_lessons) |
rescan_interval_days |
30 |
re-review older sessions this often |
rescan_quota |
20 |
max old sessions re-reviewed per night |
max_sessions_per_run |
0 (0 = unlimited) |
per-night cap on sessions processed; the shipped sample sets 50 to drain the first-run backlog gradually |
batch_size |
15 |
sessions processed per LLM run (each run starts with a fresh context) |
consolidate_interval_nights |
7 |
run the consolidation pass (merge duplicates, supersede outdated lessons) every N nights |
purge_ttl_days |
90 |
hard-delete archived lessons after this long |
run_timeout_s |
3600 |
nightly agent timeout |
transcript_max_chars |
12000 |
per-session transcript budget fed to the agent |
mode |
"local" |
"local" (single machine), "hub" (local + serve the MCP over HTTP), or "client" (spoke that writes to a remote hub) |
hub_url |
"" |
hub worker MCP URL for mode = "client", e.g. http://<hub-hostname>:4011/mcp |
mcp_host |
0.0.0.0 |
bind host for the reflect mcp streamable-http transport |
mcp_port |
4010 |
default HTTP port for the read MCP server |
mcp_worker_port |
4011 |
default HTTP port for the worker MCP server (reflect mcp --worker) |
workdir, opencode_bin, uv_bin, and path_extra default to this machine's paths; adjust
them if you run reflect on a different machine.
Sizing the token budget (rule of thumb). Sessions are processed in batches of batch_size,
and every batch runs in a fresh LLM context (the driver launches one short agent run per
batch, so context never accumulates across a night — no compaction, no overflow). To pick a
batch_size that fits your model's window, roughly one batch can hold
batch_size ≈ (context tokens × 0.85) ÷ (transcript_max_chars ÷ 4 + ~750)
tokens per session before overflowing — a token is about 4 characters, the 0.85 reserves
~15% of the window for the prompt, tool definitions, and output, and the ~750 accounts for
the agent's own reasoning per session. For a 140K-token model with transcript_max_chars = 12000 that is (140000 × 0.85) ÷ (12000 ÷ 4 + 750) ≈ 32 sessions per batch; the shipped
default of 15 is a safe choice. If a batch aborts with "request exceeds the available
context size", lower transcript_max_chars (each session costs transcript_max_chars ÷ 4
tokens) or lower batch_size, rather than raising max_sessions_per_run — smaller batches
keep each run within the window. run_timeout_s applies to each batch run.
Commands
uv run reflect run— run the nightly pipeline once (worklist → LLM agent → verify → lifecycle).--config <path>is optional and only needed to point at a config other than the default (~/.config/reflect/reflect.toml). Inmode = "client"this drives the spoke against the hub instead.uv run reflect run --dry-run— print the session worklist without running the agentuv run reflect mcp— MCP server (read-only tools;--workeradds extraction tools).--transport stdio|streamable-http,--host, and--portselect the transport; the HTTP defaults come frommcp_host/mcp_port/mcp_worker_port.uv run reflect lifecycle— apply decay/archive/purge onlyuv run reflect status— recent run historyuv run reflect stats [--days N]— usefulness dashboard (retrievals, judgements, reoccurrences, coverage, per-project)
status,stats, andlifecycleoperate on the hub'slessons.db; running them on a client returns an error.
Architecture
┌─────────────────────────────────────────────────────────────┐
│ HUB machine │
│ │
│ nightly timer ──> reflect run (extracts lessons) │
│ ──> writes to local lessons.db │
│ │
│ reflect mcp (port 4010) ──> read-only lessons for anyone │
│ reflect mcp --worker (port 4011) ──> write tools for clients│
└─────────────────────────────────────────────────────────────┘
▲ ▲
│ hub_url (worker) │ opencode mcp add (read)
│ │ (localhost on hub)
┌──────────┴──────────┐ ┌───────────┴──────────────┐
│ Client machine 1 │ │ Client machine 2 │
│ │ │ │
│ nightly timer ──> │ │ nightly timer ──> │
│ reflect run │ │ reflect run │
│ (writes via hub) │ │ (writes via hub) │
│ │ │ │
│ opencode mcp add ───┘ │ opencode mcp add │
│ (reads from hub) │ (reads from hub) │
└───────────────────────┘ └──────────────────────────┘
On each machine the lesson decay/purge lifecycle is claimed at most once per UTC day across all
machines, gated by the hub's shared lessons.db: whichever reflect run or finish_run call
runs first that day applies it, and the rest skip — so spokes and the hub's own night never
double-run it.
Notes
- Reflect sessions are excluded from extraction (agent
reflect-workerand descendants). opencode.dbis read-only; all writes go tolessons.db.- Search is FTS5 keyword search; no embeddings.
- The nightly worker is confined to
reflect_*MCP tools only; all other tools (bash, edit, webfetch, …) are denied via a deny-by-default agent permission allowlist. max_sessions_per_runcaps how many sessions a single night processes (built-in default0= no cap). Sessions not processed stay in the worklist and are picked up the next night. The shippedreflect.tomloverrides this to50as a safety valve: the first run faces the entire history of past sessions as one huge backlog, so the cap drains it gradually instead of in a single enormous run.
License
This project is released to the public domain under the CC0 1.0 Universal license. See the LICENSE file for the full legal text.