Hindsight Memory for OpenCode, Cursor, and Other Coding Agents

Project memory that survives between sessions.

Page content

Every coding agent session starts cold. Last Tuesday’s naming rule and bug walk-through were in the chat, and the chat is gone. The next session starts blank, and you explain them again.

Commands below match @vectorize-io/hindsight-coding-agents 0.8.4, published on 30/10/2026, which depends on @vectorize-io/hindsight-all ^0.10.2. Package behaviour, Cloud rates, and Memory Defense on this page were checked on 03/11/2026. Other ways to keep facts across sessions are on the AI Systems Memory pages.

Home self-hosted evnvironment with desk, laptop, server rack and french door

A coding agent can read the repository, run the tests, and change the code. It still cannot see a decision that never landed in the file it has open. Say the export bug was investigated last year: ordinary CSV quoting was tried and rolled back because the receiving ERP strips leading zeros, and the only format that works is the Excel text-formula form.

Hindsight’s coding-agents documentation is aimed at that kind of decision, one that lives in a commit message or an earlier chat. The plugin copies recent commit messages into a memory bank, writes the session back as you work, and keeps five pages so the next session can start from them. OpenCode, the Cursor CLI, Claude Code, and the other tools the package lists share that bank for one repository.

What the coding-agents plugin is

An older install should keep talking to the server you already chose. hindsight-claude-code, hindsight-cursor-cli, and hindsight-codex are the packages this one replaces. If ~/.hindsight/claude-code.json or ~/.hindsight/codex.json is still on disk, install copies that file’s API URL and token, and an empty URL still means the local daemon. The tool you are installing is read first, so a Codex install keeps Codex’s server even when an old Claude file is sitting beside it. Cursor CLI, Copilot CLI, OpenCode, and Cline never wrote an endpoint file, so those installs have no server to copy.

Old recall and retain settings, bankMission, retainMission, and dynamicBankGranularity stay behind. The coding-agents documentation says they describe a pipeline this package replaced, and reinterpreting them would be guesswork. What you get instead is one bank per repository, coding-agent::{gitProject}, shared by every tool that opens that checkout. To give each tool its own bank again, set the template the documentation says matches the old names:

{ "bankIdTemplate": "{harness}::{gitProject}" }

Running npx @vectorize-io/hindsight-coding-agents install with no tool name prints the list and writes nothing. install all is the command that wires every detected tool. update replaces the copy under ~/.hindsight/coding-agents and leaves each tool’s own config file alone. autoUpdate does that replacement in the background once a day; a release that adds a new hook still needs install, because the background update does not edit those config files. If Hindsight is one option among several backends, the agent memory provider comparison lines it up against Honcho, Mem0, and Mnemosyne.

Choose where memory lives

The bank has to live somewhere before a session can store a decision. On a terminal, install asks once: Hindsight Cloud, a server you already run, or a daemon on the same computer as the coding agent. A script passes --server. A later install does not ask again, and it leaves a server you already named alone, unless you pass --server to change it.

Mode What runs What you supply What leaves the machine
cloud Hindsight Cloud. Default API base https://api.hindsight.vectorize.io An API token Prompts, git text the plugin retains, and extracted memories, sent to that API
self-hosted A Hindsight server you already run Its URL, conventionally http://localhost:8888 Sent to that URL. A localhost URL stays on the machine
daemon A local hindsight-embed the plugin starts at 127.0.0.1:9077 uv on PATH, and an LLM for fact extraction Bank data stays on the machine. Extraction still calls whichever LLM the daemon is given
npx @vectorize-io/hindsight-coding-agents install opencode --server daemon
npx @vectorize-io/hindsight-coding-agents install opencode --server self-hosted --api-url http://localhost:8888
npx @vectorize-io/hindsight-coding-agents install opencode --server cloud --api-token YOUR_TOKEN
flowchart LR A[Coding agent] --> P[coding-agents plugin] P --> B["Bank coding-agent::{gitProject}"] P --> M{serverMode} M --> C[Hindsight Cloud] M --> S[Self-hosted API :8888] M --> D["hindsight-embed 127.0.0.1:9077"]

The daemon binds 127.0.0.1:9077 and leaves port 8888 for a Hindsight server you run yourself. If something is already listening on 9077, the plugin uses that process for every coding tool and every repository, and closing a session leaves it running. The first start downloads hindsight-embed and loads models in the background, because a hook is not allowed to wait that long. Until the port answers, that session has no memory for a turn or two, the same way an unreachable Cloud or self-hosted server leaves a session without memory.

