TL;DR: faf-cli opens its renderers, injector and faf auto update chain as public exports — consumers compose instead of port — and faf export --agents is idempotent again: one block, every run.

In Plain English

Old state. MCP servers and editor extensions copied faf-cli's writers for AGENTS.md, GEMINI.md, .cursorrules, CLAUDE.md. Those copies drifted. And faf export --agents stacked its own block on every re-run.

Fix. The same functions faf export and faf auto use are now public. The injector finds the managed block by whole marker lines at column 0 — not by substring.

New state. Import from faf-cli and you write the same bytes the CLI writes. Re-export is one block, every run. A file the old injector stacked is repaired on the next export.

npm install -g faf-cli@7.12.0 faf export --agents

Try it without installing: bunx faf-cli@7.12.0 --version (or npx --yes faf-cli@7.12.0). Pin the version in scripts — @latest drifts.

What happened since The VS Code Edition

One blog, one arc — no post per patch. Last write-up was v7.11.0 The VS Code Edition (computeDrift() as a library). This cut opens the rest of the write path.

What 7.12.0 is

Lesson: compose, don't port. The bytes faf export writes should come from one place.

import { renderAgentsMd, enrichFromRepo, injectFafBlock, updateExistingFaf, writeFaf } from 'faf-cli';
  • Renderers are public — renderAgentsMd · renderGeminiMd · renderCursorrules · renderClaudeMd · renderCopilotInstructions and their write* pairs.
  • enrichFromRepo(dir, data) — the repo-facts step faf export --agents runs first. Hand-authored values win; detection fills gaps.
  • updateExistingFaf(dir, existing) — the exact chain faf auto runs on an existing project.faf. faf auto itself now calls it.
  • One injector — injectFafBlock / findFafBlock match markers as whole lines at column 0. Fenced examples are skipped. An unbalanced fence inside the block cannot hide the end marker. CRLF and BOM survive. A block that lost its end marker is prefixed, never overwritten.

faf export --agents is idempotent again

From 7.1.4 through 7.11.0 the rendered blockquote quoted the marker tokens in prose, and the injector located the block by substring. Every re-run cut the old block at the quote and appended its stale tail — AGENTS.md grew ~49 lines per export. Both ends are fixed. A file already stacked is repaired on its next export.

Try it

Install (global):

npm install -g faf-cli@7.12.0 # short name, same bits: npm install -g faf@7.12.0

Try (no install):

bunx faf-cli@7.12.0 --version bunx faf-cli@7.12.0 export --agents

More: The VS Code Edition (v7.11.0) · v7.12.0 release · repo.

Technical details

  • Version: 7.12.0 (September 9, 2026)
  • New exports: renderAgentsMd / writeAgentsMd · renderGeminiMd · renderCursorrules · renderCopilotInstructions · renderClaudeMd / readClaudeMd / parseClaudeMd / fafMetaTag · injectFafBlock / findFafBlock / FAF_START / FAF_END · enrichFromRepo · updateExistingFaf / fillEmpties · serializeFaf / writeFaf
  • Packages: dual-published — faf-cli and faf, same version
  • Tests: 1417/1417
  • Score: ✪ Trophy 100%