Skip to content

Repository files navigation

Zaphod CLI

Zaphod is a small Git workflow tool for developers who often work between two related branches.

It remembers a branch pair, shows where you are, and switches to the other side only when the repository is safe to touch. The goal is not to replace Git. The goal is to make one repetitive workflow easier without hiding what is happening.

Project Status

Zaphod is in early development. The current 0.1.x CLI can:

  • Detect the current Git repository.
  • Pair two branches in repo-local metadata.
  • Show the active pair status.
  • Switch to the paired branch.
  • Guard agent switches against target-branch claim conflicts.
  • Preview a safe switch without changing branches.
  • List, rename, and remove branch pairs.
  • Run preflight checks for humans and coding agents, including current and paired target claim guards.
  • Assert the expected branch, pair, pair side, current claim, or paired target claim before scripted work starts or resumes.
  • Claim, heartbeat, and release a pair/branch for an agent session.
  • Claim, refresh, and release the paired target branch without switching, so guarded agent switches can require an existing target claim.
  • Emit a handoff snapshot for agent-to-agent continuation.
  • Refuse unsafe switches when the worktree is dirty or Git is mid-operation.
  • Emit JSON status, switch, and pair-mutation results for scripts.
  • Diagnose repository, metadata, branch-pair, and claim health in text or JSON.
  • Generate shell completions.

Until Zaphod reaches 1.0, command names and output may change between minor versions.

Why Zaphod?

Git is powerful, but switching between two related branches can become noisy:

  • main and a feature branch.
  • A backend branch and a frontend branch.
  • A review branch and an implementation branch.
  • Two worktrees used for the same task.

Zaphod keeps that relationship explicit:

feature/api  <->  feature/ui

Then it gives you a small set of commands for checking and moving between the two sides safely.

Agentic Coding

Zaphod is not a general Git replacement. For normal human Git users, it is a small safety and convenience tool. Its stronger direction is agent-safe Git workflow guardrails: giving humans and coding agents machine-readable checks, safe branch-pair movement, and clear refusal behavior before touching repository state.

Coding agents can use Zaphod as a preflight gate:

zaphod preflight --agent codex --json
zaphod preflight --agent codex --branch feature/api --side left --json
zaphod preflight --agent codex --require-claim --json
zaphod preflight --agent codex --require-target-claim --json
zaphod preflight --agent codex --stale-after 2h --json
zaphod claim --agent codex --pair api --side left --note "editing API routes" --json
zaphod claim --agent codex --pair api --stale-after 2h --json
zaphod heartbeat --agent codex --pair api --side left --note "adding tests" --json
zaphod heartbeat --agent codex --pair api --stale-after 2h --json
zaphod assert --pair api --side left --json
zaphod assert --pair api --side left --agent codex --require-claim --json
zaphod assert --pair api --agent codex --require-target-claim --json
zaphod handoff --agent codex --require-claim --json
zaphod handoff --agent codex --require-target-claim --json
zaphod handoff --agent codex --side left --stale-after 2h --json
zaphod status --json
zaphod switch --dry-run --json
zaphod switch --agent codex --require-claim --json
zaphod doctor --json

This lets an agent check branch, worktree, merge, rebase, and pair state before editing files or switching branches.

This matters most when an agent is operating from instructions such as "make the API change on the backend branch, then update the UI branch." Without a guard, the agent has to infer whether the current branch is the correct place to edit. With Zaphod, the script can make that expectation explicit:

zaphod assert --pair search --side left --json

If the repository is on the UI side, outside the pair, detached, or missing the expected pair, the command fails before any file edits happen. The agent can then stop, report the mismatch, or ask for human approval instead of continuing on the wrong branch.

When several agents, scripts, or terminals may touch the same repository, claims add a lightweight coordination layer:

