Determine what to review. Check these in priority order to pick the right air invocation:
a. Uncommitted changes exist (staged or unstaged, check via git status --porcelain):
Run air --wait --json with no branch/commit flags. This reviews the working directory diff.
b. All changes committed, on a gg stack branch — the current branch matches the user/name pattern (no -- in the name part) and .git/gg/config.json exists:
Read the gg base branch from config (parse defaults.base, fall back to main). Run:
air --wait --json --commits <gg-base>..HEAD
This shows the full stack diff, matching what ai-review displays when opening a gg stack.
c. All changes committed, on a feature branch (not main/master):
Determine the base branch (main or master, whichever exists). Find the merge-base commit and use --commit:
air --wait --json --commit $(git merge-base <base-branch> HEAD)
d. All changes committed, on main/master itself:
Identify the first commit you made during this session. If you can determine the exact commit (e.g., you created it earlier in this conversation), use that hash directly. Otherwise, fall back to the first unpushed commit: run git log origin/main..HEAD --oneline --reverse (or origin/master) and take the first commit hash. Run:
air --wait --json --commit <first-relevant-commit-hash>
e. None of the above: Run air --wait --json with no flags (empty diff — the app handles it).
The air command opens the ai-review desktop app showing the relevant diff. The human will review the code, add comments, and submit. The command blocks until submission and prints structured JSON feedback to stdout.
Parse the feedback. The output is a JSON object with the following shape:
{
"format": "ai-review.feedback/v1",
"context": {
"mode": "unstaged|staged|commit|range|branch",
"commitRef": "...",
"selectedCommit": { "hash": "...", "short_hash": "...", "message": "...", ... } | null,
"selectedBranch": { "name": "...", "short_hash": "...", ... } | null
},
"comments": [
{
"id": "...",
"file": "src/App.tsx",
"startLine": 10,
"endLine": 12,
"side": "old|new",
"text": "...",
"createdAt": "..."
}
]
}
Parse the JSON and iterate over the comments array. Each comment has a file, startLine/endLine, side ("old" = deleted code, "new" = added/current code), and text.