sorrel/docs/cli

The sorrel CLI

A persistent, single-user local VCS over the real engine. Every command reads and writes content-addressed objects under .sorrel/; state survives process restarts; everything supports --json for tools and agents.

The core loop

$ sorrel init                      # .sorrel/: object store + manifest + HEAD
$ echo "hello" > a.txt
$ sorrel status                    # dirty: added a.txt (real diff vs HEAD)
$ sorrel change create -m "add a"  # snapshot, diff, Change object, advance HEAD
$ echo "world" >> a.txt
$ sorrel diff                      # line-level unified hunks vs HEAD
$ sorrel change create -m "edit a"
$ sorrel log                       # snapshot DAG walk with change ids,
                                   # authors, and messages

status and change create use the engine's stat cache, so unchanged files are never re-hashed. log resolves each snapshot to the Change that produced it through a small index in .sorrel/changes.index.

Lanes: parallel work without branch noise

$ sorrel lane create --name agent/feature
$ sorrel lane list                 # every lane, its head, the active one marked
$ sorrel lane switch <lane-id>     # restores that lane's tree (refuses if dirty)
$ sorrel lane submit               # create a Hub proposal for the active lane
$ sorrel stack create --name series <change-id>
$ sorrel stack show <stack-id>

Each lane has its own head under .sorrel/heads/. Two lanes advance independently — exactly what parallel agents need to work without stepping on each other.

Merge: fast-forward or real three-way

$ sorrel merge <lane-id>
# fast-forward when the active lane has not diverged
# otherwise: engine three-way merge (merge base + line merge)

# on conflicts:
#   - conflicted files get <<<<<<< ours / ======= / >>>>>>> theirs markers
#   - .sorrel/MERGE_STATE records the stored MergeResult object
#   - HEAD does not move
$ sorrel merge --continue          # after resolving markers, complete the merge
# or:
$ sorrel merge --abort             # restore the pre-merge tree

Clean merges write a merged snapshot with both parents and record a merge Change. Conflicted merges leave inspectable state: marker-annotated files in the tree and first-class Conflict objects in the store.

Git bridge: import, export, and colocated sync

$ sorrel git import                 # Git commits → Sorrel snapshots
$ sorrel git export ./mirror.git   # Sorrel snapshots → Git commits
$ sorrel git sync                  # align colocated .git/ and .sorrel/

Import and export are incremental and record SHA-to-snapshot mappings in .sorrel/git-map.json. Colocated sync fast-forwards the side that has not moved; true divergence is imported onto a normal git/<branch> lane for the usual Sorrel merge flow.

Hub sync: push and pull

$ sorrel remote add origin http://127.0.0.1:3000
$ sorrel push origin               # negotiate missing objects, upload, advance ref
$ sorrel pull origin               # fetch refs, download closure, verify ids

Push seeds the local snapshot closure, asks the Hub which objects are missing, uploads only those, and advances the remote ref with fast-forward and closure checks. Pull verifies every downloaded object's content id before trusting it.

Workflows, policy, slices

$ sorrel workflow validate         # parse + check sorrel.workflow.yml
$ sorrel workflow run <job>        # execute locally, gated by Core policy
$ sorrel policy evaluate ...       # Core policy decision for an action
$ sorrel grant create ...          # record a real Core grant decision
$ sorrel slice create ...          # slice manifest for a subproject

Secrets, environments, and run logs

$ sorrel secret list                # handles only
$ sorrel secret sync                # refresh secretspec.toml
$ sorrel secret check --provider dotenv:.env
$ sorrel env info                   # devenv or local-fallback
$ sorrel workflow run test
$ sorrel run list
$ sorrel run show <run-id>
$ sorrel run logs <run-id>

Secret resolution and injection use upstream SecretSpec only after Core grant checks. Values are injected into the child process, never Sorrel objects, and persisted output under .sorrel/runs/ is redacted. Full devenv task mapping, Hub log streaming, and a hosted secret backend remain future work.

Full walkthroughs live in the repository: DEMO.md (local flow) and SYNC.md (push/pull against sorrel-hub).