local-arc
v1.4.0
Published
Local CLI for capturing and viewing Git patch files in a browser.
Readme
Local Arc
Small, standalone and local-only tooling for create a preview diff with a git diff cmd in phabricator style (include unstaged or untracked files).
AI friendly: user & ai can read & comment diffs, achieve a local review loop between users and AI coding agents.


Highlights
- Create local arc-like git diffs with a git-like diff cmd, into a saved & browser-reviewable tickets.
- Expand changed-block context on demand when reference snapshots are available.
- Support inline comments & reply comment.
- Show comment counts in the file tree while reviewing a diff.
- Update an existing ticket with
--updatewhile preserving comments and earlier revisions. - View patches with side-by-side or unified diff modes, file navigation, etc.
- Add editable titles, Markdown summaries, and comment activity.
Install or Link the CLI
Install the published CLI:
npm install -g local-arcFor local development, link it from this directory:
npm linkThat exposes the local-arc command.
Tip: arc alias
For a near-arc local workflow, add an alias: alias arc='local-arc'
Then a diff can be opened with: arc diff HEAD~1
Diff
local-arc diff HEAD~1This runs git diff HEAD~1, captures the output into a temporary patch file
under /tmp/local-arc/<ticket_id>/diff.patch, writes reference snapshots when
the changed files can be read, serves the browser view, and opens the URL in
your default browser. The <ticket_id> is T plus six digits, so the browser
URL also identifies the saved patch path, for example
http://127.0.0.1:7788/T153871/ maps to
/tmp/local-arc/T153871/diff.patch.
The workflow is similar to arc diff in the sense that both start from a local
working-copy diff and turn it into a reviewable change. The difference is that
arc diff submits that change to Phabricator, while local-arc diff keeps
everything local: it runs the Git diff command you provide, opens a local
browser viewer, and does not create or update a remote review.
diff prepends git diff unless the first argument is git:
local-arc diff
local-arc diff --cached
local-arc diff HEAD~1 -- README.md
local-arc diff 'git -C /path/to/repo diff HEAD~1'For common commands like git diff HEAD~1, the viewer infers HEAD~1 as the
old-side source for on-demand context expansion.
When a diff compares the current working tree to another revision, local-arc
asks whether untracked files should be included as full new-file hunks. Staged
and commit-to-commit diffs skip that prompt by default. Use
--include-untracked or --no-include-untracked to force either behavior.
Update Diff
Use --update to update a ticket.
local-arc diff HEAD~1 --update T153871The ticket ID, title, summary, comments, and activity file are preserved. Inline
comment ranges are adjusted best-effort when reference snapshots are available:
Local Arc first looks for the same selected source lines in the updated file,
then falls back to unique-line anchors. Comments on files that disappear remain
in the comment store and are marked file-gone.
Use --no-open when running in scripts or tests:
local-arc diff HEAD~1 --no-openAdd Metadata to a Diff
local-arc diff HEAD~1 \
--title "Checkout diff" \
--description "Review notes in **Markdown**."The --title and --description fields are optional. If either is present,
the viewer shows a metadata section above the file diffs. When --title is
omitted, Local Arc uses the project name. When --description is omitted, the
description starts empty and can be edited in the viewer.
Inline comment changes are also recorded in diff.patch.activities.json.
Comments and replies include a role field, which defaults to user; agents can
set values such as claude or codex when they write comments through the local
comment API.
Saved Ticket Files
Default file dir is /tmp/local-arc. To use a different diff temp directory:
local-arc config set-dir /path/to/local-arc-files
local-arc config dirconfig set-dir writes to ~/.local-arc/config.json. config dir prints the
directory used by diff. Generated diff tickets are saved under
<dir>/<ticket_id>/, with the patch at <dir>/<ticket_id>/diff.patch.
A saved diff ticket directory contains the current patch, ticket metadata, and patch-specific sidecars:
<dir>/
└── T153871/
├── diff.patch # Current patch shown by the ticket URL.
├── diff.patch.refs.json # Old/new file snapshots for context expansion and comment remapping.
├── diff.patch.comments.json # Inline comments and replies.
├── diff.patch.activities.json # Editable title, summary, and comment activity.
├── metadata.json # Ticket ID, project, repo, command, current revision, and revision history.
└── revisions/ # Read-only archived revisions created by --update.
└── 1/
├── diff.patch
├── diff.patch.refs.json
├── diff.patch.comments.json
└── diff.patch.activities.jsonWhen a ticket is updated, the previous current patch and its sidecars are copied
into revisions/<number>/, then the top-level diff.patch and sidecars are
replaced with the new current revision. Historical revisions can be opened from
the viewer with ?revision=<number> and are read-only.
Save a Patch
local-arc diff HEAD~1 --out /tmp/head-1.patchThe Git command stdout is written directly to the patch file.
Git statuses 0 and 1 are treated as successful captures because some diff
forms return 1 when differences are present.
When the changed files can be read, diff --out also writes
<patch-file>.refs.json with original and new file snapshots. The viewer uses
that sidecar for on-demand context expansion, so the patch can be opened later
without access to the original repo.
Useful inputs:
local-arc diff --out /tmp/worktree.patch
local-arc diff --cached --out /tmp/staged.patch
local-arc diff main...HEAD --out /tmp/base.patch
local-arc diff --repo /path/to/repo HEAD~1 --out /tmp/head.patchOpen a Patch
local-arc open /tmp/head-1.patchThen open:
http://127.0.0.1:7788/If 7788 is already in use, the viewer automatically tries the next available
port and prints the URL to open. Use --strict-port if you want it to fail
instead. Use --no-open to print the URL without opening it.
For saved diff tickets, open also accepts the ticket directory or its
diff.patch file and opens the ticket URL:
local-arc open /tmp/local-arc/T153871
local-arc open /tmp/local-arc/T153871/diff.patchBoth forms serve the viewer at:
http://127.0.0.1:7788/T153871/The browser view supports:
- side-by-side and unified display
- per-file navigation
- file filtering
- line numbers and add/delete coloring
- simple intraline highlights for paired replacements
- on-demand changed-block context expansion when reference snapshots are available
diff --out writes a .refs.json sidecar next to the patch when it can read the
changed files. open uses that sidecar to show +20 before and +20 after
buttons around changed blocks. If the sidecar is missing, those buttons are
hidden by default.
For a saved patch without a .refs.json sidecar, pass repo context when you
want the webpage to load more lines around changed blocks:
local-arc open /tmp/head-1.patch --repo . --base HEAD~1Then click +20 before or +20 after next to a changed block.
List Saved Diff Tickets
local-arc list
local-arc list T153871This prints saved T-prefixed numeric diff tickets from the configured diff
directory. Without a ticket ID, the command prints a terminal table with the
ticket ID, creation timestamp, Git root name when it can be inferred, title,
project directory, and ticket directory. With a ticket ID, it prints detailed
metadata for that saved diff, including the ticket directory, command,
description, comments, and revisions.
To browse saved diffs in the local web viewer, use:
local-arc browseThis opens a local browser page with the same saved tickets, including the project directory and ticket directory columns. Click a ticket ID to open that saved diff in the viewer.
Local Arc and AI Agents
Local Arc can be used as a local review ticket between a person and an AI coding agent. Create a saved ticket with a title and description, then share the ticket URL or ticket directory with the agent:
local-arc diff HEAD~1 \
--title "Fix checkout totals" \
--description "Review the tax and discount calculation changes."The browser opens a ticket URL such as:
http://127.0.0.1:7788/T153871/Use the viewer to add inline comments on changed lines. Then ask the agent to
inspect the saved ticket, address the comments in the repo, and reply to each
handled comment. The agent can read comments from
<dir>/<ticket_id>/diff.patch.comments.json and review ticket activity in
<dir>/<ticket_id>/diff.patch.activities.json.
When an agent writes through the local comment API, it should set role to its
agent name, such as codex or claude, so replies and activity entries show who
made them:
{
"parentId": "comment-id",
"text": "Fixed in the working tree and verified with the focused test.",
"role": "codex"
}After applying fixes, update the same ticket instead of creating a new one:
local-arc diff HEAD~1 --update T153871--update preserves the ticket, comments, replies, and activity while replacing
the current patch. The viewer keeps earlier revisions available from the
Revisions section, so the user can compare the original comments with the latest
patch.
Command Reference
local-arc diff [git-diff-args...] [--title <title>] [--description <markdown>] [--host 127.0.0.1] [--port 7788] [--no-open]
local-arc diff [git-diff-args...] --out <patch-file> [--repo <path>] [--title <title>] [--description <markdown>]
local-arc diff [git-diff-args...] --update T123456 [--title <title>] [--description <markdown>] [--host 127.0.0.1] [--port 7788] [--no-open]
local-arc open <patch-file-or-ticket-dir> [--host 127.0.0.1] [--port 7788] [--no-open] [--repo <path>] [--base <rev>] [--head <rev>]
local-arc list [T123456]
local-arc browse [--host 127.0.0.1] [--port 7788] [--no-open]
local-arc config set-dir <dir>
local-arc config dir
local-arc render --patch <patch-file> --out <html-file>
local-arc ai-docRun local-arc ai-doc to print the AI-agent workflow, including create/update
guidance, the recommended Why/What/Test description sections, diff-scope flags,
and how agents should inspect and reply to user comments. Point your agent at
this command so it can self-discover the convention.
