brain
The guided flow, and the front door. Opens on the list of brains this machine knows about; opening one drops you into its checklist. Needs a terminal, because it asks questions — in a script, use the individual commands.
Documentation · v0.2
Everything from an empty folder to a knowledge graph Claude can query — the commands, the flags, what each one actually writes to disk, and what to do when a step fails quietly.
New here? Read what brainiphy is, then work through the quickstart. Looking for a specific flag? Jump to the command reference. Something not working? Troubleshooting covers the failures that are quiet rather than loud.
Everything a business knows is spread across systems that do not talk to each other: proposals in a Drive folder, contacts and deals in a CRM, last year's numbers in a spreadsheet, meeting notes in somebody's Documents. Ask Claude about any of it and it has no idea. The information exists — it just is not anywhere Claude can reach.
brainiphy builds that reachable place, once per client. You point it at your folders and systems; it pulls everything in, indexes it into a knowledge graph, and wires that graph into Claude. It then keeps itself current in the background.
| Stage | What happens | Who does it |
|---|---|---|
| Your sources | Nothing is moved and nothing is uploaded. You tell brainiphy where to look — a folder on this Mac, an API base URL, a CRM sub-account. | You, once |
| A connector per source | One script per system, pulling records out and writing them all as plain Markdown into raw/. For a folder or a preset it is written for you. | brain new-connector |
| The graph | graphify indexes the normalized files into graphify-out/graph.json — the people, companies, deals and documents, and how they connect. | brain sync |
| Claude reads it | Claude Code works out of the box. Claude Desktop is one extra flag. | brain connect-claude |
The difference the third stage makes is the difference between a folder full of files and something that can answer which clients did we quote in March and never hear back from.
launchd, and credentials live in the macOS Keychain. These are the two reasons there is no Linux or Windows build.A model is needed to index documents, but not necessarily an API key — see indexing, models and cost.
One command. It sets everything up in its own isolated folder, so it cannot disturb anything else on your Mac, and reports at the end whether it found what it needs.
$ curl -fsSL https://raw.githubusercontent.com/rvst312/brainiphy/main/install.sh | bashThen:
$ brainReasonable, for anything piped into bash. Download it and have it tell you what it would do:
$ curl -fsSL https://raw.githubusercontent.com/rvst312/brainiphy/main/install.sh -o install.sh
$ bash install.sh --dry-runOutside its own folder the installer touches exactly three things: two shortcuts in ~/.local/bin, a marked block in your shell profile (backed up first), and a link that registers it with Claude Code.
| Flag | Effect |
|---|---|
--prefix DIR | Where to put the files. Default ~/.claude/skills/brainiphy. |
--python BIN | Which Python to build with. Default: the newest python3 ≥ 3.9. |
--no-graphify | Skip the graphify engine. |
--no-path | Don't touch the shell profile. |
--no-skill | Don't register it with Claude Code. |
--uninstall | Remove everything it installed. |
--dry-run | Print what would happen and change nothing. |
Installing from a clone, for working on brainiphy itself or on a fork:
$ git clone https://github.com/rvst312/brainiphy.git
$ bash brainiphy/install.sh --prefix "$PWD/brainiphy"$ bash install.sh --uninstallRemoves the virtualenv, the two shortcuts, the marked block in your shell profile and the skill link. Your brains are your own folders and are left alone.
A complete brain for one client, from an empty folder to a graph Claude can query. This walks the seven steps by hand so you can see what each one does; in practice brain on its own runs the same sequence and tells you which step you are on.
brain init prepares an ordinary folder to receive connectors. It is safe to re-run: it never overwrites an existing registry.
$ mkdir -p ~/clients/acme
$ brain init ~/clients/acmeThat writes connectors/registry.yaml and connectors/state/, adds the generated output directories to .gitignore, and adds connectors/ to .graphifyignore so your connector scripts are never indexed as content.
The cheapest source is a folder on this Mac — including a synced Drive or Dropbox folder. It is generated complete and works on the next sync.
$ brain new-connector ~/clients/acme docs --mirror ~/Dropbox/acmeFor a system with a preset, you supply an account id and a token — no code. Run brain presets to see what ships today and what each one needs.
$ brain new-connector ~/clients/acme crm --preset gohighlevel --var LOCATION_ID=abc123
$ brain secret set graphify-acme-crmThe secret prompt is hidden as you type. The credential goes into the macOS Keychain and nowhere else — never into registry.yaml, never into a command argument.
brain guide reads the project and works out which steps are done, what is missing from the pending ones, and the exact next command. It only reads, so it is safe to run anywhere.
brain guide ~/clients/acme4/7 done ✓ done ▸ next ○ pending – n/a
✓ 1 Install graphify
✓ 2 Scaffold the project
✓ 3 Add data sources
▸ 4 Finish the custom connectors
not runnable yet: hubspot (needs code)
write the fetching for anything generated from a stub, and fill in the
account details a preset needs
$EDITOR ~/clients/acme/connectors/hubspot/sync.py
✓ 5 Run the first sync
○ 6 Connect it to Claude
not connected yet
brain connect-claude ~/clients/acme --desktop --trust-desktop
○ 7 Keep it in sync
not scheduled — sync is manual for now
brain schedule ~/clients/acme --interval-minutes 15 --load
↳ next step:
$EDITOR ~/clients/acme/connectors/hubspot/sync.pySteps can be done out of order, which is why 5 is ticked while 4 is not. A brain you set up six months ago usually only needs “add one more source”, and walking it through all seven to get there would be silly.
This pulls every source in and indexes it. The first build is always a full one, and it is the slow one — later syncs only run what is due.
$ brain sync ~/clients/acmeIt prints ran=[...] skipped=[...] errors=[...] graph_rebuilt=<bool> and exits non-zero if any connector failed, so it drops straight into a script or a CI job.
$ brain connect-claude ~/clients/acmeThat connects Claude Code. Add --desktop to also register the graph with Claude Desktop, and restart Desktop afterwards to pick it up.
$ brain schedule ~/clients/acme --interval-minutes 15 --loadmacOS now re-runs the sync on its own, whether or not a terminal is open. Logs land in connectors/logs/.
Seven words do most of the work in this documentation, and in the CLI's own output. They are used consistently and mean exactly this.
connectors/<name>/sync.py in your own project.graphify-out/graph.json.A checklist, not a wizard. brain re-reads the folder every time it draws, so it always knows which steps are already done and puts you on the one you are actually on. You can jump around.
↵ to run the step you’re on. You can also jump: a new brain wants the sequence, but a brain you set up six months ago just needs “add one more source”, and walking it through all seven to get there would be silly.The engine that turns the collected files into a graph. The installer does this for you.
Creates registry.yaml, .gitignore and .graphifyignore, so the connector scripts are never indexed as content.
One connector per system that feeds the brain: a folder, a URL, a CRM, an API.
Write the fetching for anything generated from a stub, and fill in the account details a preset needs.
Pull every source in and index it into a graph.
So Claude Code, and optionally Claude Desktop, can query the graph.
A LaunchAgent that re-runs brain sync on its own, whether or not a terminal is open.
brain guide prints the same seven steps as text, works out which are done, and gives the exact command for the next one — see step 4 of the quickstart.
A brain is an ordinary folder. This is everything brain puts in it — nothing is hidden anywhere else on the system except the credentials, which are in the Keychain.
connectors/
├── registry.yaml # which connectors exist + their type and interval
├── <name>/sync.py # one script per data source
├── state/<name>.json # last-run timestamps, drives interval checks
└── logs/ # LaunchAgent stdout/stderr
raw/<name>/ # connector output — normalized Markdown
graphify-out/graph.json # the built graph
.graphifyignore # excludes connectors/ from indexing
.gitignoreregistry.yaml is meant to be readable and is safe to edit by hand — a malformed entry is skipped rather than taking down the whole sync. Everything under state/, logs/, raw/ and graphify-out/ is generated and gitignored.
Five kinds of source, and each one tells you upfront whether it leaves you with homework — before you pick. Add as many as you like, each with its own polling interval.
| Source | When your data is | What is left for you | Flag |
|---|---|---|---|
| local folder | A folder on this Mac, including a synced Drive or Dropbox folder. | ready to run | --mirror <folder> |
| preset | A system brainiphy already ships a finished connector for. | ready to run | --preset <name> |
| http api | Any REST API with no preset. | needs code | --api <base-url> |
| url | A public web page. Fetched once by graphify, not a live source. | one-off | graphify add <url> |
| custom | A database, a local export, anything else. | needs code | (no flag) |
Generated complete with rsync -a --delete, so a file removed at the source disappears from the brain instead of lingering as a stale node.
$ brain new-connector ~/clients/acme docs --mirror ~/Dropbox/acmeIt copies rather than symlinks, because graphify does not follow symlinks and a linked folder would simply never be indexed. It also converts the file types graphify cannot read — CSV, JSON — into one Markdown record per row, and names anything still unreadable in its summary rather than leaving it silently out of the graph.
The preset that ships today is GoHighLevel / LeadConnector — contacts, opportunities, pipelines, conversations, calendars, users and forms from one sub-account. Adding another preset is dropping a file into the package and registering it, which is the point: presets are the fast path, not the boundary.
$ brain presets
$ brain new-connector ~/clients/acme crm --preset gohighlevel --var LOCATION_ID=abc123Reach for --api before the bare template for anything REST. A connector that hand-rolls urllib re-introduces the four bugs the shipped HTTP client exists to prevent: a banned default user-agent, a second unguarded fetch for the next page, no retry on transient faults, and a missing scope reported as an auth failure.
$ brain new-connector ~/clients/acme billing --api https://api.example.comWhat is left is one collect_* function per kind of object. Before you write any of them, probe the generated connector against the live API: it reports which objects your credential can actually read and writes nothing. That is the fastest way to find out whether a token has the scopes you assumed.
A one-off, not a live source — graphify fetches it once. No connector is involved.
$ graphify add https://example.com/handbook
$ brain sync ~/clients/acme --fullbrain new-connector generates a script that already satisfies the contract. In most cases you only fill in fetch_records().
SOURCE_SYSTEM = "hubspot"
def fetch_records() -> list[dict]:
token = get_secret("graphify-acme-hubspot")
req = urllib.request.Request(
"https://api.example.com/v3/records",
headers={"Authorization": f"Bearer {token}"},
)
with urllib.request.urlopen(req, timeout=30) as resp:
data = json.load(resp)
return [
{"id": r["id"], "title": r["name"], "body": r["notes"]}
for r in data["results"]
]Each record needs id, title and body; any other keys are written into the Markdown frontmatter. The template's main() handles --out, normalization and stable file naming.
If you ever write one from scratch, this is all it has to do:
--out <dir> and write normalized Markdown there via frontmatter.write_record().0 on success and non-zero on failure, with a human-readable summary on stdout.keychain.get_secret() — never as a CLI argument and never hardcoded, since shell history, process listings and launchd logs would all leak them.Nothing in the orchestrator branches on what kind of connector a script is. At run time every connector is just an executable that satisfies the contract above, which is why supporting a new kind of source means writing a script and never extending brainiphy.
| If the source is | Do this | Code you write |
|---|---|---|
| A local folder already on disk | --mirror <folder> | none |
| Content reachable by public URL | graphify add <url> | none |
| A system Claude already has an MCP connector for | Call that from the generated sync.py rather than building fresh auth. | a little |
| A REST API | --api <base-url> | one collect_* per object |
| Anything else — a database, a local export | The bare template. | one fetch_records() |
Each source says how often it should be checked. brain sync runs the ones that are due — tracked in connectors/state/<name>.json — and rebuilds the graph only if one of them actually ran. So running it often is cheap.
$ brain sync ~/clients/acme # just what's due
$ brain sync ~/clients/acme --dry-run # what would run, touching nothing
$ brain sync ~/clients/acme --full # re-read and re-index everythingbrain sync picks for you. This table matters when you script around it instead.
| Pass | Indexes | Needs a model | When brain uses it |
|---|---|---|---|
graphify extract | documents and code | yes, for documents | first build, and every --full |
graphify update | code only (local AST) | no | every later manual run |
$ brain schedule ~/clients/acme --interval-minutes 15 --loadWrites a LaunchAgent at ~/Library/LaunchAgents/com.graphify.sync.<slug>.plist. Without --load it only writes the plist and prints the launchctl bootstrap command. It refuses to run if the project has no registered connectors — scheduling a sync loop with nothing to sync is a silent no-op that is confusing to debug later.
Indexing code is a local AST pass with no model involved. Indexing documents — the proposals, the notes, the CRM records — needs one. That does not mean it needs a new bill.
brain sync chooses a backend in this order:
--backend <name>, if you pass one.ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY, and so on.claude CLI is installed. graphify shells out to claude -p, so indexing is billed to your Pro or Max plan instead of pay-as-you-go API credit.$ brain sync ~/clients/acme --full --backend claude-cli # force the subscription
$ export GRAPHIFY_CLAUDE_CLI_MODEL=haiku # faster and lighter than the Opus defaultThe subscription backend runs one chunk at a time, so a large first sync is slower than an API key would be. A local model works too: --backend ollama.
$ brain connect-claude ~/clients/acme # Claude Code
$ brain connect-claude ~/clients/acme --desktop # ...and Claude Desktop| Flag | Effect |
|---|---|
(none) | Connects Claude Code via CLAUDE.md and hooks. The low-risk default. |
--desktop | Also registers a graphify-mcp MCP server in claude_desktop_config.json, pointed at this project's graph.json. |
--trust-desktop | Appends the project to localAgentModeTrustedFolders. Additive — existing entries are never replaced. |
claude_desktop_config.json is backed up to .bak-<timestamp> before any edit. Restart Claude Desktop to pick up the change.
Connector credentials live in the macOS Keychain — the same place Safari keeps your logins — and only there. Connectors read them at run time.
$ brain secret set graphify-acme-hubspot # prompts, hidden as you type
$ brain secret get graphify-acme-hubspot # debugging only; prints bare, for pipingA secret never reaches registry.yaml, a CLI argument, or shell history. Process arguments are readable by anything running as the same user for as long as the command runs, and they land in ps output and launchd logs — so the value is written to security's stdin instead of passed as an option.
brain sync runs every connectors/<name>/sync.py in the target project. A brain built from an untrusted project directory runs whatever those scripts contain.brain forget removes the entry and touches nothing inside the folder. Deleting a client's knowledge base for real is rm, deliberately, by a human.Security issues go to a private advisory, not a public issue.
brain on its own covers everything below. These exist for scripts, for automation, and for when you know exactly what you want. Every one of them takes --help. Output is coloured and tabulated at a terminal and plain when piped, so logs stay clean.
brain status answers “is this thing actually working”: every source, whether it can really run, when it next will, how much it has brought in, and how big the graph is. When a source can’t run, the table says which kind of not-ready it is instead of making you open files to find out.The guided flow, and the front door. Opens on the list of brains this machine knows about; opening one drops you into its checklist. Needs a terminal, because it asks questions — in a script, use the individual commands.
Start a new brain, skipping the list.
Every brain on this machine, as text: setup progress, source count, graph size, last sync and location. A folder that has moved or been deleted is reported as missing rather than quietly dropped.
Register a brain that already exists — one made before the list did, or one that arrived with a cloned repo. At a terminal, run it with no argument to browse for the folder.
Remove a brain from the list and touch nothing inside it. The connectors, the graph and the mirrored documents all stay. brain add puts the entry back.
Prepare a folder to receive connectors: creates connectors/registry.yaml and connectors/state/, appends generated output to .gitignore, and appends connectors/ to .graphifyignore. Never overwrites an existing registry, so it is safe to re-run.
Print the seven steps, work out from the project on disk which are done, and give the exact next command. Read-only, so it is safe to run anywhere — including from an agent that needs to know where a brain stands.
Registered sources — type, whether each can actually run, when it next will, how many records it has pulled in — then the graph size in nodes and edges, and where the setup stands.
List the systems brainiphy ships a finished connector for, and the account details each one needs.
Write connectors/<name>/sync.py and register it in registry.yaml with its type and interval. Existing scripts are never overwritten.
Run every connector whose interval has elapsed, then rebuild the graph — but only if at least one connector actually ran. Prints ran / skipped / errors / graph_rebuilt and exits non-zero if any connector failed.
Open graphify's interactive graph in your browser. If the picture is older than the graph it is redrawn first, which costs no tokens.
Wire the graph into Claude.
Generate a LaunchAgent that runs brain sync <project> --full on an interval. Logs land in connectors/logs/. Refuses to run if the project has no registered connectors.
Store a connector credential in the macOS Keychain, prompting with hidden input. The write is confirmed by reading it back.
Print a stored credential, bare, for piping. Debugging only.
For the parts that change least often — the exact on-disk layout and the connector contract — see a brain on disk and writing a connector above.
These are the failures that are quiet rather than loud — the ones where something reports success and the result is still wrong. Loud errors usually say what they are.
Why
graphify honours .gitignore, and brain init puts raw/ there so mirrored client material is never committed. Without --no-gitignore, graphify skips the whole corpus.
Fix
Use brain sync, which passes the flag for you. If you are calling graphify extract by hand, add --no-gitignore.
Why
The incremental graphify update pass indexes code only. A brain made of documents needs the full extract pass.
Fix
The LaunchAgent brain schedule writes already passes --full. If you wrote your own cron entry, add it.
brain view shows a picture that does not match the graph.Why
graphify extract writes graph.json without redrawing graph.html, so after a full rebuild the picture is the previous one.
Fix
brain view redraws it when it is stale. Run brain view rather than opening graph.html directly.
Why
A stub with no fetching written, and a preset missing its account id, are both present on disk and neither can run.
Fix
brain status names which kind of not-ready it is. brain guide gives the exact command to fix it.
Why
The credential has no scope for those objects. A missing scope can look like an auth failure.
Fix
Probe the generated connector before syncing: it reports which objects the credential can actually read and writes nothing.
Why
macOS security can exit 0 on a write that stored nothing, or that stored a different value.
Fix
brain secret set already reads the value back to confirm it. If you wrote the item another way, re-set it with brain secret set.
brain schedule refuses to run.Why
The project has no registered connectors.
Fix
Add at least one source first. Scheduling a sync loop with nothing to sync is a silent no-op that is confusing to debug later.
Why
connectors/ is missing from .graphifyignore — usually because brain init was skipped on a project that already had a registry.yaml.
Fix
Run brain init on the project. It never overwrites an existing registry, so it is safe.
graphify is the engine that turns a folder of normalized files into a knowledge graph. brainiphy is the layer around it: discovering sources, writing the connectors, keeping them synced, and wiring the result into Claude. Keeping them apart means graphify stays a general graph tool and brainiphy stays replaceable around it. The installer brings graphify in for you.
Two hard dependencies: credentials go in the macOS Keychain, and scheduling is a launchd LaunchAgent. Everything else is ordinary Python. Ports are welcome — it is MIT.
Then it is a REST API, and --api <base-url> generates a connector with the networking already done. You write one function per kind of record — or ask Claude to, since the skill exists for exactly that. If it is not an API at all, the bare template takes one function that returns records.
No. Sources are read locally or from their own APIs and indexed on your machine. The only thing that leaves is what you send when a model indexes your documents — the same trust boundary you already accepted by using Claude.
No. Files are named by a stable slug of each record's remote ID, so a re-sync overwrites in place. Mirrored folders use rsync --delete, so a file removed at the source stops being a node.
Yes — it is a folder. Copy it, and brain add puts it on their list. What does not travel is the credentials, which are in their own Keychain, not in the folder.
No, and this is deliberate. brain forget and the app's d key only remove the entry from the list. The connectors, the mirrored documents and the graph all stay exactly where they are. Deleting them for real is rm.
bash install.sh --uninstall. It removes the virtualenv, the two shortcuts in ~/.local/bin, the marked block in your shell profile, and the skill link. Your brains are your own folders and are left alone.
One brain per business. A folder on your Mac, a handful of scripts you can read, and a graph Claude can query.
$ curl -fsSL https://raw.githubusercontent.com/rvst312/brainiphy/main/install.sh | bash
$ brain