zaphod claim --agent codex --pair search --note "backend search endpoint" --json
zaphod claim --agent codex --pair search --target --note "reserve UI side" --json
zaphod claim --agent codex --pair search --stale-after 2h --json
# work on the branch
zaphod preflight --agent codex --require-claim --json
zaphod preflight --agent codex --require-target-claim --json
zaphod assert --pair search --agent codex --require-claim --json
zaphod assert --pair search --agent codex --require-target-claim --json
zaphod handoff --agent codex --require-target-claim --json
zaphod heartbeat --agent codex --pair search --note "writing regression tests" --json
zaphod heartbeat --agent codex --pair search --target --note "still reserving UI side" --json
zaphod heartbeat --agent codex --pair search --stale-after 2h --json
zaphod claims --current --json
zaphod claims --target --json
zaphod claims --conflicts-for codex --current --json
zaphod claims --pair search --side left --json
zaphod claims --pair search --stale-after 2h --json
zaphod prune-claims --pair search --stale-after 2h --json
zaphod prune-claims --current --stale-after 2h --json
zaphod prune-claims --target --stale-after 2h --json
zaphod prune-claims --pair search --side left --stale-after 2h --json
zaphod prune-claims --pair search --stale-after 2h --apply
zaphod prune-claims --orphaned --json
zaphod prune-claims --orphaned --apply
zaphod unclaim --agent codex --pair search
zaphod unclaim --agent codex --pair search --branch feature/api
zaphod unclaim --agent codex --pair search --side left
zaphod unclaim --agent codex --pair search --target

Claims are local metadata only. They do not lock Git, modify branches, or delete anything. They make accidental overlap visible so another agent can refuse to start on the same pair and branch.

Use --note on claim or heartbeat when an agent should leave short local context for the next person or process that sees the claim. Notes are optional, stored in repo-local metadata, and surfaced in JSON claim reports.

Agent-aware switching extends that refusal to branch movement. With zaphod switch --agent codex, Zaphod checks the target branch before switching and refuses if another agent owns that pair/branch claim. With --require-claim, the target branch must already be claimed by the requested agent before the switch can happen.

Stale-claim filters help automation notice abandoned sessions after crashes, terminal closes, or interrupted agent runs. They are read-only, so cleanup still requires an explicit unclaim or prune-claims --apply.

Long-running agents can call heartbeat while work is in progress. It refreshes only an existing claim owned by that agent on the current pair and branch, so fresh sessions stay visible without allowing silent takeover of another claim.

For handoffs between agents or terminals, zaphod handoff --json captures the current branch, selected pair status, active claims, and claim readiness in one read-only report. With --stale-after, the handoff can also mark old claim conflicts as stale without removing them. With --branch or --side, the handoff can also prove the snapshot was captured from the expected branch or pair side before another agent trusts it. When --agent is present, handoff JSON also includes target_claim, so the receiving agent can see whether the paired target branch is already claimed before it attempts a switch. With --require-target-claim, handoff refuses unless that paired target branch is already claimed by the requested agent.

Safety Model

Zaphod is intentionally cautious.

The CLI should refuse operations that could surprise the user or disturb local work. In particular, switching should fail when:

  • The current directory is not inside a Git repository.
  • The worktree has uncommitted changes.
  • Git is in the middle of a merge or rebase.
  • The current HEAD is detached.
  • The current branch is not part of a known pair.
  • The target branch cannot be found.

Forceful behavior may be added later, but the first release prioritizes clear refusals over convenience.

Installation

GitHub Release Binaries

Download the latest binary for your platform from the GitHub Releases page.

Linux:

curl -LO https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-linux
curl -LO https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-linux.sha256
sha256sum -c zaphod-linux.sha256
chmod +x zaphod-linux
sudo install -m 0755 zaphod-linux /usr/local/bin/zaphod

macOS:

curl -LO https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-macos
curl -LO https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-macos.sha256
shasum -a 256 -c zaphod-macos.sha256
chmod +x zaphod-macos
sudo install -m 0755 zaphod-macos /usr/local/bin/zaphod

Windows PowerShell:

Invoke-WebRequest -Uri https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-windows.exe -OutFile zaphod-windows.exe
Invoke-WebRequest -Uri https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-windows.exe.sha256 -OutFile zaphod-windows.exe.sha256
$expected = (Get-Content .\zaphod-windows.exe.sha256).Split(' ')[0]
$actual = (Get-FileHash .\zaphod-windows.exe -Algorithm SHA256).Hash.ToLower()
if ($actual -ne $expected) { throw "checksum mismatch" }
New-Item -ItemType Directory -Force "$HOME\bin"
Move-Item .\zaphod-windows.exe "$HOME\bin\zaphod.exe"

Add $HOME\bin to your PATH if it is not already there.

Then confirm the binary is available:

zaphod --version
zaphod --help

From Source

Zaphod is not published to crates.io yet. Install from a local checkout:

cargo install --path .

Then confirm the binary is available:

zaphod --help

Quickstart

Inside a Git repository with two existing local branches:

zaphod init feature/ui
zaphod status
zaphod switch

init uses the current branch as one side of the pair. You can also name both branches explicitly:

zaphod pair feature/api feature/ui
zaphod status
zaphod switch

Example status output:

Pair: default
Current: feature/api
Other: feature/ui
Worktree: clean
Git state: ready
Switch: allowed

If the worktree is dirty, switching is refused:

Pair: default
Current: feature/api
Other: feature/ui
Worktree: dirty
Git state: ready
Switch: refused (worktree has uncommitted changes)

Workflows

Solo Development

Pair your main integration branch with the branch you are actively changing:

zaphod pair main feature/search
zaphod status
zaphod switch

This keeps the relationship explicit while still letting Git own the actual branch state.

Split Work

Pair two branches that represent different sides of the same task:

zaphod pair feature/api feature/ui --name search
zaphod status --name search
zaphod switch --name search

This is useful when backend and frontend changes move together, but should stay reviewable as separate branches.

Review Work

Pair an implementation branch with a review or experiment branch:

zaphod pair feature/parser review/parser-notes --name parser-review

Use zaphod list when a repository has multiple named pairs.

Pair Naming

Pair names are local labels stored in the repository metadata. Names may contain letters, numbers, ., _, and -.

Good names are short and tied to the workflow:

default
api
search-ui
parser-review

Avoid names that depend on one person's machine or temporary context.

Commands

zaphod pair <left> <right>

Store a branch pair in the current repository:

zaphod pair feature/api feature/ui

Use --name to store more than one pair:

zaphod pair main feature/api --name api

Both branches must already exist locally. Branch names are validated with Git's branch-name rules before Zaphod stores the pair.

Use --json to emit a structured report with the action, repository root, metadata path, stored pair, and previous pair when an existing name is updated.

zaphod init <other>

Store a branch pair using the current branch and another local branch:

zaphod init feature/ui
zaphod init feature/ui --name api

Both branches must already exist locally. init refuses detached HEADs, invalid branch names, missing branches, and attempts to pair the current branch with itself.

Use --json to emit the same pair-mutation report as pair, with "action": "initialized" for newly stored pairs.

zaphod preflight

Check whether the current repository is ready for paired-branch work:

zaphod preflight --json
zaphod preflight --agent codex --json
zaphod preflight --branch feature/api --side left --json
zaphod preflight --agent codex --require-claim --json
zaphod preflight --agent codex --require-target-claim --json
zaphod preflight --agent codex --stale-after 2h --json

Preflight is read-only. It reports the requested pair, current branch, paired target branch, worktree state, Git operation state, switch readiness, and any refusal reasons. It exits successfully when the pair is ready and exits with an error when the repository is not ready.

Use --agent to check whether another agent has already claimed the current pair and branch. This does not create or remove a claim; it only reports whether claiming would be allowed. JSON output also includes target_claim readiness for the paired branch that zaphod switch would move to, so an agent can see target-branch claim conflicts before it attempts a switch.

Use --require-claim with --agent when an agent is resuming work and must prove it already owns the current pair and branch claim. This is read-only: it does not refresh the claim timestamp. JSON reports include claim_required, claim_owned, and owned_claim fields.

