Command reference

Every command is non-interactive, idempotent, has --help with examples, and supports --json. Fetching uses your own git — only requirement is that it's on your PATH.

regraft add — vendor and start tracking

Copies the files in, pins the source to a commit, and hashes every file it wrote. Supports --dry-run.

# A directory from a repo
regraft add owner/repo/tree/main/src/components lib/components

# A single file
regraft add owner/repo/blob/main/src/utils.ts

# Code you copied in by hand a while ago — track it without overwriting your edits
regraft add owner/repo/tree/main/src/utils lib/utils --adopt

Per file: doesn't exist → write; exists untracked with different content → skip (needs --force or --adopt); exists and identical → track silently. --force overwrites, --adopt keeps — they're mutually exclusive.

Accepted source forms

Anything your git can clone — GitHub, GitLab, self-hosted, private, even file://.

owner/repo # whole repo, default branch
owner/repo#ref # whole repo at a branch/tag
owner/repo/tree/<ref>/<path> # directory
owner/repo/blob/<ref>/<file> # single file
owner/repo/pull/<number> # whole repo at the PR head (a live ref)
https://github.com/owner/repo[...] # same four forms as web URLs
<git-url>#<ref> # any git remote at a ref
<git-url>#<ref>:<subpath> # any git remote, ref + subpath
<git-url>#:<subpath> # default branch + subpath

<git-url> may be https://, ssh://, file://, or scp-style (git@host:repo.git). Branch names containing / need the #<ref>:<subpath> syntax.

regraft note — record intent after customizing

One plain-English sentence: what changed and why. Snapshots current disk hashes, appends the entry, and regenerates PATCH.md.

# Default file set: every modified tracked file not already covered by a snapshot
regraft note "Swapped the default tokens for our brand palette"

# Scope explicitly
regraft note "Removed the telemetry hooks" --files lib/components/analytics.ts

Intent integrity is enforced: regraft status classifies every locally modified file as modified+intent or modified-unrecorded (which fails the exit code). That's what keeps PATCH.md trustworthy over time.

regraft diff — see both directions

Never writes to the project. Handy right before writing a note or pulling.

# What you changed since vendoring (against the pinned baseline)
regraft diff

# What upstream changed since your pin — what a pull would bring in
regraft diff --upstream

Exits 1 when there are differences, 0 when there are none (like git diff). Binary files are flagged and not diffed.

regraft status — anything need attention?

Checks each upstream ref for new commits and classifies every tracked file.

regraft status            # exits 1 if anything is stale, unrecorded, missing, or unresolved
regraft status --offline  # skip the upstream checks entirely (no network)
clean Matches what regraft last wrote
modified+intent Locally edited, covered by an intent snapshot
modified-unrecorded Locally edited without a note — fails the exit code
missing Tracked file no longer on disk
conflict-unresolved A previous pull left markers waiting for resolve

Run it in CI to catch drift before it compounds.

regraft pull — the core

Per file: no local edits → fast-forward to upstream; local edits and upstream unchanged → leave alone; both changed → three-way merge.

regraft pull            # merge upstream updates
regraft pull --dry-run  # report the plan, write nothing
regraft pull --force    # take upstream wholesale for conflicting files

On conflict, diff3 markers (<<<<<<< local / ||||||| base / >>>>>>> upstream) are written in place and a reconciliation brief lands in .regraft/briefs/ with the conflicted files, the full text of every intersecting intent note, the scoped upstream commit log, and instructions for the resolving agent. Conflicted files are skipped on subsequent pulls until resolved, so markers never stack.

regraft resolve — close the loop

Run after you (or your agent) fix the conflicts. Verifies no markers remain and records the outcome.

# Resolve and record the intent in one step
regraft resolve --note "Re-applied our palette on the new token system"

# Or resolve specific files first, note later
regraft resolve lib/components/theme.ts

Housekeeping

# Untrack a source — substring match on URL or dest; --hard also deletes the files
regraft remove lib/components
regraft remove owner/repo --hard

# Update regraft itself (latest, or a specific tag)
regraft update
regraft update v0.1.0

# Tab completion
echo 'eval "$(regraft completion bash)"' >> ~/.bashrc
regraft completion zsh > ~/.zfunc/_regraft
regraft completion fish > ~/.config/fish/completions/regraft.fish

Removed sources keep their intent entries as history, marked orphaned in PATCH.md.

Files regraft manages

regraft.json
The manifest — commit it

Single source of truth: upstream URL, pinned SHA, per-file hashes, and intent entries. Validated with zod on read.

PATCH.md
The intent journal — commit it

Generated, human- and agent-readable view of every note. Regenerated from the manifest.

.regraft/
Working dir — ignores itself

Git clone cache and reconciliation briefs. regraft writes .regraft/.gitignore containing * so you never have to think about it.

Exit codes

0 Clean / success / already done
1 Drift, stale, conflict, or error — status: anything stale/unrecorded/missing/unresolved; pull: any conflict or warning; resolve: markers remain or resolution intent missing; add: any file skipped; diff: any difference found

Every command except update accepts --json and prints exactly one JSON object to stdout, with stable shapes agents can pattern-match on.