Linux and Windows install litellm, which the API pulls in, from wheels. macOS compiles it, because there is no macOS wheel, and the compile needs a current Rust toolchain from rustup. Turning a transcript into memories still needs a chat model: HINDSIGHT_API_LLM_PROVIDER if you set it, otherwise the first key among OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, and GROQ_API_KEY, otherwise the Claude Code CLI. Any HINDSIGHT_API_* variable is passed through to the daemon. The embedding model, the reranker, a local OpenAI-compatible server, and a bank backup are in Hindsight for Hermes Agent. With serverMode set to self-hosted, --api-url is that server’s base URL.

Vectorize’s pricing page describes the self-hosted edition as MIT-licensed, with no seat fee and no token fee. Cloud bills each operation. The rates are in the last section.

Wire OpenCode

OpenCode has to be installed before the plugin can attach to it. The OpenCode quickstart covers that install. Then point the plugin at the daemon:

npx @vectorize-io/hindsight-coding-agents install opencode --server daemon

The installer writes one plugin path into the user file ~/.config/opencode/opencode.json, aimed at ~/.hindsight/coding-agents. That is separate from a project opencode.jsonc. OpenCode loads hindsight_* tools from the path, and it does not need an MCP server. OpenCode 2 (npm @opencode/cli, install target opencode2) reads the same file. Installing either CLI wires both, and uninstalling either removes the entry. OpenCode v1 rejects the file if it contains v2’s plugins key, so the installer writes a single path and each CLI loads its own entry point from the package.

On OpenCode 2 the read-only survey of the repo runs under some other installed tool, because a v2 plugin cannot define the read-only agent that survey needs, and the “seeded” notice goes to the plugin log because a v2 plugin cannot raise a toast. Recall, the hindsight_* tools, and writing the session back work as they do on v1.

OpenCode reads ~/.hindsight/coding-agent.json when the workspace loads, so a change to that file applies after you restart OpenCode. A new apiToken is the exception on every tool: the client reads it again when the server rejects a request, so rotating the key does not need that restart.

To see whether the bank actually kept a decision, use two sessions in a repository you are willing to store:

  1. Start OpenCode there and state one disposable decision, such as a public error code you do not want changed.
  2. End the session. OpenCode writes the transcript during the session and flushes the last reply when it goes idle.
  3. Start a new session in the same directory and ask for that code, and for where it was decided.
  4. Ask the agent to call hindsight_sync_status. "synced": true means the seeded memory can be queried. The same result reports how fresh the git copy is, and how many survey documents have landed (surveyDocs, from 0 to 4).

If the second session has no memory of the code, look in ~/.hindsight/coding-agents-logs/. plugin.log is the readable trail, diag.jsonl records each reflect and page fetch, and usage.jsonl records which hindsight_* tools the turn called. npx @vectorize-io/hindsight-coding-agents stats summarises that usage file per agent. When a reflect times out or returns a 5xx, diag.jsonl records a search of the knowledge pages and, if no page matches, a plain recall of observations. The agent still answers. If those fallbacks fail too, that turn has nothing from the bank in front of it.

Wire Cursor and the other agents

Cursor joins the same bank through its CLI. The installer writes hooks and MCP under ~/.cursor:

npx @vectorize-io/hindsight-coding-agents install cursor-cli --server daemon

That writes hooks to ~/.cursor/hooks.json, an MCP server to ~/.cursor/mcp.json, and a companion skill. Each hook is a short-lived process, so an edit to coding-agent.json applies on the next prompt. The MCP process reads the file when it starts, so the hindsight_* tools see the edit in the next session.

Claude Code gets the same hook-and-MCP wiring in Claude’s files. install claude-code adds three hooks to ~/.claude/settings.json, registers MCP for your user through claude mcp add, and installs ~/.claude/skills/hindsight-coding-agent. That skill is what Claude reads when you tell it to store something in Hindsight. Codex CLI (install codex) writes three hooks to ~/.codex/hooks.json and an [mcp_servers] block to config.toml, and those hooks run only when codex_hooks is true.

Tool Install target Wiring How the agent reaches memory
OpenCode opencode ~/.config/opencode/opencode.json Native tools, no MCP
OpenCode 2 opencode2 Same file as OpenCode Native tools, no MCP
Cursor CLI cursor-cli ~/.cursor/hooks.json, ~/.cursor/mcp.json, companion skill Hooks plus MCP
Claude Code claude-code Three hooks in ~/.claude/settings.json, user-scope MCP, skill under ~/.claude/skills/ Hooks plus MCP
Codex CLI codex ~/.codex/hooks.json, [mcp_servers] in config.toml Hooks plus MCP
pi pi Extension in ~/.pi/agent/settings.json, skill under ~/.pi/agent/skills Native tools, no MCP

Kilo, Copilot CLI, Cline, Devin CLI, and the other names on the coding-agents page use the same install <name> command, and they share the bank of whatever repository you open. Devin CLI will not install on Node older than 22.5, because its hooks read the session database through node:sqlite. pi and Prime Agent have to use that install command too. pi install npm:@vectorize-io/hindsight-coding-agents is left unwired: both tools read one pi key in package.json, and that key can point at only one entry.

To keep Claude Code from writing into the bank while OpenCode still does, set disabled for that tool:

{
  "serverMode": "daemon",
  "harnesses": {
    "claude-code": { "disabled": true }
  }
}

Claude Code then stores nothing, recalls nothing, and receives no Hindsight tools. retainSessions set to false, for every tool or for one of them, stops saving transcripts and stops importing old ones. Recall, git history, and the memory tools keep working, so you can still ask about what is already stored.

Per-repo banks and knowledge pages

OpenCode and the Cursor CLI on one checkout should remember the same project. The bank name that does that is coding-agent::{gitProject}. {gitProject} is the directory name of the main git checkout, from git rev-parse --git-common-dir, so a linked worktree uses the same bank as that checkout. A folder that is not a git repository takes the name of the directory where the session started.

A folder that should use a different bank goes in mapPathToBank. You give an absolute path, the longest matching path wins, and listing the repo root covers every subdirectory under it. That map overrides everything else, including a fixed bankId. Set bankId when every directory you open should write into one bank you named. dynamicBankId set to false keeps resolution on that static bank instead of bankIdTemplate.

If the agent cds into a directory that belongs to another bank, turns from then on are stored in the new bank. Turns already saved stay where they were written. Each move is a warning in plugin.log.

The first time you open a repository, that bank has none of the project’s past decisions. The session copies recent commit messages in, which is how a choice that exists only in git becomes something the next session can recall. The copy is on by default (autoSeed). A full write of that document keeps the newest commits, between half of seedLimit and seedLimit (300). The window moves in steps of half, and the log says how many older commits it dropped.

gitIngest chooses how much of that git history is stored, both for the first copy and for commits you make later. "message" stores commit messages and updates the document when HEAD contains a commit the document lacks. "full" also stores each commit’s diff, newest first. "none" leaves git out. A later commit adds its own message. The whole document is written again, still inside that seedLimit window, when the saved commit is no longer an ancestor of HEAD, when more than seedLimit commits are new, or when the server cannot append safely.

Commit messages do not show how the code is arranged now, so the same first session can also walk the tree. That walk is codebaseSurvey, on by default. The coding tool you are using runs it when its CLI can host a read-only agent; otherwise another installed tool runs it.

On Claude Code the walk is claude -p, and surveyBudgetUsd (default 2) stops the spend. Other tools use the model they are already pointed at, inside their own read-only sandbox. After 20 new commits (surveyRefreshCommits) the walk runs again. Set that to 0 and it runs only on a bank that has never been surveyed.

Note: The walk starts a separate coding agent, such as headless OpenCode. Those calls go to that agent’s model, so HINDSIGHT_API_LLM_MAX_CONCURRENT set to "1" does not slow them down. When the model is a self-hosted server already answering your session, the survey is a second full pass on the same server. Set codebaseSurvey to false when the server cannot take it.

The next session starts from five pages instead of rereading every stored fact. Each page is synthesized from one knowledge tier, so it reads only the facts the extractor routed to that tier.

Page What it answers Tag
Component map Main components, their responsibilities, and how they depend on each other knowledge:component
Core concepts Domain abstractions and the vocabulary a developer has to know knowledge:concept
Conventions and patterns How this project tests, handles errors, names things, structures code, and makes changes knowledge:convention
Key decisions and rationale Significant technical decisions and why the project does it that way knowledge:decision
Initiatives and enhancements Major initiatives over time, with links to captured initiative pages knowledge:feature-work

A shared minute would fire all five pages at once, on the same worker pool that stores new memories, and a session ingesting then waits behind that pile. The default schedule spreads them. pageTriggerCron is H * * * *: each page gets its own minute, hashed from the bank id and the page name, and a tick with nothing new is skipped. pageTriggerType set to auto-refresh rewrites a page after every consolidation that produced new material. The documentation calls that the most current schedule and the most expensive, one model synthesis per page per consolidation. manual leaves a page as it is until you refresh it.

A built-in page you do not want updated is false under pages. The copy already stored stays until you delete it in the control plane. source_query replaces the question that page asks, and the plugin still appends the clause that keeps a dependency’s decisions off the page. A page of your own goes under customPages and needs its own source_query. An unknown name under pages is ignored, with a warning in plugin.log, so a typo does not create a second empty page. Asking the agent to capture an initiative writes a separate page, one per initiative, through hindsight_capture_initiative.

Deleting the bank on the server clears that repository’s memory. The next session copies git history and walks the tree again. Uninstalling the plugin leaves the bank where it is.

Scope and privacy controls

Opening a repository creates a bank for it, which is how a client tree can be stored without you asking. optInOnly limits memory to directories you name. Everywhere else the plugin creates no bank, stores nothing, and copies no git history, and the coding tool behaves as if the plugin were not installed.

{
  "optInOnly": true,
  "optInPaths": ["~/prj", "~/oss"]
}

Each entry is a directory prefix, ~ expands, and each repository under it still gets its own coding-agent::{gitProject} bank. A path in mapPathToBank is also allowed, because you already named that folder. A lone bankId does not allow anything: it names a bank, not a folder, so with optInOnly on, unnamed directories stay out. The plugin reads no config from the repository. The documentation’s reason is that a cloned repo must not be able to turn memory on. Which folder uses which bank stays in mapPathToBank. Which tool is on or off stays in harnesses. Once the bank id is known, further rules for that one repository go in banks:

{
  "banks": {
    "coding-agent::secret-client": { "disabled": true }
  }
}

npx @vectorize-io/hindsight-coding-agents uninstall cursor-cli removes the Cursor hooks, MCP entry, and skill the installer added. uninstall all does that for every tool it wired. A file that already existed is copied to a .hindsight-backup beside it before the installer edits it. The bank, the daemon’s database, and ~/.hindsight/coding-agent.json stay.

A token pasted into a chat should not become a memory. Memory Defense is a 45-pattern scan of text before it is stored, and it stays off until that bank has a policy. redact replaces a match with a marker such as [REDACTED:github_token] and stores the rest. block drops the matching item and returns 422 when every item in the request matched. The policy applies to later stores on that bank. Memories already stored are left as they are. Those two actions are the open-source sensitive_data rule.

Who is allowed to turn the scan on is written differently in two places. The 03/09/2026 enterprise post says the basic scanner is on all plans, and that the extra detectors — a catalog of 220 or more patterns, base64 re-scanning, prompt-injection checks, and an LLM screen — are Enterprise. The pricing comparison marks Memory Defense as Enterprise only, with the self-hosted and Cloud cells empty. Check that row on the current pricing page before you count on a Cloud bank to strip secrets.

manageBankConfig defaults to true, which lets the plugin write the bank settings its ingestion needs, and any fields you put in defaultBankConfig. It writes a field only where the bank does not already define one, so a value you set in the control plane stays. The extraction mode on the plugin’s own strategies is the exception: it follows retainExtractionMode on every session. retainContext, sent with every session write-back, tells extraction who said the sentence. The default names both speakers, and asks for the agent’s own “fixed” or “passes” to be stored as the agent’s claim rather than as fact. When a conclusion is stored as if someone had observed it, the ways that feed the next session are in Self-Reinforcing Memory Loops.

Settings for day-to-day development

Every coding tool reads the same file, ~/.hindsight/coding-agent.json, so a limit you set once applies to OpenCode and to Cursor. HINDSIGHT_CONFIG moves that file. It does not create a second one. For each setting, a built-in default loses to an environment variable (HINDSIGHT_API_URL, HINDSIGHT_API_TOKEN, or HINDSIGHT_<FIELD> for other single values), which loses to the top of the file, then to harnesses.<name>, then to paths.<prefix>, then to banks.<id>. A value written in the file stays when you later set the same environment variable. retainTags and optInPaths can be a comma-separated variable. mapPathToBank, harnesses, paths, banks, retainMetadata, and recallOptions are read only from the file.

When a session is slow, expensive, or remembering the wrong repository, the change is one of these fields. The defaults are from the coding-agents reference.

