Command reference
Every command Tomo exposes. All informational commands take --json for scripting. Human output is colored on a terminal and plain when piped.
tomo init #
Optional. tomo sync <host:/path>, tomo sync --local-peer <path>, and tomo connect <host:/path> all initialize the project automatically on first use, so you rarely need init directly. Run it explicitly to pre-configure a project, or to initialize a directory (like your $HOME) that auto-init deliberately refuses.
Initialize a Tomo project in the current directory (create .tomo/). Writes a commented starter config.toml and a stable replica id, and adds .tomo/ to .gitignore if the folder is a git repo. Idempotent: re-running on an existing project changes nothing.
$ tomo init initialized Tomo project in /home/you/proj/.tomo
tomo sync #
Sync this project with a peer — the primary command. Name the peer as a single rsync-style target (user@host:/remote/path, also host:~/path for the remote home). It records the peer if new and starts syncing over SSH in one step — the session's own bootstrap and handshake are the validation. An identical already-recorded peer just runs; a different target simply switches the recorded peer — a loud one-line notice (⇄ peer changed: was … · now …) prints before reconnecting. With no target it runs against the recorded [remote], or --local-peer <path> for a local directory, or watch-only if neither is configured.
Auto-initializes. Given a target or --local-peer in a directory that is not yet a Tomo project, sync initializes it in place (creating .tomo/, printing a one-line initialized .tomo/ (private)) and continues seamlessly — no separate tomo init needed. As a safety net it refuses to auto-initialize your entire home directory or a filesystem root (cd into a project first, or run tomo init there explicitly if you truly mean it). A bare tomo sync in an uninitialized directory — nothing to resume — still errors and points you at the one-command form.
On a real terminal, tomo sync runs the session in the background and attaches the interactive TUI — the live stream plus a status heartbeat, with the conflict center one keypress away (c). q stops the session (with a confirm), d detaches and leaves it syncing. Pipes, scripts, --json, and --plain get the classic line stream, byte-for-byte.
- [TARGET]
- The peer as a single
user@host:/remote/pathtarget (alsohost:~/pathfor the remote home). Omit to resume the recorded peer. - --local-peer <PATH>
- Sync with a local project directory instead of over SSH (spawns a served peer rooted there). Mutually exclusive with a
<target>. - --json
- Emit machine-readable JSON event lines.
- -d, --detach
- Start the session in the background and return (prints the pid and how to attach).
- --plain
- Classic line stream instead of the TUI (pipes and
--jsonalways stream).
Examples
$ tomo sync you@gpu-box:~/proj # first run: record + bootstrap + sync (TUI on a terminal) $ tomo sync # reuse the recorded peer $ tomo sync --local-peer ../mirror # mirror to a local dir (no SSH)
$ tomo sync dev@gpu-box:~/proj initialized .tomo/ (private) recorded remote dev@gpu-box:~/proj connecting to dev@gpu-box over SSH pushed remote binary tomo 0.2.0 (x86_64-unknown-linux-musl, 48234112 bytes) ✓ peer connected ✓ synced src/main.rs ✓ synced target/release/proj
tomo connect #
Record a sync peer for this project and validate the connection — without starting a session. It records the [remote], bootstraps the remote binary, exchanges the handshake, and exits. A one-shot health check. tomo sync does this automatically as it starts a session, so you rarely need connect directly. Idempotent: re-running with the same target revalidates instead of erroring; a different target simply switches the recorded peer (a loud one-line notice prints before revalidating against the new target).
- <TARGET>
- The peer as a single
user@host:/remote/pathtarget (alsohost:~/pathfor the remote home). - --identity <PATH>
- Explicit SSH private-key path, recorded in the
[remote]so later sessions reuse it. Tried before ssh-agent,~/.ssh/config, and the defaultid_ed25519/id_rsa. Use when your key is neither in the agent nor a default name nor selectable via~/.ssh/config.
Examples
$ tomo connect you@gpu-box:~/proj $ tomo connect you@gpu-box:~/proj --identity ~/.ssh/gpu_ed25519
tomo status #
Show sync status for this project: index root hash, file and tombstone counts, the conflict badge, peer connection state, and history mode. Safe to run alongside a live session.
- --json
- Emit the machine-readable status JSON.
Examples
$ tomo status root af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262 files 128 tombstones 3 conflicts 0 peer offline history adaptive (412 versions)
$ tomo status --json{
"root": "af1349b9…f3262",
"files": 128,
"tombstones": 3,
"conflicts": 0,
"conflicts_unresolved": 0,
"connected": false,
"reconciling": false,
"history": { "mode": "adaptive", "versions_recorded": 412, "rung": 0, "staged": 0 },
"updated_unix_ms": 1784585581996
}tomo log #
Show version history, newest first. With a <path>, shows that path's full history. With no path, shows recent activity across all paths (the 20 newest versions by default; raise it with --limit).
- [PATH]
- The path whose history to show (repo-relative or absolute). Omit for repo-wide recent activity.
- --json
- Emit machine-readable JSON.
- --limit <N>
- Show at most this many versions (newest first). Repo-wide
logdefaults to 20.
Examples
$ tomo log src/main.rs history of src/main.rs (newest first): #3 present 1.2 kB replica 332f855c local 2s ago (2026-07-20 22:13:26Z) #2 present 1.1 kB replica a91be004 remote 5m ago (2026-07-20 22:08:11Z) #1 present 0.9 kB replica 332f855c local 9m ago (2026-07-20 22:04:02Z)
$ tomo log # repo-wide recent activity $ tomo log src/main.rs --json # machine-readable, ids from "id"
The JSON is an array of version objects, each with id, present, tombstone, size, exec, content_hash, replica, origin, wall_unix_ms, and the vector clock. The id is what you pass to diff and restore.
tomo diff #
Show a textual diff of a path between recorded versions and/or the working tree. By default diffs the newest recorded version against the current working-tree file. Exit 0 when identical (or binary/oversized, declined), exit 1 when they differ — git-style.
- <PATH>
- The path to diff (repo-relative or absolute).
- --version <VERSION>
- The recorded version id to use as the base (left) side. Defaults to the newest recorded version.
- --against <AGAINST>
- A recorded version id to use as the target (right) side instead of the working-tree file. So
--version A --against Bdiffs two recorded versions. - --json
- Emit machine-readable JSON.
Examples
$ tomo diff note.txt --version 1 --against 2 diff note.txt: version #1 → version #2 (- base / + target): hello + there world
$ tomo diff src/main.rs # newest recorded vs working tree $ tomo diff src/main.rs --version 5 # version 5 vs working tree
tomo restore #
Restore a path to a previous version. With no --version, restores the version before the current newest — the common "undo my last save". The write goes through staging + atomic rename; if a session is running, the restored bytes then sync to the peer as an ordinary local change.
- <PATH>
- The path to restore (repo-relative or absolute).
- --version <VERSION>
- The exact version id to restore (an id from
tomo log). Defaults to the version before the current newest. - --stdout
- Write the restored bytes to stdout instead of the file on disk — useful for inspecting or piping an old version without overwriting your working copy.
Examples
$ tomo restore src/main.rs # undo the last save $ tomo restore src/main.rs --version 2 # restore an exact version $ tomo restore src/main.rs --version 2 --stdout | less
$ tomo restore src/main.rs --version 2 restored src/main.rs to version #2 (1.1 kB)
tomo conflicts #
List, inspect, or resolve conflicts. Conflicts are resolved automatically and never block sync (last-writer-wins); the loser is always preserved in history. These commands surface that record non-blockingly and let you recover a losing version. With no subcommand, lists the unresolved conflicts.
conflicts list
- --all
- Include already-acknowledged conflicts, not just unresolved ones.
- --json
- Emit machine-readable JSON.
$ tomo conflicts list unresolved conflicts: #7 src/config.rs local ✓ winner · remote preserved 2m ago #6 README.md remote ✓ winner · local preserved 8m ago
A clean tree reports no unresolved conflicts 🎉.
conflicts show <ID-OR-PATH>
- <ID-OR-PATH>
- The conflict id (from
tomo conflicts list), or a project-relative path — which shows that path's newest unresolved conflict. - --json
- Emit machine-readable JSON.
Shows one conflict in detail, including a diff of the two heads and their vector clocks — so you can see exactly what the losing side would have written.
$ tomo conflicts show 7 $ tomo conflicts show src/main.rs
conflicts resolve [ID-OR-PATH]
- [ID-OR-PATH]
- The conflict id to resolve, or a project-relative path — which targets that path's newest unresolved conflict. Omit only with
--allor--interactive. - --keep-current
- Keep the current file and mark the conflict acknowledged (the tree is left untouched).
- --take-loser
- Replace the current file with the preserved losing version, then mark it resolved. A running session syncs it as a normal local edit.
- --both
- Keep both: materialize the preserved loser alongside the winner as
<path>.theirsfor a manual merge, then acknowledge. The sidecar syncs like any file. - --all
- Mass-acknowledge every unresolved conflict (only with
--keep-currentsemantics; not valid with--take-loser/--both). - --interactive
- Walk every unresolved conflict one at a time — show its diff, prompt keep/take/both/skip. A terminal-based fallback for the conflict center.
$ tomo conflicts resolve 7 --keep-current $ tomo conflicts resolve src/main.rs --take-loser $ tomo conflicts resolve 7 --both $ tomo conflicts resolve --all --keep-current $ tomo conflicts resolve --interactive
tomo ignore #
Add an ignore rule to .tomo/config.toml, or — with no argument — list the effective ignore rules. Adding a rule appends to the config; your existing comments and formatting are kept byte-for-byte (it never parses and rewrites the file), and the write goes through the same crash-safe staging + atomic-rename path as every other Tomo write.
A bare directory name (no glob metacharacters and no /; a trailing / is stripped) expands to the same two rules the built-in defaults use — <name> and <name>/** — so both the directory and everything under it are ignored (per-path enforcement needs both). A pattern with glob characters or a slash is added verbatim as a single rule (power-user mode). Invalid globs are rejected writing nothing, and a pattern targeting .tomo (always ignored) writes nothing. Because config is read at session startup, adding a rule while a session runs prints a restart hint — restart with tomo stop && tomo sync.
- [PATTERN]
- The path or glob to ignore. Omit to list the effective ignore rules (built-in defaults marked
(default), then your rules). - --json
- Emit machine-readable JSON.
Examples
$ tomo ignore node_modules # bare name → node_modules + node_modules/** $ tomo ignore 'build/**' # glob → added verbatim, one rule $ tomo ignore # list the effective ignore rules
$ tomo ignore node_modules ✓ added ignore rule `node_modules` (+ `node_modules/**` for its contents)
tomo attach #
Join the running session from any terminal — the TUI by default, --plain for the line stream, --json for raw event records. Detaching (q/d/Ctrl-C) never disturbs the session. Multiple terminals can attach at once.
tomo stop #
Cleanly stop the running session (idempotent — a second stop is a friendly no-op). Falls back to SIGTERM if the control socket is unresponsive.
tomo logs #
Tail the session log (.tomo/logs/session.log) — the detached session's stream. -f follows.
tomo events #
Stream the running session's control-channel event feed — human lines by default, versioned JSON records with --json (connect/disconnect, synced, conflict with id, transfer progress, heartbeat). The scriptable twin of the TUI: everything the TUI shows rides this same feed.
tomo db check #
Inspect the history database. db check verifies the integrity of the history store — every recorded version's chunks are present and re-hash to their expected BLAKE3 ids. Exit 0 when healthy, 1 on problems found.
- --json
- Emit machine-readable JSON.
$ tomo db check history OK: 412 versions, 1043 chunks verified
tomo update #
Update Tomo to the latest release, in place. It mirrors the installer: it detects this platform's release asset, fetches the release SHA256SUMS, and compares the published hash of that asset against the SHA-256 of the running binary — a content check, never a version-number compare. If they differ it downloads the asset, verifies its checksum (a mismatch aborts with nothing replaced), and atomically swaps it over the running executable. upgrade is an accepted alias.
- --check
- Report whether an update is available (with short content hashes and, best-effort, the latest tag) without installing it.
$ tomo update --check update available current fe1f4be3d62c latest ac5b3bf59ce0 $ tomo update updated tomo 0.1.6 -> 0.2.0
Running sessions keep the old version until restarted; the remote peer auto-updates at the next connect (the bootstrap re-pushes on any version skew). The download base defaults to the GitHub latest-release URL and is overridable via TOMO_UPDATE_BASE (the test hook). If the binary lives somewhere you can't write, update says so and points you back at the installer.
tomo bug #
File a bug report against Tomo's issue tracker (github.com/jakequist/tomo). With no arguments it opens the blank new-issue page in your browser. With a -m message (title optional — derived from the message's first line when you omit -t) it files the report: through the gh CLI when one is installed and authenticated (printing the issue URL it returns), otherwise by opening a prefilled issues/new URL. A -t title alone prefills a title-only URL.
Only the tool version and target platform are attached — a small tomo <version> · <triple> footer — never logs, paths, or environment. When you're reporting a sync problem, pasting the output of tomo logs -n 50 into the issue body helps a lot. On a headless server with no browser (or with --print), the URL is printed on stdout with a one-line prompt — copy it anywhere to file the issue. Works anywhere; no initialized project required.
- -t, --title <TITLE>
- The issue title. Derived from the message's first line (truncated to 72 chars) when omitted.
- -m, --message <MESSAGE>
- The issue body. Its presence is what triggers filing, rather than just opening the blank page.
- Print the URL instead of opening a browser (the script-friendly fallback; handy on headless servers).
$ tomo bug -t "sync stalls on large files" -m "repro: 2 GB file, both sides idle after 40%" https://github.com/jakequist/tomo/issues/128 $ tomo bug --print # headless: print the new-issue URL open this to file the issue: https://github.com/jakequist/tomo/issues/new
tomo completions <SHELL> #
Print a shell completion script to stdout. Generate for your shell and source it. Safe to pipe: output stops cleanly on a closed reader. Supported shells: bash, elvish, fish, powershell, zsh.
$ tomo completions bash > ~/.local/share/bash-completion/completions/tomo $ tomo completions zsh > "${fpath[1]}/_tomo"
tomo dev <…> — diagnostics #
Hidden developer/diagnostic commands, not part of the stable surface. Two are worth knowing:
- tomo dev ssh-route <TARGET>
- Resolve an SSH target through
~/.ssh/configand print the route Tomo would take — the direct analogue ofssh -G <target>, for diffing. Per hop: alias, resolved hostname/port/user, identity files,StrictHostKeyChecking, the known-hosts files consulted, and theProxyJumpchain. Pure resolution, no network. Takes--json. - tomo dev embedded-binaries
- List the release binaries embedded into this build's bootstrap payload. Empty in ordinary dev builds; populated only in a fat release build. Used to verify a fat binary's embedded inventory. Takes
--json.
See Configuration → Troubleshooting for when to reach for ssh-route.