factsheet
Per-project memory for coding agents. Facts live in .factsheet/facts.jsonl,
one JSON object per line. factsheet prints them at session start. Agents add,
edit and drop facts with the CLI; users curate them in an editor.
The inject:
## Project memory (3)
[ze6] deploy log at /var/log/app; rotation 7d | check: systemctl status app
[q3o] azure vm resize needs deallocate first
[b78] check: systemctl --user status ssh-add-key
---
factsheet = persistent per-project memory (this CLI), injected each session start.
The footer continues with the write rules: when to store a fact, and that every
add, edit or drop needs user approval.
Quick start
Requires a Rust toolchain.
~/.claude/settings.json:
First fact:
The last command prints the inject. In a git repository the first add appends
/.factsheet/ to .git/info/exclude.
Full command guide, fact style and write rules: factsheet agent.
How-to
Update or uninstall
Hook per project
Put the same SessionStart block into .claude/settings.local.json in the
project.
Hook for droid (Factory)
~/.factory/hooks.json, event keys at the top level:
Hook for Codex CLI
~/.codex/hooks.json or <repo>/.codex/hooks.json:
Then run /hooks in a session and trust the new hook.
Hook for Cursor
~/.cursor/hooks.json:
~/.cursor/hooks/factsheet-context.sh, executable:
#!/bin/bash
input=
root=
||
ctx=
Hook for agy (Antigravity CLI)
agy has no SessionStart; PreInvocation with a stamp per conversation.
~/.gemini/config/hooks.json:
~/.gemini/config/factsheet-bootstrap.sh, executable:
#!/bin/bash
input=
cid=
stamp="/tmp/factsheet-inject-"
if [ && [; then
ws=
[ &&
ctx=
else
fi
Extend or change the write rules
user_instructions in ~/.config/factsheet/config.toml
($XDG_CONFIG_HOME/factsheet/config.toml, %APPDATA%\factsheet\config.toml on
Windows) is appended after the footer. Any TOML string:
= """
Add facts without asking; I review them with `factsheet curate`.
Tag every fact with the ticket id, for example PROJ-123.
Store command recipes with the tag `ref`, never as hot facts.
"""
Curate facts by hand
factsheet curate writes every fact to .factsheet/curate.txt, opens it in
$VISUAL, $EDITOR, vi or notepad, and applies the result when the editor
exits. Facts are grouped by their first tag, hot facts first, ref facts after.
# factsheet curate. One fact per line: [id] text | check: cmd #tag #tag (date)
# delete a line = drop | change text, check or tags = edit | new line without [id] = add
# lines starting with # and the (date) are ignored | empty sheet = abort
# deploy
[ze6] deploy log at /var/log/app; rotation 7d | check: systemctl status app #deploy (2026-08-19)
# ref: azure
[q3o] azure vm resize needs deallocate first #ref #azure (2026-08-19)
Fields are separated by two spaces. A changed text or check refreshes the date; a tag change alone does not. A parse error, unknown id or empty sheet aborts the run; the sheet stays on disk. The store is locked while the editor is open.
Find and clean duplicates
Reference
factsheet --help: commands and flags.factsheet agent: the guide the agent follows, with line types, tagref, fact style and the write rules.- Store:
{project_root}/.factsheet/facts.jsonl, plus.lock,facts.jsonl.bak(one-step undo after edit or drop) andcurate.txtwhile curating. - Config:
~/.config/factsheet/config.tomlwithmax_text(default 200),hot_warn(default 40) anduser_instructions. Created with defaults on first run. A non-string value or invalid TOML puts a hint into the inject instead of the text. - Registry:
~/.config/factsheet/projects, one store path per line, pruned byfactsheet projects.
Design notes
- Project root is the nearest directory up from CWD with
.factsheet/or.git; otherwise CWD. In a project without git, run the firstaddfrom the project root. - Reserved tag
refmarks reference facts (keys, IPs, lookup lists). They stay out of the inject; a footer lists their tag counts. - The store is opaque: edit only via
factsheet. factsheetnever executes storedcheckcommands.add,editanddrophold an OS lock on.factsheet/.lockwhile they run.addappends one JSONL line, after a newline if the file lacks one.editanddropkeep the previous file asfacts.jsonl.bakand stop if the backup fails.- A missing store is treated as empty; other read errors stop the command.
- No database, daemon or network. Every command reads the whole file.
License
MIT.