Skip to content

hlix push, pull & sync

These three commands reconcile a bound folder with its cloud revision head. push sends local changes as a new immutable revision, pull brings the cloud head down, and sync picks whichever direction is unambiguous and refuses the one that is not.

pull --force is the only command in this CLI that can destroy work which was never uploaded. Two gates sit in front of it.

  • A folder bound by hlix import — otherwise every command here exits not_initialized
  • A credential that reaches the bound workspace
Terminal window
hlix push [--dry-run] [--json]
hlix pull [--force] [--yes] [--dry-run] [--json]
hlix sync [--dry-run] [--json]

None of the three accepts a positional argument. The bound project is found by walking up from --cwd, so running from packages/api acts on the project, not a fragment of it.

  • --dry-run (boolean, all three, default false): print the divergence and the verdict the real command would reach, and change nothing. Exits 1 when the verdict is conflict, 0 otherwise — including when the real run would stop to ask.
  • --force (boolean, pull only, default false): replace local files with the cloud revision. States “replace my changes”. Without local changes it replaces nothing and does not prompt.
  • --yes (boolean, pull only, default false): states “do not ask”. Separate from --force deliberately — the flag that deletes local files used to be the same flag that skipped every question.
  • --json (boolean, all three, default false): the dry-run payload is { dryRun: true, action, approvalRequired, ...divergence }; a real run returns its action and the resulting revision identity.

Expected result from a dry run:

sync --dry-run: Would push the local changes as a new cloud revision.
local generation 3 (rev_01J8Z4) — modified
cloud generation 3 (rev_01J8Z4)
files 412 local, 410 cloud — 3 local-only, 1 cloud-only, 2 differing

Both sides are compared against the folder’s last synced base revision. local changed means the manifest hash moved; cloud changed means the head’s revision ID or generation moved.

local changed cloud changed push pull sync
no no none none none
yes no push none push
no yes conflict pull pull
yes yes conflict conflict, or pull with --force conflict

A dry run computes this with the same function the real command uses, so it cannot promise an outcome the command would refuse.

conflict is the only verdict that exits 1. Every other dry run exits 0, including one that reports it would have to ask first.

A forced pull over local changes adds one line to the human output, verbatim:

pull --dry-run: Would replace local files with the cloud revision.
Requires --yes (or interactive approval) to execute.

and one field to the envelope’s data:

{"dryRun":true,"action":"pull","approvalRequired":true}

That run exits 0. Needing an answer is not a refusal — a caller who has one executes exactly this plan.

  • A scan blocker aborts all three with scan_blocked before any comparison — a folder that cannot be scanned cannot be reconciled.
  • push uses a compare-and-swap against the expected revision ID and generation. A cloud head that moved exits conflict: Cloud changed since the local base. Run `hlix pull` or resolve with `hlix sync`.
  • push with an unchanged manifest is a no-op returning action: "none" — it does not manufacture an empty revision.
  • An ordinary pull refuses over local changes: Local files changed since the last sync. Push them, or use `hlix pull --force` to replace them.
  • pull --force over local changes asks first, listing the cloud generation, how many local-only files will be deleted, and how many will be replaced. A --json or non-interactive caller cannot answer and exits approval_required pointing at --dry-run; a declined prompt exits cancelled.
  • After a pull, the local baseline is recomputed from the checked-out tree rather than the source manifest — the bundle checkout is a synthetic snapshot commit whose tracked flags can differ.
  • sync never reaches the forced-pull gate: it only pulls when the working tree is unchanged.
  • not_initializedThis folder is not bound to an imported Hlix project. Run `hlix import .` first.
  • conflict on syncBoth local and cloud changed since the last sync. Pull or push explicitly after reviewing the conflict. Run hlix pull --dry-run to see the file counts on each side first.
  • conflict on push — the cloud moved. Pull or sync, then push again.
  • approval_required on pull --force — review with hlix pull --dry-run, then repeat with --yes.
  • cancelled — the forced-pull prompt was declined. Nothing was replaced.
  • scan_blocked — the same blockers import reports; see If the scan blocks the import.
  • Revision upload has no bundle target. — the API accepted the revision but returned no upload URL. Retry; the idempotency key makes that safe.