Use --require-target-claim with --agent when startup checks must prove the paired target branch is already reserved by the same agent before the agent continues. This is useful before workflows that expect a later guarded switch to the other branch.

Use --branch or --side when automation must prove it is about to edit the intended branch before doing any work:

zaphod preflight --agent codex --name api --side left --json

The report includes an expectation section when either option is used. A wrong branch or wrong pair side makes preflight fail before an agent starts editing files.

When --agent is present, preflight also reports the repo-local metadata mutation lock. If .git/zaphod/metadata.lock is present, preflight marks claim readiness as blocked because claim would refuse to write while another Zaphod process appears to hold the metadata lock.

Use --stale-after with --agent to mark old claim conflicts in the preflight report. Preflight still refuses the conflict; stale reporting is only a signal for scripts or humans to decide whether an explicit unclaim is appropriate. The same stale marker is included on target_claim when the paired target branch is claimed by another agent.

zaphod assert

Fail fast when the current repository state does not match an expected branch, pair, or pair side:

zaphod assert --branch feature/api
zaphod assert --pair api
zaphod assert --side left
zaphod assert --pair api --side right --json
zaphod assert --pair api --agent codex --require-claim --json
zaphod assert --pair api --agent codex --require-target-claim --json

If no selector is provided, assert checks that the current branch belongs to the default pair. --side uses the default pair unless --pair is provided.

This command is read-only and is designed for scripts and coding agents that need to prove they are in the right place before editing files.

With --agent, assert also checks claim readiness for the current pair and branch. With --require-claim, the requested agent must already own that claim. This is useful during long-running work because unlike preflight, it does not require a clean worktree and does not refresh the claim timestamp.

Use --require-target-claim with --agent when the script must prove the paired target branch is already reserved by the same agent before continuing. This is useful after claim --target and before handing work to a script that may later switch branches.

zaphod claim

Claim the current pair and branch for an agent session:

zaphod claim --agent codex --pair api
zaphod claim --agent codex --pair api --json
zaphod claim --agent codex --pair api --note "editing API routes" --json
zaphod claim --agent codex --pair api --clear-note --json
zaphod claim --agent codex --pair api --target --json
zaphod claim --agent codex --pair api --side left --json
zaphod claim --agent codex --pair api --stale-after 2h --json

claim writes repo-local metadata under .git/zaphod/claims.toml. It refuses when another agent has already claimed the same pair and current branch. It also uses the same readiness checks as preflight, so it refuses when the worktree is dirty, Git is mid-merge or mid-rebase, the current branch is outside the pair, or the paired target branch is missing.

Agent names may contain only letters, numbers, ., _, and -.

Use --note to attach short local context to the claim. Notes are optional, must be non-blank, cannot contain control characters, and are limited to 240 characters. Use --clear-note to remove an existing note while keeping the claim.

Use --branch or --side when the claim should only be written from a specific branch or pair side. This lets an agent combine "am I in the right place?" and "claim this work" into one guarded command.

Use --target to claim the paired branch that zaphod switch would move to without switching branches. This lets an agent reserve the other side of a paired task before running zaphod switch --agent <name> --require-claim.

Use --stale-after to add conflict_stale to JSON conflict reports. This is a reporting aid only: claim still refuses the conflict and never takes over or removes another agent's claim automatically.

zaphod heartbeat

Refresh an existing agent session claim for the current pair and branch:

zaphod heartbeat --agent codex --pair api
zaphod heartbeat --agent codex --pair api --json
zaphod heartbeat --agent codex --pair api --note "adding tests" --json
zaphod heartbeat --agent codex --pair api --clear-note --json
zaphod heartbeat --agent codex --pair api --target --json
zaphod heartbeat --agent codex --pair api --side left --json
zaphod heartbeat --agent codex --pair api --stale-after 2h --json

heartbeat updates the claim timestamp in .git/zaphod/claims.toml so stale-claim checks can distinguish an active long-running session from an abandoned one. It requires the claim to already exist, refuses if another agent owns the same pair and branch, and does not switch branches, clean files, or change Git history.

