Skip to content

Repository files navigation

Codegrave

License: MIT

Your coding agent forgets yesterday's mistakes. Codegrave turns every bug, failed fix, and hard-won lesson into a rule enforced on every future edit.


Yesterday your agent broke your streaming parser by calling json.loads() on a partial chunk. Tomorrow it tries the exact same fix. Codegrave blocks the edit before it happens:

Agent proposes:  json.loads(chunk) in stream.py

Codegrave intercepts:
  🚫  CODEGRAVE PREFLIGHT
  β†’ [BUG] Partial JSON parse crash
  β†’ Wrong fix attempted: json.loads per chunk
  β†’ Lesson: Always buffer streaming JSON before parsing

Agent adapts:  accumulated buffer + json.loads

Works with the leading coding agents

  • Cursor
  • Claude Code
  • GitHub Copilot CLI
  • Codex CLI
  • OpenCode
  • Antigravity
  • Cline
  • Windsurf

The outcome: your project builds a permanent memory of bugs, failed fixes, and engineering lessons that every future agent inherits -- across any IDE, any model, any day.

What changes

Without Codegrave With Codegrave
Agent repeats known mistakes Blocked before editing
Each new model starts from zero Inherits the project's full history
Review catches the same regressions Reviews focus on new problems
Lessons live in your head Lessons live in the codebase

Quick start

./please build           # PyInstaller -> dist/codegrave (lean binary)
./please deploy opencode # Wire into OpenCode (or cursor / claude / antigravity / codex / copilot / cline / windsurf)

Done. Zero setup. Codegrave defaults to agent mode -- your coding agent does the risk analysis using its own LLM. Costs tokens, works immediately.

Already running Ollama? Codegrave detects it at startup and offers to switch (shared weights, zero extra RAM).

How it works

Codegrave integrates with your coding agent to perform a preflight check before code is modified:

  1. PREFLIGHT
     query_memory() before every edit.
     If the approach matches a known regression, the edit is blocked.

  2. BUILD & VERIFY
     Write code, run tests, confirm it works.

  3. POSTFLIGHT
     Agent records what happened (symptoms, root cause, wrong fixes, lesson).
     New records are stored as *pending* -- invisible until a human approves them via `codegrave --review`.
     This prevents hallucinated lessons from poisoning the rule base.

  4. WRAP-UP
     Once per turn (not per file): scans this session's changed files for secrets/
     hardcoded host paths, kills dev servers/daemons left running under the project,
     and runs the test suite. Report-only on secrets -- nothing is auto-edited.

PREFLIGHT hook capability by platform

The PREFLIGHT gate is enforced by a small hook script per IDE (vendors/hook_contract.py defines one shared control-flow skeleton; vendors/hook_strategies.py supplies one HookStrategy per platform -- see the module docstring there for the full per-platform writeup). What each one can actually do is bounded by what that platform's hook system hands it, not by a codegrave design choice:

Platform Blocks writes at all New-file exemption
Claude Code Yes Yes -- tool_input.file_path is documented for Edit/Write/MultiEdit
Codex CLI Yes Yes -- apply_patch's patch header says Add File vs Update/Delete File
OpenCode Yes, per-file Yes -- plugin has full filePath visibility
Cursor Only other codegrave tool calls No
Antigravity Only other codegrave tool calls, most likely No
GitHub Copilot CLI Only other codegrave tool calls (previously: nothing at all -- see below) No

Cursor's own docs (cursor.com/docs/agent/hooks) state that beforeMCPExecution "fires exclusively for external MCP tool calls, not Cursor's built-in file editing tools" -- and codegrave's hook is registered with matcher "codegrave", narrowing that further to just this server's own tools. It never sees a native Edit/Write call's arguments, so there's no file path to check and no new-file exemption to give it. Antigravity's "call_mcp_tool" matcher appears (per independent sources; Antigravity's own docs page didn't render enough to confirm directly) to work the same way, with native file operations routed through separate matchers instead. Both platforms already support this for their own agent surfaces -- Claude Code and OpenCode do -- so if either exposes native file-edit arguments through a hook in the future, only that platform's HookStrategy needs to change; the shared contract already supports per-file target paths.

Copilot CLI is a different story: building this table surfaced that its hook had three compounding bugs (wrong input field name, an unverified guess at its tool names, and the wrong response schema) that meant it never blocked a single write in real use, on any version. That's now fixed -- Copilot CLI is gated the same conservative way as Cursor/Antigravity going forward, since its actual tool names still aren't documented.

Review

Every record lands in a pending queue. Nothing enters the searchable memory until you review it:

$ codegrave --review

opens a small Tkinter window with two tabs. Tkinter specifically because it's stdlib -- zero extra dependency, no compiled extension to bundle, nothing that can fail to build on someone else's machine (the same class of problem local's llama-cpp-python dependency causes, see below). That's a real tradeoff against how it looks, made deliberately in favor of staying lean rather than pulling in a GUI framework for what's fundamentally a review queue.

Unlike the MCP server, this standalone window has no coding agent attached to fall back on. If [llm]/[embedding] are still on the default local provider and the GGUF models/llama-cpp-python aren't available, it looks for llama-server on $PATH and offers to load a GGUF you pick with it (one process per role -- embedding and chat can't share a single llama-server instance). Decline or no llama-server found: falls back to agent for that session, same graceful degradation the MCP path already does.

Pending Records -- select one or more and:

  • Approve / Reject -- the usual per-record decision.
  • Consolidate -- select two or more records a human recognizes as duplicates or the same underlying issue, click Consolidate, and get a draft canonical record (merged fields, not a compressed summary -- every distinct symptom/wrong-fix from every source is kept) to review and edit before saving. Saving inserts one new approved record with derived_from pointing at the source ids, and removes the sources. Agents create records faster than a human can review them one at a time; this lets a human clear a whole duplicate cluster in two clicks instead of five separate approvals.

Consolidation Proposals -- merges proposed by an agent from inside a live editor session (see stage_consolidation() below), reviewed offline one at a time: the first proposal is selected automatically, β—€ Prev / Next β–Ά step through the rest (disabled at either end of the list), and ⌘/Ctrl+1 opens Review & Commit for the current proposal while ⌘/Ctrl+2 discards it -- either way the next proposal in line is selected automatically so you can keep triaging without touching the mouse.

The draft comes from the configured [llm] provider (local/openai/anthropic, or any Ollama-compatible endpoint via base_url). With provider = "agent" there's no model attached to the standalone window -- the dialog instead starts from a plain concatenation of the selected records for you to edit by hand. (Merging inside an agentic editor session, where the agent itself is attached and does the reasoning, is a different path.)

Set [review] mode = "auto" in codegrave.toml to skip review.

None of this is tied to Tkinter specifically -- the whole review workflow is just reads and writes against a handful of plain functions in database.py (get_pending_records, get_consolidation_proposals, update_record, insert_record, delete_record, insert_consolidation, delete_consolidation_proposal) over an ordinary SQLite file. If you'd rather review records in a web UI, a native app, or anything else, nothing stops you from building one against that same layer -- review_gui.py is the reference implementation, not the only allowed one.

Configuration (optional)

Create a codegrave.toml to switch providers or customize behavior:

# Agent mode is the default. All of this is optional.

[embedding]
provider = "agent"   # agent | local | openai

[llm]
provider = "agent"   # agent | local | openai | anthropic

[review]
mode = "manual"      # "manual" (review via --review) | "auto"
Provider Cost Setup
agent (default) Tokens Nothing
openai / anthropic Tokens + API API key
local (GGUF) RAM (~2.6 GB) pip install codegrave[local]
Ollama Nothing Auto-detected at startup

See CODEGRAVE.md for the full TOML schema, API key resolution, and embedding dimension configuration.

please is the project runner

Commands

./please build               # PyInstaller (lean: agent/cloud/Ollama)
./please build --local       # PyInstaller (bundles llama-cpp for offline)
./please test                # pytest (197 tests, ML mocked)

./please install opencode    # OpenCode
./please install cursor      # Cursor
./please install claude      # Claude Code
./please install antigravity # Antigravity
./please install codex       # Codex CLI
./please install copilot     # GitHub Copilot CLI
./please install cline       # Cline
./please install windsurf    # Windsurf

codegrave --review           # Review pending records

please (repo root, marked executable) is a stdlib-only Python script that handles every build, test, and deploy task. Zero additional toolchain -- if you have Python, you can build, test, and deploy. Same uv commands underneath, pretty-printed with plain ANSI codes (no rich, no colorama):

Run ./please with no arguments for a pretty-printed command listing. Windows can't execute a shebang line -- use python please <task> instead.

MCP tools

Tool Purpose
query_memory(code, approach, file_path) Risk analysis against project history
record_memory(type, title, symptoms, root_cause, wrong_fixes, resolution, lesson) Capture a lesson
pending_records() What's awaiting review
update_memory(id, ...) Edit a record (re-embeds automatically)
delete_memory(id) Remove a record
export_to_markdown() Write CODEGRAVE.md
synthesize_invariants() Batch synthesis across the whole memory corpus β€” see below
stage_consolidation(source_ids, ..., rationale) Propose merging duplicate records the user just approved in chat β€” see below
session_wrapup(test_command="") End-of-turn gate: scan changed files for secrets/hardcoded paths, kill dev servers, run tests β€” see below

Batch synthesis

query_memory reasons about one task against the records it retrieves. synthesize_invariants() is different: it reads every approved record at once and looks for patterns that only exist across records β€” a recurring root cause nobody's written down as its own invariant, or a file that keeps showing up in unrelated BUG/GOTCHA entries and is quietly the most brittle part of the codebase.

That's a much heavier reasoning load than a single risk check, so this tool refuses to run on the local provider and requires openai, anthropic, or agent instead. Nothing is written to memory automatically β€” review the output and use record_memory(type="INVARIANT", ...) for anything worth keeping, through the same review gate as any other record.

Consolidating duplicates from inside an agentic editor

Agents write memory faster than a human can review it one record at a time, and over time the approved corpus accumulates near-duplicates β€” the same bug filed from two different angles. stage_consolidation() lets an agent (Claude Code, Codex, etc.) propose fixing that from inside a live session: it presents a write-up of why it thinks a set of approved records describe the same underlying issue, and only after the user explicitly agrees does it call the tool.

That call doesn't touch memory_records at all β€” it writes to a separate holding table (consolidated), leaving every source record untouched and still searchable. A human reviews staged proposals offline in the Consolidation Proposals tab of codegrave --review, where Review & Commit opens an editable draft (title/symptoms/root cause/etc., plus the agent's rationale) for a final look before it becomes one new approved record with derived_from pointing at its sources and the sources are removed; Discard just drops the proposal, no other changes. Same two-step principle as everywhere else in this tool: propose fast, commit deliberately.

End-of-turn WRAP-UP

Unlike PREFLIGHT, which gates every individual edit, session_wrapup() is meant to be called once, at the end of a turn -- right before the agent tells you the task is done. It bundles three checks:

  • Secret / PII scan -- greps this session's changed files (git status-scoped, not the whole repo) for API keys, private key blocks, and the literal path to your home directory. Report-only: it never rewrites a file, because a blind find-and-replace risks silently changing a value that was load-bearing, not just leaking a credential. The agent fixes each hit with the context a regex doesn't have.
  • Dev server cleanup -- kills processes that match a known dev-server command signature (npm run dev, vite, uvicorn, flask run, ...) and have a working directory inside the project. Both conditions have to hold -- signature alone could match something unrelated elsewhere on the machine, cwd alone could match your editor's own shell.
  • Test suite -- auto-detects (pytest/npm test/cargo test/go test) or runs [wrapup] test_command from codegrave.toml, and reports pass/fail with a tail of output.

Set test_command in codegrave.toml if auto-detection guesses wrong or your project uses something else entirely:

[wrapup]
test_command = "make test"

Architecture

main.py                  # FastMCP tools + CLI
database.py              # SQLite + sqlite-vec (vector search)
ai_models.py             # Embed / analyze / search
config.py                # TOML config + provider resolution
wrapup.py                # End-of-turn WRAP-UP gate: secret scan, dev-server cleanup, test run
providers/               # Pluggable embed & LLM backends
vendors/                 # Per-IDE installers + PREFLIGHT enforcement hooks
  hook_contract.py       #   shared control-flow skeleton + HookStrategy dataclass
  hook_strategies.py     #   one HookStrategy per platform (see capability table above)

Records are namespaced by directory -- bugs in frontend/ don't pollute queries from backend/. The SQLite database uses WAL mode for concurrent access across multiple IDE processes.

Origin

codegrave started as a habit on a different (unreleased) project -- a local-LLM chat/RAG engine -- that had a plain BUGS.md file. After every session I'd ask the agent to write what broke and how it got fixed into that file, then read it before touching code again. A few weeks of doing that by hand made two things obvious: the document was catching context git log doesn't -- wrong fixes that were tried and abandoned, the reasoning behind a decision, not just the diff -- and a flat growing file doesn't scale. An agent tweaking the frontend doesn't need to wade through bug history from the voice pipeline; it needs its own corner of the project's memory, not all of it. That's where the namespacing above came from.

Codegrave formalized that habit into an MCP server in about 30 minutes on Cursor: BUGS.md became a namespaced, structured, RAG-searchable SQLite database instead of one growing markdown file, with a human review gate so an agent's own account of what it fixed doesn't get trusted blind -- paying that history the respect of actually being checked, not just accumulated. The goal from the start was portability: something any indie dev could drop into a project and walk away with both a bug history and carried-over agent experience, no platform lock-in required.

The multi-IDE support wasn't planned up front -- it fell out of actually switching between OpenCode, Claude Code, and Antigravity on the same project and wanting all three to read the same memory instead of each one starting from zero. Whatever agent shows up to a project next inherits what the last one learned.

Git remembers what changed. Codegrave remembers what never should happen again.

About

opencode repohistory agent

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages