feat: implement archeflow-review.sh for Guardian-only diff review
Standalone bash script that extracts git diffs for af-review without PDCA orchestration. Supports --branch, --commit, and uncommitted modes. Reports stats (files/lines changed) to stderr, diff to stdout.
This commit is contained in:
197
lib/archeflow-review.sh
Executable file
197
lib/archeflow-review.sh
Executable file
@@ -0,0 +1,197 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# archeflow-review.sh — Get a git diff for Guardian review, with stats.
|
||||||
|
#
|
||||||
|
# Standalone diff helper for af-review. No PDCA orchestration — just extracts
|
||||||
|
# the right diff and reports stats so the Claude Code agent can feed it to
|
||||||
|
# Guardian (or other reviewers).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# archeflow-review.sh # Uncommitted changes (staged + unstaged)
|
||||||
|
# archeflow-review.sh --branch feat/batch-api # Branch diff vs main
|
||||||
|
# archeflow-review.sh --commit HEAD~3..HEAD # Commit range
|
||||||
|
# archeflow-review.sh --base develop # Override base branch (default: main)
|
||||||
|
# archeflow-review.sh --stat-only # Only print stats, no diff output
|
||||||
|
#
|
||||||
|
# Output:
|
||||||
|
# Prints the diff to stdout. Stats go to stderr so they don't pollute the diff.
|
||||||
|
# Exit code 0 if diff is non-empty, 1 if empty (nothing to review).
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Globals
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
BASE_BRANCH="main"
|
||||||
|
MODE="uncommitted" # uncommitted | branch | commit
|
||||||
|
TARGET=""
|
||||||
|
STAT_ONLY="false"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo "[af-review] ERROR: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
info() {
|
||||||
|
echo "[af-review] $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print diff stats (files changed, insertions, deletions) to stderr.
|
||||||
|
print_stats() {
|
||||||
|
local diff_text="$1"
|
||||||
|
|
||||||
|
local files_changed lines_added lines_removed total_lines
|
||||||
|
files_changed=$(echo "$diff_text" | grep -c '^diff --git' || true)
|
||||||
|
lines_added=$(echo "$diff_text" | grep -c '^+[^+]' || true)
|
||||||
|
lines_removed=$(echo "$diff_text" | grep -c '^-[^-]' || true)
|
||||||
|
total_lines=$(echo "$diff_text" | wc -l | tr -d ' ')
|
||||||
|
|
||||||
|
info "--- Review Stats ---"
|
||||||
|
info "Files changed: ${files_changed}"
|
||||||
|
info "Lines added: +${lines_added}"
|
||||||
|
info "Lines removed: -${lines_removed}"
|
||||||
|
info "Diff size: ${total_lines} lines"
|
||||||
|
|
||||||
|
if [[ "$total_lines" -gt 500 ]]; then
|
||||||
|
info "Warning: large diff (>500 lines). Consider reviewing per-file."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detect the default base branch (main or master).
|
||||||
|
detect_base_branch() {
|
||||||
|
if git show-ref --verify --quiet "refs/heads/main" 2>/dev/null; then
|
||||||
|
echo "main"
|
||||||
|
elif git show-ref --verify --quiet "refs/heads/master" 2>/dev/null; then
|
||||||
|
echo "master"
|
||||||
|
else
|
||||||
|
echo "main"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Argument parsing
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
parse_args() {
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--branch)
|
||||||
|
MODE="branch"
|
||||||
|
TARGET="${2:?Missing branch name after --branch}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--commit)
|
||||||
|
MODE="commit"
|
||||||
|
TARGET="${2:?Missing commit range after --commit}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--base)
|
||||||
|
BASE_BRANCH="${2:?Missing base branch after --base}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--stat-only)
|
||||||
|
STAT_ONLY="true"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
echo "Usage: $0 [--branch <name>] [--commit <range>] [--base <branch>] [--stat-only]"
|
||||||
|
echo ""
|
||||||
|
echo " (no args) Review uncommitted changes (staged + unstaged)"
|
||||||
|
echo " --branch <name> Review branch diff against base (default: main)"
|
||||||
|
echo " --commit <range> Review a commit range (e.g. HEAD~3..HEAD)"
|
||||||
|
echo " --base <branch> Override base branch (default: auto-detect main/master)"
|
||||||
|
echo " --stat-only Print stats only, no diff output"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
die "Unknown argument: $1. Use --help for usage."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Diff extraction
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
get_diff() {
|
||||||
|
local diff_text=""
|
||||||
|
|
||||||
|
case "$MODE" in
|
||||||
|
uncommitted)
|
||||||
|
# Combine staged and unstaged changes against HEAD
|
||||||
|
diff_text=$(git diff HEAD 2>/dev/null || true)
|
||||||
|
if [[ -z "$diff_text" ]]; then
|
||||||
|
# Maybe everything is staged, try just staged
|
||||||
|
diff_text=$(git diff --cached 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
branch)
|
||||||
|
# Verify target branch exists
|
||||||
|
if ! git show-ref --verify --quiet "refs/heads/${TARGET}" 2>/dev/null; then
|
||||||
|
# Maybe it's a remote branch
|
||||||
|
if ! git rev-parse --verify "${TARGET}" &>/dev/null; then
|
||||||
|
die "Branch '${TARGET}' not found."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
diff_text=$(git diff "${BASE_BRANCH}...${TARGET}" 2>/dev/null || true)
|
||||||
|
;;
|
||||||
|
commit)
|
||||||
|
# Validate commit range resolves
|
||||||
|
if ! git rev-parse "${TARGET}" &>/dev/null 2>&1; then
|
||||||
|
die "Invalid commit range: '${TARGET}'"
|
||||||
|
fi
|
||||||
|
diff_text=$(git diff "${TARGET}" 2>/dev/null || true)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "$diff_text"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Main
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
main() {
|
||||||
|
# Verify we're in a git repo
|
||||||
|
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
|
||||||
|
die "Not inside a git repository."
|
||||||
|
fi
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
# Auto-detect base branch if not overridden
|
||||||
|
if [[ "$BASE_BRANCH" == "main" ]]; then
|
||||||
|
BASE_BRANCH=$(detect_base_branch)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Describe what we're reviewing
|
||||||
|
case "$MODE" in
|
||||||
|
uncommitted) info "Reviewing: uncommitted changes vs HEAD" ;;
|
||||||
|
branch) info "Reviewing: branch '${TARGET}' vs '${BASE_BRANCH}'" ;;
|
||||||
|
commit) info "Reviewing: commit range '${TARGET}'" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
local diff_text
|
||||||
|
diff_text=$(get_diff)
|
||||||
|
|
||||||
|
# Validate non-empty
|
||||||
|
if [[ -z "$diff_text" ]]; then
|
||||||
|
info "No changes found. Nothing to review."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Print stats to stderr
|
||||||
|
print_stats "$diff_text"
|
||||||
|
|
||||||
|
# Output the diff to stdout (unless stat-only)
|
||||||
|
if [[ "$STAT_ONLY" != "true" ]]; then
|
||||||
|
echo "$diff_text"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
@@ -37,23 +37,28 @@ af-review --evidence # Enable evidence-gating (stricter)
|
|||||||
|
|
||||||
### Step 1: Get the Diff
|
### Step 1: Get the Diff
|
||||||
|
|
||||||
```bash
|
Use `lib/archeflow-review.sh` to extract the diff and stats:
|
||||||
# Uncommitted changes
|
|
||||||
DIFF=$(git diff HEAD)
|
|
||||||
|
|
||||||
# Branch diff
|
```bash
|
||||||
DIFF=$(git diff main...HEAD)
|
# Uncommitted changes (default)
|
||||||
|
DIFF=$(bash lib/archeflow-review.sh)
|
||||||
|
|
||||||
|
# Branch diff against main
|
||||||
|
DIFF=$(bash lib/archeflow-review.sh --branch feat/batch-api)
|
||||||
|
|
||||||
# Commit range
|
# Commit range
|
||||||
DIFF=$(git diff HEAD~3..HEAD)
|
DIFF=$(bash lib/archeflow-review.sh --commit HEAD~3..HEAD)
|
||||||
|
|
||||||
# If diff is too large (>500 lines), split by file
|
# Override base branch
|
||||||
if [[ $(echo "$DIFF" | wc -l) -gt 500 ]]; then
|
DIFF=$(bash lib/archeflow-review.sh --branch feat/x --base develop)
|
||||||
# Review per-file to keep context focused
|
|
||||||
FILES=$(git diff --name-only HEAD)
|
# Stats only (no diff output)
|
||||||
fi
|
bash lib/archeflow-review.sh --stat-only
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The script prints the diff to stdout and stats to stderr. It exits 1 if the diff
|
||||||
|
is empty (nothing to review). For large diffs (>500 lines), it warns on stderr.
|
||||||
|
|
||||||
### Step 2: Spawn Reviewers
|
### Step 2: Spawn Reviewers
|
||||||
|
|
||||||
Default: Guardian only (fastest, highest ROI).
|
Default: Guardian only (fastest, highest ROI).
|
||||||
|
|||||||
Reference in New Issue
Block a user