Unlike claim, heartbeat can run while the worktree is dirty. This lets an agent refresh its own claim while it is actively editing files.

Use --note to replace the existing claim note while refreshing the timestamp. Use --clear-note to remove the note while keeping the claim active. When no note option is provided, heartbeat preserves the existing note.

Use --branch or --side to refuse the refresh unless the current branch is still the expected branch or pair side.

Use --target to refresh an existing claim on the paired branch that zaphod switch would move to without switching branches. This keeps a reserved target branch fresh while the agent continues work on the current branch.

Use --stale-after to add conflict_stale to JSON conflict reports. This is a reporting aid only: heartbeat still refuses the conflict and never takes over or removes another agent's claim automatically.

zaphod claims

List active agent session claims:

zaphod claims
zaphod claims --json
zaphod claims --current --json
zaphod claims --target --json
zaphod claims --conflicts-for codex --current --json
zaphod claims --pair api --side left --json
zaphod claims --agent codex --pair api --branch feature/api --json
zaphod claims --pair api --stale-after 2h --json

Use filters when a script needs to check a specific agent, pair, branch, the current branch, paired target branch, pair side, conflict owner, or stale-claim window without parsing unrelated claim entries. Use --target to inspect claims on the paired branch that zaphod switch would move to without switching branches. Use --side left or --side right to inspect claims for a pair side without switching branches. Durations use a positive number followed by s, m, h, or d.

Use --conflicts-for <agent> to show claims owned by other agents. Combined with --current, --branch, or --side, this gives agents a read-only blocking-claim summary before they start or resume work.

zaphod prune-claims

Preview or remove stale or orphaned agent session claims:

zaphod prune-claims --stale-after 2h
zaphod prune-claims --pair api --stale-after 2h --json
zaphod prune-claims --current --stale-after 2h --json
zaphod prune-claims --target --stale-after 2h --json
zaphod prune-claims --pair api --side left --stale-after 2h --json
zaphod prune-claims --pair api --stale-after 2h --apply
zaphod prune-claims --orphaned --json
zaphod prune-claims --orphaned --apply

prune-claims defaults to dry-run mode and does not change metadata unless --apply is present. Use --agent, --pair, and --branch to narrow the cleanup scope. Use --current to narrow cleanup to the current Git branch without scripting a separate branch lookup. Use --target to narrow cleanup to the paired branch that zaphod switch would move to without switching branches. Use --side left or --side right to clean claims for a pair side without switching branches. Use --orphaned to match claims for missing pairs, branches outside their configured pair, or local branches that no longer exist. The command only edits .git/zaphod/claims.toml; it never switches branches, deletes files, or changes Git history.

zaphod unclaim

Release an agent session claim for the current pair and branch:

zaphod unclaim --agent codex --pair api
zaphod unclaim --agent codex --pair api --json
zaphod unclaim --agent codex --pair api --branch feature/api
zaphod unclaim --agent codex --pair api --target
zaphod unclaim --agent codex --pair api --side left

By default, unclaim releases the matching claim for the current branch. Use --branch to release a claim for another branch without switching to it. Use --target to release a claim on the paired branch that zaphod switch would move to without switching branches. Use --side to release a claim by the left or right side of the named pair without scripting a pair-metadata lookup. This is useful when an agent stopped early and left stale claim metadata behind.

unclaim only removes the matching claim metadata. It does not switch branches, clean files, or alter Git history.

zaphod handoff

Emit a read-only snapshot for another agent, script, or terminal to continue from:

zaphod handoff
zaphod handoff --json
zaphod handoff --branch feature/api --side left --json
zaphod handoff --name api --agent codex --json
zaphod handoff --name api --agent codex --require-claim --json
zaphod handoff --name api --agent codex --require-target-claim --json
zaphod handoff --name api --agent codex --stale-after 2h --json

The handoff report includes repository root, current branch, worktree state, Git operation state, selected pair status, active claims, and optional claim readiness for the requested agent. When --agent is present, JSON output also includes target_claim readiness for the paired branch that zaphod switch would move to. It does not create claims, remove claims, or switch branches.