Field Default When to change it
serverMode cloud daemon for a local bank, self-hosted for a server you run
autoInject "reflect" "pages" or "recall" skip the session-start synthesis. "none" injects nothing; the agent searches pages and reflects when they are too shallow
reflectTimeoutMs 20000 The automatic session reflect. On tools that use hooks, the installer sets a 30 second prompt-hook timeout, so a value past about 20 seconds also needs that hook’s timeout raised
reflectBudget "high" Budget for the agent-called hindsight_reflect tool. The automatic session reflect always uses "low" and ignores this
recallOptions observations, low budget, max_tokens 2000 Merged key by key. A bank with consolidation disabled grows no observations, and the default recall comes back empty — set "types": ["world", "experience"] there
retainExtractionMode "concise" "verbose" pulls more detail. The reference says that costs several times the tokens, because every Stop writes the session back. "chunks" stores text with no extraction
retainTags / retainMetadata unset On a shared bank, stamp project:{gitProject} so recalls can filter. The bank id already answers “which repo” when each repo has its own bank
bankIdTemplate coding-agent::{gitProject} {harness}::{gitProject} splits agents. mapPathToBank sends a directory tree at one named bank
codebaseSurvey true false when the survey and your session share one self-hosted model. surveyBudgetUsd (default 2) caps the Claude recipe only
pageTriggerType "cron" "auto-refresh" follows every consolidation. "manual" leaves pages where they are
maxParallelRetains 10 Lower it if the API returns 429. The reference says the API rate-limits bursts

A local daemon that remembers only repositories under ~/prj, stores commit messages, leaves the repo survey off, and asks new banks to redact secrets looks like this:

{
  "serverMode": "daemon",
  "optInOnly": true,
  "optInPaths": ["~/prj"],
  "gitIngest": "message",
  // codebaseSurvey starts another coding agent. HINDSIGHT_API_LLM_MAX_CONCURRENT does not limit it.
  "codebaseSurvey": false,
  "retainExtractionMode": "concise",
  "autoInject": "reflect",
  "defaultBankConfig": {
    "memory_defense": {
      "enabled": true,
      "rules": [{ "on": "sensitive_data", "action": "redact" }]
    }
  }
}

defaultBankConfig fills memory_defense only on a bank that does not already have it, and it does nothing when manageBankConfig is false. hindsight_diagnose shows the file as it is now and the values the running client actually loaded.

What it costs, and when a file is enough

A session can spend three things as it starts: a low-budget reflect on the first prompt, a copy of recent commit messages capped by seedLimit, and the tree walk. With retainSessions left on, the transcript is stored as the session goes. The five pages refresh on their own minute of the hour when something changed. gitIngest set to "full" stores diffs along with messages.

Vectorize’s pricing FAQ lists Cloud rates with no fixed monthly fee and no seat price. The FAQ says new accounts receive free credits and does not publish the credit amount.

Operation Price
Retain $10.00 per million tokens
Recall $0.75 per million tokens
Reflect $0.05 per call
Iris Extract $7.50 per million tokens
Mental Model Retrieve $0.25 per million tokens
Mental Model Refresh $0.05 per call
Storage after 30 days $0.25 per million tokens per month

Memories are free to store for the first 30 days. The Cloud overview says creating or refreshing knowledge pages is billed per call, and the FAQ does not publish a separate knowledge-page line. A daemon or a self-hosted server does not incur these Cloud rates. Extraction still spends whatever the daemon’s LLM costs, including a cloud API key if that is the key install found.

The same documentation page reports an AMB sdebench run: 61 repository tasks, three runs each, with and without memory. Every run solved 60–61 of the 61 either way. What moved is corrections per task and cost per task. The page says the correction figures reproduce labels AMB prints on its own chart, and that cost is derived from the same per-run table.

Agent Corrections per task Cost per task
Claude Code 0.85 → 0.36 $0.445 → $0.338
Codex CLI 1.34 → 0.47 $0.577 → $0.276
opencode 1.20 → 0.80 $0.634 → $0.553

No memory is the left-hand number. Those three agents are the whole table the page prints.

A rule you want reviewed next to the code belongs in AGENTS.md or CLAUDE.md. That file is in the repository when the daemon is down, and saving it does not store the chat. The bank carries a decision that was never written there and that the next session keeps getting wrong. On Hermes, a local SQLite store with its own retention switches is Mnemosyne.

Sources

  • Coding Agents — install targets, coding-agent.json fields, bank naming, knowledge pages, daemon mode, and the sdebench table. npm package @vectorize-io/hindsight-coding-agents 0.8.4, published 30/10/2026.
  • Pricing — Cloud operation rates, the free-to-self-host claim, and the plan matrix row for Memory Defense.
  • Memory Defense — per-bank sensitive_data policy, redact and block, 45 patterns, off until a bank policy is set. Existing memories stay unscanned.
  • Enterprise features, 03/09/2026 — basic scanner described as all plans; extra detectors described as Enterprise.

Subscribe

Get new posts on AI systems, Infrastructure, and AI engineering.