Changelog
Every release of brainfile, newest first. Versions follow Semantic Versioning and match the git tags.
The canonical file is CHANGELOG.md in the repository; this page is rendered from it.
Unreleased
0.22.0 — 2026-10-12
Memory moves onto the board, and the board learns its own shape. Decisions
with claim families, one search over a local index, a brief that starts
every session, and check before acting (spec-11). Every board has epics,
specs, plans, tasks and decisions, and knows how they fit together
(spec-12). A repository has one board, and Brainfile notices when it has two
(spec-15). Boards name their sisters in other repositories (spec-10).
Everything the CLI does to a board has an MCP tool (spec-13), and the TUI
catches up (task-19, task-20).
Upgrading
- A parent with open children no longer completes, whatever its type (it was
only epics);
--forcestill overrides. - Decisions are not listed in columns;
list --type decisionlists them.--type adrnow writes adecision(decision-N); existingtype: adrfiles read as decisions and keep their IDs, and a board's owntypes.adrentry no longer applies. idPrefixintypes:now names new IDs; astrictboard with notypes:map refuses types it doesn't know.- The SessionStart hook prints a real brief (about 1,600 characters) where it printed a reminder.
searchanswers from a local index on Node 22.13 and later (node:sqlite); older Node scans as before.enginesstays>=20.- While two copies of a board disagree, writes are refused until
adoptormerge-boardssettles it;migrate --to-branchalways commits the old copy's removal from the code branch. contract validateandtemplateexit 1 when they fail; scripts that ignored a failed validation will now see it.
Added
Memory and decisions
brainfile decide -k <claim.key> "text"records a decision in its claim family (--kind,--stance/--subject,--holds-while,--recheck,--valid-until,--sourcewith its sha256,--package name<op>version,--forwork that rests on it). The family's current decision is superseded: status,supersedes/supersededByboth ways, moved tologs/. An empty family lists related ones (same parent key, same words).supersede <id> "text"replaces one by hand;retire <id> --as rejected|stalelets one go. One board commit per command; no ledger lines.brainfile check "<action>"(or a decision ID,--claim-key,--claim-key-prefix) answersuse,review,use_replacementorno_decisionin recoil's order, with the stance rule and the predicatesvalidUntil, source unchanged and package version (package.json, go.mod, pyproject.toml). Exit codes 0, 2, 3, 0; errors 1.- A local search index at
.brainfile/state/index.db(gitignored, rebuildable) on Node's ownnode:sqlitewith FTS5: board and logs documents, the ledger,plans/and the project's Markdown, viagit ls-filesso.gitignoreholds. Tracked by size, mtime and sha256; changed files replace their chunks.brainfile index [--rebuild] [--status]. - MCP:
decide,supersedeandcheck(fourteen tools). - Core:
decisions(claim families, stance rule, verdicts; also in@brainfile/core/browser),decideFile,supersedeDecisionFile,retireDecisionFileand the file predicates in the main entry, and the new Node-only@brainfile/core/memory:MemoryIndex,searchMemory,composeBrief,checkDecision,checkClaimKeyPrefix. /v2/decision.jsondocumentssourcePath,sourceHashandpackageVersion.
Types and the graph
- Five built-in types that need no
types:entry:epic,spec,plan,task(aliasissue) anddecision(aliasadr).brainfile typesprints each with its place in the graph;types add <name> --like <type>takes a built-in's place (parents, children, completion rule, glyph). - Containment by
parentId: epic ▸ spec · plan · task, spec ▸ plan · task, plan ▸ task, task ▸ task.list --treeprints the outline (--parentfor a subtree);shownames the parent and children. decisions: [ids]on work: the decisions it rests on.add --decisions,patch --decisions;showlists them with where each stands and says when one is superseded, and a decision shows the work resting on it. The reverse is computed, never written.patch --parent / --clear-parentand--depends-on / --clear-depends-on;add --depends-on;list --typeandsearch --type, aliases included.- MCP:
task_addandtask_patchtakedecisions(anddependsOn);list_taskstakestree: trueand lists decisions withtype: "decision";get_taskreturnsgraph;searchtakestype. /v2/decision.json;task.jsondocumentsparentIdanddecisions;board.jsondocumentslike.- Core:
docTypes(registry,resolveDocType,parentFit) anddocGraph(buildDocGraph,docPlace,checkDocumentLinks,lintDocGraph), both in@brainfile/core/browser;readBoardGraphandfindWorkOnSupersededDecisionsin the main entry.
Board integrity
- Brainfile looks for every board a repository has: the
.brainfile/folder in the checkout (tracked by the code branch or not), thebrainfilebranch, the remote'srefs/brainfile/<name>, and checkouts of the board branch outside.brainfile/.brainfile where(and the MCPwheretool) lists them with document counts and last change, says how the two stand (one already has everything the other has, or each has changes the other lacks), and--fetch/fetch: trueasks the remote first. - While two boards disagree, every write from the CLI or the MCP tools is refused, naming the command that settles it; reads still work. A board ahead of or behind the remote is not two boards.
wheresays how many changes on the remote are not here yet, and reports a code branch that still tracks an old copy of.brainfile/.brainfile adopt <ref|path>takes one board as it is;brainfile merge-boards <ref|path>combines two by document ID on the field-level merge, renumbering the newer of two different documents that share an ID and rewriting references to it, and adopts outright when one board already has everything. Both leave one board on its branch at.brainfile/, descending from the remote's copy, with the code branch no longer tracking.brainfile/(one commit).migrate --to-branchfinishes the move: a code branch that tracked the board drops.brainfile/and ignores it in one commit, built so other staged work stays staged; on a board already on its branch it makes that commit if the code branch still tracks an old copy; and when a board already lives on the branch it says the two are two boards and points tomerge-boardsinstead of failing on the checkout.- One home for the board branch:
whereandlintreport a checkout of it outside.brainfile/; a checkout whose folder is gone is forgotten before the board is checked out (it used to block that) or bylint --fix.lintreports two boards as an error. - Backups: anything that replaces a board saves it first as
refs/brainfile/backups/<UTC time>-folder|branch.brainfile backupslists them;brainfile backups restore <name>puts one back.
Related boards
related:inbrainfile.mdnames sister boards in other repositories: a short alias for a folder relative to this repository's root, committed with the board.alias:task-id(for examplemobile:task-12) works anywhere a task ID does:dependsOn,show, and MCP tool arguments. The alias resolves to that folder's board exactly as runningbrainfilethere would, branch worktree included.- A
dependsOnon a related board blocks until that task is completed there; contract pickup uses the same rule. A folder that is not on this machine keeps the reference valid, says so inshowandlist, and counts as not done. list --relatedshows the open tasks on related boards that this board's tasks depend on,--related allevery related board's open tasks; MCPlist_taskstakes the same option.lintchecks therelated:map.
MCP and the command line
brainfile columns: list (the default, with task counts and--json),add <title>(--id,--after/--before,--completion),rename <column> <title>(the ID stays, so its tasks do),reorder <columns...>(the ones named first, the rest after) andremove <column>, which refuses a column that holds tasks unless--move-tonames where they go; they move there without being completed. One board commit per change.- MCP: eleven more tools, twenty-five in all:
note(append to a task's log, attributed likebrainfile note),log(one task's log, a search, the most recent),columns(the operations above),board(read; set the title, agent instructions andrelated:entries),types(list with the graph; add withlike,idPrefix,completable),template(list; use),lint(fix: true),sync(pull/push),where,retireandadr_promote. Every one has an output schema; every write is one board commit.list_tasksreports a document's ownstatusand filters by it. - The bundled schemas as MCP resources:
brainfile://schema/boardandbrainfile://schema/base. brainfile mcpsends server instructions: use these tools for every board change, never edit.brainfile/by hand, start withbrief, note as you go,checkbefore acting; then one line per tool.- A parity test enumerates the CLI's commands and fails unless each maps to
a tool, a resource, or an exclusion with a reason (
tui,mcp,merge-driver,hooks,auth,config,init,migrate,restore,index). - Core:
addColumn,renameColumn,reorderColumns,removeColumn,readBoardSettingsandupdateBoardSettings(pure, also in@brainfile/core/browser), and their*Fileforms withsummarizeColumns;removeColumnFilemoves a column's tasks first or refuses.
TUI
- The TUI has a decisions view, on
Dand at the type cycle'sdecisionstop: standing decisions grouped by claim-key family, history (superseded, rejected, stale) folded below and opened withL. Rows lead with●(○once retired) and say only a kind other thandecision,proposed,reviewpastvalidUntil, or where a retired one went. - A decision's detail in the TUI: claim key, kind, stance and subject, holds
while, recheck, valid until (
reviewonce past), replaces and replaced by, and the open work resting on it, each opened from the detail cursor. - Work's detail in the TUI lists the decisions it rests on, a retired one
marked
superseded → decision-2; its row saysdecision changed.
Core
@brainfile/core/repo: the board's git storage, sync, merge driver and autosync, moved from the CLI into core for other hosts to use (Node and git only; the main and browser entries stay free of git). The CLI behaves as before.
Changed
Memory and decisions
search(CLI and MCP) uses the index when Node hasnode:sqlite(22.13 and later): AND then OR, titles over bodies, current results first and history (completed work, superseded decisions) in its own lane, every hit with its path and line range, and the project's docs alongside the board. Filter tokens work as before. Elsewhere, or withBRAINFILE_INDEX=off, the scan answers in the same shape. New:search --json,--limit,--no-history.briefprints one budgeted block (about 1,600 characters,--max-chars): work resting on a superseded decision and decisions whose predicates broke, current decisions, your tasks (or the work in flight), what changed since your checkpoint, and the docs that bear on your tasks.--jsonaddssections,text,maxCharsandtruncated;lanesis unchanged.- The SessionStart hook injects that brief (Claude Code:
hookSpecificOutput.additionalContext) instead of a reminder, from a lean entry that loads neither commander, the MCP server nor the TUI (~150ms warm). log --recentand MCPsearch recentleave out decisions retired tologs/; they were never completed.
Types and the graph
- A parent with open children refuses to complete, whatever its type (it was
only epics);
--forcestill overrides. A decision is never completed. - An off-pattern parent (an epic under a task, anything under a decision) is a
lintwarning; on astrictboardadd,patchandlintrefuse it. AparentIdordependsOnloop is always refused. --type adrwritestype: decisionwith adecision-NID, no column andstatus: current;--type issuewrites a plain task. Existingtype: adrfiles read as decisions and keep their IDs.- A strict board accepts the five built-ins without listing them.
- New IDs use the type's
idPrefixfromtypes:; before, the prefix was always the type name whateveridPrefixsaid. listleaves decisions out of the columns (they have none) and says how many;list --type decisionlists them.brieforients against current decisions as well as accepted ADRs.
Board integrity
migrate --to-branchalways commits the old copy's removal from the code branch;--commitis accepted and hidden.
MCP and the command line
- The commander program is built by
createProgram()insrc/program.ts;cli.tsparses it. Nothing changes for the command line.
TUI
- The TUI no longer puts decisions in a column (with none they landed in
todo) or retired decisions in the done view. In the decisions view the board's mutating keys say how decisions change (decide,supersede,retire) instead of acting;estill edits. - The TUI's detail cursor is drawn as a bar as well as reverse video, so it
shows under
NO_COLOR; archived documents show their description. - The TUI's list keeps a child under its parent across columns: it is drawn
in the parent's tab, says its own column at the row's right edge (
review), and is no longer listed in its own tab with← epic-1. A child completed into logs/ stays under its open parent as✓ done, read-only, until the parent is done. Grandchildren nest too. A tab counts what its list holds;mstarts on the document's own column, and the cursor follows a child moved or completed under it.
Fixed
contract validateandtemplateexit non-zero when they fail; the result was dropped, so a failed contract read as passed (task-15).contract graphaccepts its own flags again.
0.21.2 — 2026-10-11
Descriptions come back whole, headings, dividers and all.
Fixed
- A task description may carry headings and dividers of its own. Reading one
stopped at its first
##heading, soshow,list --json, search and the MCP tools returned only the text above it; it now runs to the## Logor## Child Taskssection brainfile writes itself. - A
---line inside a multi-line frontmatter value (a description saved bypatch) no longer ends the frontmatter early. Only an unindented---closes it. patch --descriptionedits the description whereaddwrote it, in the body, instead of adding a second copy to the frontmatter that the body's stale one then sat beside.
0.21.1 — 2026-09-23
Shared boards stop showing up as a branch on the remote, and the board records which agent made each change without being told.
Changed
syncstores the board on the remote asrefs/brainfile/<name>instead of a branch. It is pushed, fetched and kept like any other ref, but hosts list only branches, so autosync no longer triggers GitHub's "had recent pushes / Compare & pull request" banner, and the board stays out of the branch list andgit branch -a. The last-known remote position is kept underrefs/brainfile/remotes/, wheregit fetch --prunenever touches it.--remote-branchis now--board-name(defaultboardinside a repository), stored asbrainfile.boardName. The old flag and config key still work.- A fresh clone finds the shared board with one lookup on its first
brainfilecommand, since a plain clone no longer downloads it. The lookup never prompts, gives up after a few seconds, and a miss is remembered for ten minutes. sync --jsonandwhere --jsonreportremoteRefin place ofremoteBranch;wherealso reportswebUrl.- Board commits are always authored by your git user. The acting agent is
recorded on the board instead: a
[codex]or[claude]suffix on the commit message, and the[name]on notes.
Added
- Agent detection. Without
--agentorBRAINFILE_AGENT, brainfile names the agent from the nearest agent CLI above the command (claude, codex, cursor-agent, gemini, opencode and others), falling back to the variables agents set for their commands (Codex'sCODEX_THREAD_ID, Claude Code'sCLAUDECODE) where a sandbox hides the process tree. Commands you type get no tag; your notes carry your gituser.name.BRAINFILE_DETECT_AGENT=offkeeps only explicit names. whereandsync --set-remotesay where the board is stored on the remote and, for GitHub remotes, link to its history (https://github.com/<owner>/<repo>/commits/refs/brainfile/board).
Upgrading
- Boards shared by 0.21.0 move themselves: the first
syncmerges thebrainfilebranch from the remote, publishes it underrefs/brainfile/, and deletes the branch (only if nobody pushed to it in the meantime). Upgrade every machine that shares the board; a 0.21.0 machine keeps pushing to the branch.
0.21.0 — 2026-09-22
Share a board without a service. Inside a git repository the board now lives
on its own brainfile branch: still ordinary files in .brainfile/, every
change a commit, kept out of your code and pull requests, and shared through
any git remote you choose. Nothing leaves your machine until you choose one.
Added
- Board on a branch. Inside a git repository
brainfile initstores.brainfile/as a worktree on an orphanbrainfilebranch, hidden from the code branch via.git/info/exclude. Outside one,init --trackedmakes the board its own small repository, and-gtargets a home board at~/.brainfile. Every change from the CLI, the MCP tools and the TUI is one commit authored as the acting agent; hand edits commit asedit: <files>. brainfile where: where the board lives, how it is stored, who else has it, and how many changes are waiting to be sent. Offline and read-only.brainfile sync [--pull|--push] [--set-remote <name|url>] [--remote-branch <name>] [--autosync off|push|full]: fetch, merge and push the board branch, entirely inside.brainfile/. No remote is ever assumed; a URL is registered as remoteboard, so a private board can sit next to public code.- Autosync. Once a remote is set, changes are pushed a few seconds after each
edit from a background process;
fullalso fetches before stale reads.brief(CLI and MCP) syncs first unless--offline. The TUI header showssynced 12s ago,local only, ornot synced · saved locally. - A clone gets the board on its first
brainfilecommand, says so, and sends changes back to the remote it came from. brainfile merge-driver, selected by.gitattributeson the board branch. Frontmatter merges field by field (the laterupdatedAtwins, ties go to the incoming side), log and note lists union by timestamp,ledger.jsonlmerges by line, and a task completed on one machine while edited on another stays completed with the edit appended to its archive. The driver is registered asbrainfile merge-driveronly when none is configured, so a custom one (for example an absolute path whenbrainfileis not on PATH) is kept.brainfile migrate --to-branch [--commit]moves an existing board onto the branch: history included when it was committed with the code, uncommitted edits and new files included always, every file checked by content before the old copy is removed, and a full rollback on failure.--to-plainreverses it.@brainfile/core:findBrainfileandresolveBrainfilePathacceptstopAtGitRoot;parseFrontmatterandserializeFrontmatterare exported.
Changed
brainfile initinside a git repository now creates the branch-backed board, at the repository root. Pass--plainfor a plain folder in the current directory, as before. Existing boards are untouched until you runmigrate --to-branch.- Board discovery stops at the repository root instead of walking into
parent directories, then looks for the board branch. A board kept above a
repository is no longer picked up from inside it; pass
-f <path>. - Storage messages from init, migrate, sync and brief say in plain words where the board is, who has it, and that nothing is lost when a sync fails.
0.20.0 — 2026-08-13
Added
brainfile brief --agent <name>— per-agent session orientation: full briefing on first run, delta since your last brief after (notes, moves, completions, contract changes). Also an MCP tool (count is now 11).- MCP: all tools declare
outputSchemaand returnstructuredContent(spec 2026-07-28); legacy-era clients receive identical JSON as text. - TUI:
Ltoggles the completed-history view, orthogonal to thettype cycle (they compose);$EDITORhandoff for editing documents; NO_COLOR support; launch resumes your last column and type filter;h/lcolumn keys.
Changed
- Completing a task now always writes BOTH
logs/ledger.jsonland the archived markdown, with rollback on failure;legacyModeis a no-op. - TUI panel system (1/2/3) collapsed into the single list; completed work
is the
Lview; rules panel removed with the rules system. - Releases are tag-driven: pushing
vX.Y.Zpublishes and creates the GitHub Release.
Fixed
- CLI errors print a clean message + usage instead of a stack trace.
migrate --logs-to-ledgeris a non-destructive, idempotent backfill — it previously DELETED the markdown archives it migrated.log --recentincludes ledger-only records;archive --to localcompletes the task instead of dead-ending; failed overlays no longer report documents removed; MCPcontract validatereturnsok: falseon failed checks instead of a tool error; help opened from a detail view returns to that detail.
Removed
- The rules system (adr-2): schema block, core rule operations, and the
rulescommand. Legacy boards parse fine;lint --fixfolds rules intoagent.instructions.[]column keybinds (useh/lor tab).
0.19.0 — 2026-08-12
Added
- TUI v3: complete redesign on ink 7 + react 19 via esbuild bundle — dense one-row-per-document board, type glyphs, signal-only color, split detail.
- First-class plans:
brainfile plan add/list/show/link,plan-Ndocuments. - MCP: spec 2026-07-28 (stateless era) via
@modelcontextprotocol/serverv2; one stdio server serves both protocol eras.
Changed
- Core owns all V2 board mutations; CLI, MCP, and TUI are thin frontends with parity locked by tests.
Removed
- V1 board-format support (adr-1);
brainfile migrateremains the converter.
Security
- Cleared GHSA-345p-7cg4-v4c7 et al. by leaving the pinned v1 MCP SDK.
0.18.1 — 2026-08-11
Fixed
- First monorepo release: workspaces, tokenless publishing (npm trusted publishing / OIDC), commander 14, jest 30.
0.18.0 — 2026-08-11
Changed
- Package renamed
@brainfile/cli→brainfile. Monorepo consolidation at github.com/1broseidon/brainfile.