Use --stale-after with --agent when the receiving agent needs to know whether a claim conflict looks abandoned. The value uses the same duration format as other stale-claim checks: a positive number followed by s, m, h, or d.

Use --require-claim with --agent when the receiving agent should only trust the snapshot if that agent already owns the current pair and branch claim. This is read-only: it does not refresh the claim timestamp or change claim metadata.

Use --require-target-claim with --agent when the receiving agent should only trust the snapshot if that agent already owns the paired target branch claim. This is useful after reserving the other side with claim --target because it can run while the current worktree is dirty and does not switch branches.

Use --branch and --side when the receiving agent should only trust a handoff captured from a specific branch or pair side. The JSON report includes an expectation section when either option is used. If the expectation fails, handoff exits non-zero and reports the mismatch without creating claims, removing claims, switching branches, or editing Git state.

When --agent is present, the handoff claim-readiness section includes the same metadata lock state as preflight --agent.

zaphod status

Show the active pair status:

zaphod status

To inspect every configured pair in the repository, use --all:

zaphod status --all

For scripts, use JSON output:

zaphod status --json
zaphod status --all --json

JSON fields use script-friendly names:

{
  "pair": "default",
  "current": "feature/api",
  "other": "feature/ui",
  "worktree": "clean",
  "git_state": "ready",
  "switch_allowed": true,
  "refusal_reasons": []
}

When switching is refused, switch_allowed is false and refusal_reasons contains one or more values:

{
  "pair": "default",
  "current": "feature/api",
  "other": "feature/ui",
  "worktree": "dirty",
  "git_state": "ready",
  "switch_allowed": false,
  "refusal_reasons": ["dirty_worktree"]
}

Known refusal reasons are dirty_worktree, merge_in_progress, rebase_in_progress, and target_branch_missing.

status --all --json returns an array. Each item includes the pair's left and right branch names, whether the pair is active for the current branch, branch existence booleans, and switch availability details. Inactive pairs use current_branch_not_paired as the refusal reason.

zaphod switch

Switch to the other branch in the pair:

zaphod switch

Zaphod refuses to switch if the worktree is dirty, a merge is in progress, or a rebase is in progress. It also refuses when the paired target branch no longer exists. Use zaphod status to see the current refusal reason before switching.

To preview the target without changing branches, use --dry-run:

zaphod switch --dry-run

Dry-run mode applies the same safety checks as a real switch.

For agent sessions, --agent checks the target branch claim before switching:

zaphod switch --agent codex
zaphod switch --agent codex --require-claim

With --agent, Zaphod refuses to switch into the target branch when another agent owns that pair/branch claim. With --require-claim, the requested agent must already own the target branch claim before the switch can happen.

For scripts, use JSON output:

zaphod switch --json
zaphod switch --dry-run --json
zaphod switch --agent codex --require-claim --json

When switching is refused, JSON output still reports the pair, current branch, target branch, worktree state, Git state, and refusal reasons before exiting with the same safety-refusal code.

zaphod list

List all branch pairs configured for the current repository:

zaphod list

For scripts, use JSON output:

zaphod list --json

zaphod rename <old> <new>

Rename a branch pair label without changing either Git branch:

zaphod rename default api

Zaphod refuses to overwrite an existing pair name. Pair names must contain only letters, numbers, ., _, and -.

Use --json to emit the renamed pair and its previous name in a structured pair-mutation report.

zaphod doctor

Diagnose Git availability, repository state, metadata health, and configured branch pairs:

zaphod doctor

doctor is read-only. It exits successfully when the repository and configured pairs look healthy, and exits with an error when it finds problems such as corrupt metadata or missing paired branches.

For scripts, use JSON output:

zaphod doctor --json
zaphod doctor --stale-after 2h --json

Use --stale-after to make doctor report stale agent claims as repository health problems. This is read-only; cleanup still requires unclaim or prune-claims --apply.

