synaptly
v0.1.8
Published
Your AI's memory, synced. A private home for what Claude Code, Codex or ChatGPT learned about you and your projects.
Maintainers
Readme
syn
Your AI's memory, synced.
Install
npm install -g synaptlyUse
syn login [--name <device>] Sign this machine in
syn init [<mind>] Link this folder to a mind
--mind <name> The same name, as a flag
syn snap [-m <message>] Send what changed here, bring what changed there
--retry-conflicts Resend conflicts (resolve them elsewhere first)
--quiet Only errors and the final conflict lines
syn journal "<text>" Append one line to today's journal
--tool <name> Label the entry with the tool that wrote it
syn conflicts Show both sides of every conflict
--brief One line naming them, or nothing when clean
syn resolve <path> --mine Close a conflict, keeping the version that conflicted
--theirs | --both | --from <f> ...or the server's, or both, or a merge of yours
--id <version> Pick one conflict when a path has several
syn status See what would change, without changing it
--retry-conflicts The same view snap would act on
syn hook install Sync when a Claude Code session starts here
syn hook status Say whether this folder has the hook
syn watch [--every <n>] Keep this folder in sync
--every instant|5m|15m|30m|manual
--once One pass, then exitUnknown options are refused, not ignored: syn snap --retry-conflict stops and
says so rather than reporting success without retrying anything.
syn status never writes anything: it reads the same three pictures syn snap
does and prints what syn snap would do.
Exit codes
| Code | Meaning |
|---|---|
| 0 | The pass went fine. Conflicts, if any, are named in the output. |
| 2 | syn refused something the server sent, or a file it cannot upload: a path that leaves the project folder, or a file type the server does not accept. |
| 1 | Could not run at all: not signed in, folder not linked, unreadable settings, server unreachable. |
Non-zero means this pass had a problem, not that there is work to do. A
waiting conflict is not a failure, so it does not change the code — syn snap
and syn status both say so in their output instead.
Environment
| Variable | What it does |
|---|---|
| SYN_API | Which API syn login signs in to. Default https://api.synaptly.dev. It does not redirect the other commands: once you are signed in the host comes from your saved credentials, and for a linked folder from its .syn.json. |
| SYN_HOME | Where credentials and links live. Default ~/.syn. |
What gets synced
MEMORY.md, memory/, assets/ and refs/. The journal/ folder is written
by the server: it comes down, it never goes up.[^journal]
[^journal]: The CLI side of journal/ is built and tested — it downloads those
entries and will never propose deleting them. They will not appear until the
server lists them in the manifest, which it does not do yet.
Linked folders
If a folder or file inside MEMORY.md, memory/, assets/ or refs/ is a
symlink or junction that points outside the project, syn refuses it — in both
directions and from the very first run. It names the path and says why:
⚠ memory/notes.md Refusing "memory/notes.md": it resolves outside the project folder through a symlink. Nothing under it syncs, in either direction.So pointing memory/ at your real notes folder somewhere else on disk will not
work. Do one of these instead:
- keep the real files inside
memory/, or - link the whole project folder rather than a folder inside it — run
syn initwhere the files actually live.
This is a deliberate limit, not an oversight. syn cannot tell a link you made
on purpose from one that arrived in a git clone, and writing through either
lands a downloaded file outside the folder you pointed it at. So a link it will
not write to is one it will not read from either. The alternative is worse: a
rule that changes under you, uploading a folder happily until the day the
server first hears about it and then going quiet.
Conflicts
If two machines change different parts of the same file, the server merges them
and syn snap brings the result down. If they change the same lines, your
version is kept beside the good one as a dendrite and syn snap tells you.
Nothing is ever lost and no conflict markers are written into your files.
A file with an open conflict is not sent again until something changes on one
side or the other. --retry-conflicts sends it anyway.
Claude Code
syn hook install writes a SessionStart hook into the project's
.claude/settings.json — never your global settings — merging with whatever is
already there, and installing twice adds nothing. Every Claude Code session in
that folder then starts with syn hook run: a quiet sync, plus one line naming
any open conflicts, so the agent finds out on its own. A sync that worked but
found conflicts exits 0 on purpose: in Claude Code hooks a 2 blocks the
action, and a waiting conflict is a state, not a failure.
If CLAUDE.md or AGENTS.md already exist in the folder, install also leaves
them a replaceable block between <!-- synaptly:start --> and
<!-- synaptly:end -->: read MEMORY.md at the start, note what happened with
syn journal, and show both sides of any conflict before doing anything else.
A journal entry is one line about what happened, up to 8 KB. Anything longer belongs in a memory file, not the journal — the server enforces the same cap.
Files it keeps
~/.syn/credentials.json— your device token. Treat it like a password.~/.syn/links.json— which folders are linked to which minds..syn.json— the mind this folder belongs to. Safe to commit..syn/state.json— what the last sync saw. Do not commit it..syn/trash/— files deleted on another device. Nothing is deleted outright.
syn writes .syn/.gitignore for you, so git ignores that folder on its own.
