Skip to content

Latest commit

 

History

History
41 lines (38 loc) · 10.9 KB

File metadata and controls

41 lines (38 loc) · 10.9 KB

Commands

Every command is run as ferry <command> (e.g. ferry init).

Command What it does
init First-run setup: locate/clone the config repo into ferry's own space (~/.config/ferry/repo by default; --fresh <dir> puts a new repo somewhere else instead), write ferry's config. On a fresh interactive run (stdin and stdout both ttys) an adoption wizard scans your existing ~/.zshrc and lets you keep it as-is, route it per block (shared / local / drop), or start fresh from a portable starter: nothing is written before the preview confirm, the original is kept in a timestamped ~/.zshrc.ferry-<ts>.bak, and secret-shaped lines are always routed to the out-of-repo secret store or dropped, never seeded. Non-interactively (a non-tty run, or --wizard=off) the same adopt happens without prompts: secrets are extracted to the store automatically (refs listed on stderr) and everything else is kept shared verbatim. The plugin set is zsh (~/.zshrc). init handles setup only; the single guided reconcile walkthrough lives in apply, which init points you to (or runs directly with --apply).
init --yes Assume yes for init's confirmations — the --github create-confirm and the closing apply confirm with --apply. It does not skip the wizard (use --wizard=off for that).
init --wizard=off|interactive|answers:<file> Choose the first-run wizard mode. off skips it (the non-interactive adopt-and-extract fallback); interactive forces the TUI (needs a tty pair); answers:<file> drives every wizard decision from a TOML answers file (same gates, preview, backup, and confirm, no tty needed; schema in Configuration). Default (unset): interactive on a real tty pair, else off.
init --repair Opt into the wizard's repair review: hardcoded /Users/<name> paths to $HOME, duplicate PATH exports, dead source lines: each fix is accepted or declined individually. Needs a running wizard, so it conflicts with --wizard=off and (in interactive mode) a non-tty run — unless --wizard=answers:<file> drives it, which satisfies the consent requirement.
init --github [name] Create a new private GitHub repo via the gh CLI's existing auth and manage it as ferry's HTTPS remote. Needs gh authenticated; ferry stores no token. Always private, never reuses an existing repo, and won't push a file that looks like a secret: the wizard (or the non-interactive fallback) extracts detected secrets to the local store first, so only placeholders are committed and pushed. Add --yes to assent to the create-confirm (needed non-interactively).
apply Reconcile this machine to the repo (deploy dotfiles, terminal settings). On a run that has changes it walks the pending work grouped by domain (each managed domain — dotfiles, agents, terminals, keybindings, emacs, iTerm2 profiles — under its own name), staying quiet when safe and stopping when risky: a safe change (creating a file where none exists, or updating a target whose live content still matches what ferry last deployed) applies automatically, while a risky change — overwriting a file that differs from the last-deployed baseline, adopting a pre-existing file, deploying a value from the secret store, or a conflict (a file edited both locally and in the repo) — halts for confirmation. A conflict is surfaced with its diff like the others, but confirming it does not overwrite the local edit: resolve it with ferry capture (keep the local edit) or ferry apply --force (take the repo's version). In the walkthrough you confirm a domain wholesale, drill into it to see each change's full diff, apply or skip a change this run, or skip it always (remembered per machine in the gitignored .local layer). A clean, in-sync apply prints one line. Non-interactively — or with --skip-wizard — nothing risky is applied unattended: risky changes fail closed (listed, refused, non-zero exit) while the safe subset still applies. Idempotent; safe to re-run. Dependencies install behind apply --deps, and only for the dependency domains declared under [manage]: brew = true for the Homebrew/apt install, npm-globals = true for global npm packages.
apply --skip-wizard Skip the guided walkthrough (for experts and scripts): safe changes still auto-apply, but risky changes are refused rather than prompted — they never happen unattended.
apply --force Treat every risky change as confirmed (an explicit override) and overwrite uncaptured local edits on a conflict; the downstream data-loss guards still apply and warn.
capture Pull local changes back into the repo. Interactive: approve each change, route it shared (synced everywhere) or local (this machine only). For sources that reference stored secrets, capture compares against the rendered content and splices your edits back around the placeholders, so stored values never re-enter the repo and a store-routed secret never blocks its own round-trip. It also captures edits to deployed agent files (routing to their shared source or a local/agents/ overlay, refusing a true divergence with a diff) and offers to adopt new agent-shaped files it finds; see The agents domain.
sync Publish captured changes and pull remote ones for a managed repo, in one command. Integrates the remote first, never force-pushes, gates the whole push range for secrets, and leaves your machine unchanged on a conflict. ferry manages a single-branch config repo: sync integrates and pushes main, and refuses to run on a detached HEAD or with any other branch checked out. A repo not marked managed — anything not set up by ferry init --github — needs --allow-unmanaged. Run ferry apply after to deploy pulled changes.
status Report config drift (what changed on this machine).
doctor Report machine/tool health, and observe ferry's managed-target invariants read-only: no deployed target is a symlink (ferry deploys regular-file copies), none resolves under ~/.ssh, and every target resolves inside $HOME. A genuine breach is reported [fail] and exits non-zero; a machine with nothing managed yet is advisory.
diff Preview what apply would change.
restore Reverse ferry's changes, returning the machine to its pre-ferry state from an automatic backup.
bundle Parent noun for moving the config repo offline as a portable bundle. Its subcommands are bundle export and bundle import.
bundle export Write a portable, secret-scanned .zip bundle of the repo's tracked shared files for an offline move. Prints the bundle's reproducible SHA256 — exporting the same tracked sources always yields the same digest (no timestamps or randomness are bundled), so the digest can be recomputed and verified with bundle import --expect-sha256. Never includes secrets, ~/.ssh, or the local layer (unless --include-local).
bundle import Ingest a bundle into a fresh config repo (~/.config/ferry/repo by default), validate it fully, then write ferry's config. Refuses a non-empty target. --expect-sha256 <hash> verifies integrity.
work Parent noun for carrying a project's in-flight work state — handover note, run journal, per-project agent memory, redacted transcripts — between accounts as an explicit baton pass. Needs a [work] store in ~/.config/ferry/config.toml. See The work domain.
work pack <project-dir> Bundle the project's work state into the cargo store. Refuses without a handover note (--allow-empty permits memory/transcript-only cargo) and stops on any high-confidence secret finding — --exclude <item> leaves an item out (recorded in the manifest), --acknowledge <item>/<path> lets a flagged file travel, pinned to its current content. Records a sidecar handover marker and applies keep-last-N retention after storing.
work receive <project-dir> Land the project's latest cargo (by pack sequence, never timestamps) on this account. Every write is backed up first and the receive is snapshotted. Guarded items refuse when the destination changed since this account last held the baton (--force overrides); transcripts union-merge (never overwrite, never delete). On the account that packed, it is a take-back: the handover marker is cleared and nothing is restored. An equal-seq tie refuses until one bundle is named (--bundle <sha256>) or pruned; a taken-back bundle refuses without --force.
work status [<project-dir>] Show the project's cargo, both accounts' claims, handover-marker freshness ("handed over, not modified since" vs modified after), drift against this account's baseline, store size, and other projects present in the store. Read-only.
work prune [<project-dir>] Apply keep-last-N retention now (--keep <n>, default from the [work] table, else 5), or remove one bundle exactly with --bundle <sha256>.
work restore <project-dir> Revert exactly the last work receive on this account from its per-receive snapshot. (ferry restore work instead reverts all work-verb writes to the pre-ferry baseline.)
agents Parent noun for onboarding project repos and migrating agent-instruction setups. Its subcommands are agents scaffold and agents adopt.
agents scaffold Set up a project repo for AI-agent work from the templates in the config repo's agents/ area: an AGENTS.md router, CLAUDE.md/GEMINI.md bridges, and committed .abcd/work/ memory files. --private instead creates a .abcd/.work.local/ layer hidden via .git/info/exclude — for repos you don't own, it leaves zero tracked trace. --attribution (mutually exclusive with --private) instead installs a prepare-commit-msg hook that appends a kernel-style Assisted-by: trailer to agent-authored commits, for repos that require AI disclosure. Idempotent; never overwrites or repoints anything it didn't create. Works in linked worktrees and submodules.
agents adopt One-time migration of an existing symlink-based instruction setup into the config repo: imports the source files (never modifying the source directory), then swaps each $HOME bridge symlink for a ferry-managed copy in a single journalled transaction — any failure rolls back and the symlinks return. Refuses directory-level bridges with exact instructions rather than writing through them.
version Print the version; --verbose adds the Go version and platform.

The agents domain itself (which instruction files deploy where, for which coding CLIs) is enabled with agents = true in the manifest and rides the normal lifecycle: apply deploys, status/diff report drift, capture flows live edits to deployed agent files back to the repo (routing each to its shared source or a per-machine local/agents/ overlay) and refuses only a true divergence, and restore agents reverts the domain even when the config repo is gone. See The agents domain.