doctor also reports claim metadata that no longer matches the repository, including claims for removed pairs, claims on branches outside the configured pair, and claims on local branches that no longer exist.

doctor reports the repo-local metadata mutation lock as clear or locked. A present .git/zaphod/metadata.lock makes the report unhealthy because pair and claim write commands will refuse to run while another Zaphod process appears to hold the lock.

zaphod unpair

Remove a branch pair:

zaphod unpair

Use --name to remove a named pair:

zaphod unpair --name api

Use --json to emit the removed pair and metadata path instead of prose.

zaphod completions <shell>

Generate shell completions to stdout:

zaphod completions bash
zaphod completions zsh
zaphod completions fish
zaphod completions powershell
zaphod completions elvish

Redirect the output to the location expected by your shell.

Exit Codes

Zaphod uses stable app-level exit codes for scripts:

0  success
1  runtime or unexpected failure
2  invalid input, missing branch, missing pair, or incompatible repository state
3  safety refusal, such as a dirty worktree or in-progress merge
4  doctor found repository, metadata, or pair health problems

Use --json-errors to emit app-level failures as JSON on stderr:

zaphod --json-errors switch

Example error output:

{"error":{"exit_code":3,"kind":"switch_refused","message":"refusing to switch: worktree has uncommitted changes"}}

Demo Transcript

$ git branch --show-current
feature/api

$ zaphod pair feature/api feature/ui
Paired 'default': feature/api <-> feature/ui

$ zaphod status
Pair: default
Current: feature/api
Other: feature/ui
Worktree: clean
Git state: ready
Switch: allowed

$ zaphod switch
Switched pair 'default': feature/api -> feature/ui

Metadata

Zaphod stores branch-pair and agent-claim data inside the current repository under .git/zaphod.

This keeps Zaphod state local to the repository and avoids changing global Git configuration. The metadata format is TOML so it stays readable and easy to debug.

Metadata files include a top-level schema_version. Files without this field are treated as schema version 1 for compatibility with early releases. Files with a newer unsupported schema version are refused with a clear error instead of being partially interpreted.

Commands that write pair or claim metadata create a short-lived .git/zaphod/metadata.lock directory while they load, modify, and save the metadata. If another Zaphod process already holds that lock, the command refuses instead of risking an overwritten update.

Metadata saves use atomic file replacement so interrupted writes do not leave .git/zaphod/pairs.toml or .git/zaphod/claims.toml partially written.

Development

Zaphod is a Rust CLI using:

  • clap for command parsing.
  • serde, serde_json, and TOML for metadata and machine-readable output.
  • Carefully wrapped git commands for repository operations.

The implementation is organized around three boundaries:

  • CLI parsing and command output.
  • Core branch-pair and safety logic.
  • Git repository adapter.

Run the local quality gate before opening a pull request:

cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets --all-features

Build a release binary locally:

cargo build --release

The binary will be written to target/release/zaphod on Unix-like systems and target/release/zaphod.exe on Windows.

The release workflow builds binary artifacts for Linux, macOS, and Windows and publishes them as GitHub Release assets when a v* tag is pushed. Each binary is published with a matching .sha256 checksum file:

git tag -a v0.1.0 -m "v0.1.0"
git push origin v0.1.0

Verify downloaded release assets before running them:

sha256sum -c zaphod-linux.sha256
shasum -a 256 -c zaphod-macos.sha256

On Windows PowerShell:

Get-FileHash .\zaphod-windows.exe -Algorithm SHA256
Get-Content .\zaphod-windows.exe.sha256

Pull requests that touch release-critical files validate the release build without publishing a GitHub Release.

Before tagging a release, run the full quality gate and manually test the release binary in a temporary Git repository.

Contributing

Contributions are welcome. The most useful contributions are:

  • Clear bug reports.
  • Small pull requests with tests.
  • Documentation improvements.
  • Safety-focused edge cases around Git state.

Before opening a pull request, please make sure the project formats, lints, and tests cleanly.

License

Zaphod is licensed under the MIT License. See LICENSE.

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages