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
- 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.
| 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 |
./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).
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.
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.
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_frompointing 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.
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 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 recordsplease (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.
| 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 |
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.
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.
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_commandfromcodegrave.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"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.
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.