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.
Prerequisites
Section titled “Prerequisites”- A folder bound by
hlix import— otherwise every command here exitsnot_initialized - A credential that reaches the bound workspace
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, defaultfalse): print the divergence and the verdict the real command would reach, and change nothing. Exits1when the verdict isconflict,0otherwise — including when the real run would stop to ask.--force(boolean,pullonly, defaultfalse): replace local files with the cloud revision. States “replace my changes”. Without local changes it replaces nothing and does not prompt.--yes(boolean,pullonly, defaultfalse): states “do not ask”. Separate from--forcedeliberately — the flag that deletes local files used to be the same flag that skipped every question.--json(boolean, all three, defaultfalse): 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) — modifiedcloud generation 3 (rev_01J8Z4)files 412 local, 410 cloud — 3 local-only, 1 cloud-only, 2 differingThe verdict table
Section titled “The verdict table”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.
approvalRequired
Section titled “approvalRequired”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.
Conflicts and validation
Section titled “Conflicts and validation”- A scan blocker aborts all three with
scan_blockedbefore any comparison — a folder that cannot be scanned cannot be reconciled. pushuses a compare-and-swap against the expected revision ID and generation. A cloud head that moved exitsconflict:Cloud changed since the local base. Run `hlix pull` or resolve with `hlix sync`.pushwith an unchanged manifest is a no-op returningaction: "none"— it does not manufacture an empty revision.- An ordinary
pullrefuses over local changes:Local files changed since the last sync. Push them, or use `hlix pull --force` to replace them. pull --forceover local changes asks first, listing the cloud generation, how many local-only files will be deleted, and how many will be replaced. A--jsonor non-interactive caller cannot answer and exitsapproval_requiredpointing at--dry-run; a declined prompt exitscancelled.- 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.
syncnever reaches the forced-pull gate: it only pulls when the working tree is unchanged.
If reconciliation fails
Section titled “If reconciliation fails”not_initialized—This folder is not bound to an imported Hlix project. Run `hlix import .` first.conflictonsync—Both local and cloud changed since the last sync. Pull or push explicitly after reviewing the conflict.Runhlix pull --dry-runto see the file counts on each side first.conflictonpush— the cloud moved. Pull or sync, then push again.approval_requiredonpull --force— review withhlix pull --dry-run, then repeat with--yes.cancelled— the forced-pull prompt was declined. Nothing was replaced.scan_blocked— the same blockersimportreports; 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.