Re-reads re-pay.
The only way a model knows a file at turn 40 is that the file is literally present in the transcript at turn 40. Read a 500-line file ten times and you get ten copies, each billed and each diluting attention.
GFC is an OpenCode plugin and MCP server. It treats the transcript the way git treats a working tree: the model gets a diff against what it last saw, never a full re-checkout.
MIT license. Zero dependencies. Zero tokens of per-turn overhead. Measured −22% to −34% context per turn.
Scroll to watch one file get read ten times.
Every turn re-sends the whole history. Here is what the usual fixes change, and what they don’t.
The only way a model knows a file at turn 40 is that the file is literally present in the transcript at turn 40. Read a 500-line file ten times and you get ten copies, each billed and each diluting attention.
Provider prompt caches lower the price of re-sent context. The tokens still flow, still compete for attention, and the cache breaks as soon as history changes mid-prefix.
Compaction throws away detail, and the model re-derives what was lost. That is waste plus repeated work.
Not compression, but deduplication. A sha256 proves the bytes are identical, so the duplicate never enters the transcript at all. Nothing is summarized away. The content is verifiably still in context from the first read.
One registry, one invariant. The filesystem is the store of truth. If the content on disk still hashes to the registered value, it is byte-identical to what the model already saw.
sha256 of file content, recorded per session in ~/.gfc/registry.json. Registry writes are atomic and cross-process locked, so parallel sessions can’t corrupt each other.
The OpenCode plugin hooks tool.execute.after and replaces repeat content before it enters the message history. The duplicate never exists.
Unchanged gives a ~70-token stub that cites the hash, including for unchanged pages of large files. Changed gives a numbered, multi-hunk diff from last-seen to current. Reads with offset or limit always return real lines, with a note if the file has drifted.
Models are stateless, so the first read in any session is always real content. No stub can hide something the model hasn’t seen. On compaction, summaries are told not to paste file contents, and GFC forgets the session, so the next read is real content again.
One file, read the way an agent reads it, with and without GFC. Read it a few times, then edit it and read again.
On disk: const retries = 3. The model has seen nothing yet.
Nothing read yet.
Nothing read yet.
Counts lines sent to the model, not tokens. A stub is one line here, though it costs about 70 tokens.
72-run A/B benchmark: 3 opencode free-tier models × 4 scenarios × 3 repetitions. Fresh sessions, identical prompts, GFC on versus parked. All attempts are counted, with no outlier removal. The harness ships in bench/.
It finds OpenCode, Claude Code and Codex on your machine and wires each one. Your own settings and hooks are kept, and every changed file is backed up.
Automatic. A plugin for OpenCode’s read tool, hooks for shell reads in Claude Code and Codex.
curl -fsSL https://raw.githubusercontent.com/jjjebli/gfc/master/install.sh | bash
Restart your agent. In Codex, type /hooks once and trust the GFC hooks. Re-run to update; --uninstall removes GFC and leaves your own hooks alone.
Advisory. Adds a gfc_read tool that your agent is instructed to prefer.
command: node
args: ["/Users/you/.gfc/mcp.mjs"]
Works with Cursor, Gemini CLI, Windsurf and other MCP clients. Add a rule such as “prefer gfc_read over cat, head, sed, and tail” to your agent instructions.
Two environment variables, one reset, and a test suite that runs without an LLM or network access.
~/.gfc/debug.log.GFC_HOME=$(mktemp -d) node ~/.gfc/core.mjs and node ~/.gfc/test.mjs. Deterministic, with no LLM and no network.Stated up front, because a token-saving tool that oversells is a token-wasting tool. Open any of them for the details.
Diffs are multi-hunk (Myers), but past ~1000 changed lines, or when the diff would be over 80% of the file, the full content is sent instead. Diffs also apply only when the whole previous version was read.
They get a full re-read on change. Binary files are rejected outright by the MCP tool.
It relies on tool.execute.after output mutation, checked against the opencode 1.19.0 source. If the read format changes, verification fails closed: output passes through untouched. OpenCode’s unreleased 2.x plugin API will need a port.
If your model reads a file once and never looks back, GFC does nothing. By design, it costs nothing too.