Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 90 additions & 13 deletions misc/auto-fixes/auto-fix.sh
Original file line number Diff line number Diff line change
@@ -1,26 +1,78 @@
#!/usr/bin/env bash
# Scan a contributor's branch for code-quality issues and open a PR of fixes back into that branch.
#
# Usage: misc/auto-fixes/auto-fix.sh <branch>
# Usage: misc/auto-fixes/auto-fix.sh [--agent claude|opencode] <branch>
#
set -euo pipefail

# Print the script's own messages in green so they stand out from git/uv/claude output.
# Print the script's own messages in green so they stand out from git/uv/agent output.
GREEN=$'\033[0;32m'
RESET=$'\033[0m'
log() { echo "${GREEN}$*${RESET}"; } # stdout (progress)
err() { echo "${GREEN}$*${RESET}" >&2; } # stderr (errors)

usage() {
err "usage: $0 <branch>"
err "usage: $0 [--agent claude|opencode] <branch>"
err " AUTO_FIX_AGENT=opencode $0 <branch>"
exit 2
}

[[ $# -eq 1 ]] || usage
BRANCH="$1"
AGENT="${AUTO_FIX_AGENT:-claude}"
BRANCH=""

while [[ $# -gt 0 ]]; do
case "$1" in
--agent|-a)
[[ $# -ge 2 ]] || usage
AGENT="$2"
shift 2
;;
--agent=*)
AGENT="${1#*=}"
shift
;;
-h|--help)
usage
;;
--)
shift
break
;;
-*)
err "unknown option: $1"
usage
;;
*)
[[ -z "$BRANCH" ]] || usage
BRANCH="$1"
shift
;;
esac
done

if [[ $# -gt 0 ]]; then
[[ -z "$BRANCH" && $# -eq 1 ]] || usage
BRANCH="$1"
fi

[[ -n "$BRANCH" ]] || usage

case "$AGENT" in
claude|opencode)
;;
*)
err "unsupported agent: $AGENT (expected claude or opencode)"
exit 2
;;
esac

command -v "$AGENT" >/dev/null 2>&1 || { err "missing $AGENT on PATH"; exit 1; }

# shellcheck disable=SC2016
PLACEHOLDER='$$BRANCH$$'
# shellcheck disable=SC2016
AUTOFIX_PLACEHOLDER='$$AUTOFIX_BRANCH$$'
# shellcheck disable=SC2016
RULES_PLACEHOLDER='$$RULES$$'

REPO_ROOT="$(git rev-parse --show-toplevel)"
Expand All @@ -46,6 +98,32 @@ SUFFIX="$(openssl rand -hex 4)"
WORKTREE="$(cd "$REPO_ROOT/.." && pwd)/dimos-worktree-${SUFFIX}"
AUTOFIX_BRANCH="" # set once we know there are issues; cleanup uses it

run_scan_agent() {
local prompt="$1"
case "$AGENT" in
claude)
( cd "$WORKTREE" && claude -p "$prompt" --dangerously-skip-permissions )
;;
opencode)
opencode run --auto --dir "$WORKTREE" "$prompt"
Comment thread
TomCC7 marked this conversation as resolved.
;;
esac
}

run_fix_agent() {
local prompt="$1"
case "$AGENT" in
claude)
( cd "$WORKTREE" && claude -p "$prompt" \
--dangerously-skip-permissions \
--settings '{"includeCoAuthoredBy": false}' )
;;
opencode)
opencode run --auto --dir "$WORKTREE" "$prompt"
;;
esac
}

# Leave no state in the user's repo: remove the worktree, then (it shares the common git dir) the
# local autofix branch and any filter-branch backup ref. On success the branch lives on origin; on
# failure this is a throwaway attempt, so deleting the local ref is intended.
Expand All @@ -70,15 +148,15 @@ log ">> creating worktree $WORKTREE (detached on origin/$BRANCH)"
git -C "$REPO_ROOT" worktree add --detach "$WORKTREE" "origin/$BRANCH"

log ">> installing dependencies"
( cd "$WORKTREE" && CYCLONEDDS_HOME=/opt/cyclonedds uv sync --all-extras --all-groups )
( cd "$WORKTREE" && uv sync --extra all --all-groups )
Comment thread
TomCC7 marked this conversation as resolved.

log ">> running scan agent"
log ">> running scan agent ($AGENT)"
# In the detached worktree the bare local <branch> ref may not exist, so the template's
# `git diff main...$$BRANCH$$` is pointed at origin/<branch>.
# `git diff origin/main...$$BRANCH$$` is pointed at origin/<branch>.
scan_prompt="$(cat "$SCAN_TEMPLATE")"
scan_prompt="${scan_prompt//"$RULES_PLACEHOLDER"/$(cat "$RULES_FILE")}"
scan_prompt="${scan_prompt//"$PLACEHOLDER"/origin/$BRANCH}"
if ! ( cd "$WORKTREE" && claude -p "$scan_prompt" --dangerously-skip-permissions ); then
if ! run_scan_agent "$scan_prompt"; then
err "scan agent failed"
exit 1
fi
Expand Down Expand Up @@ -108,12 +186,11 @@ log ">> autofix branch: $AUTOFIX_BRANCH"
BASE_SHA="$(git -C "$WORKTREE" rev-parse HEAD)"
git -C "$WORKTREE" checkout -b "$AUTOFIX_BRANCH"

log ">> running fix agent"
log ">> running fix agent ($AGENT)"
fix_prompt="$(cat "$FIX_TEMPLATE")"
fix_prompt="${fix_prompt//"$PLACEHOLDER"/$BRANCH}"
if ! ( cd "$WORKTREE" && claude -p "$fix_prompt" \
--dangerously-skip-permissions \
--settings '{"includeCoAuthoredBy": false}' ); then
fix_prompt="${fix_prompt//"$AUTOFIX_PLACEHOLDER"/$AUTOFIX_BRANCH}"
if ! run_fix_agent "$fix_prompt"; then
err "fix agent failed"
exit 1
fi
Expand Down
2 changes: 1 addition & 1 deletion misc/auto-fixes/scan_template.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
I want you to automatically fix the following issues. First, scan the current branch and identify any code which breaks the following. Write down the issues to fix into a file called `issues.ignore.md`.

The branch is $$BRANCH$$. You can perform a diff with `git diff main...$$BRANCH$$ -- . ':(exclude)*.lock' ':(exclude)uv.lock' ':(exclude)package-lock.json' ':(exclude)yarn.lock' ':(exclude)pnpm-lock.yaml' ':(exclude)Cargo.lock' ':(exclude)*.pdf' ':(exclude)*.png' ':(exclude)*.jpg'
The branch is $$BRANCH$$. You can perform a diff with `git diff origin/main...$$BRANCH$$ -- . ':(exclude)*.lock' ':(exclude)uv.lock' ':(exclude)package-lock.json' ':(exclude)yarn.lock' ':(exclude)pnpm-lock.yaml' ':(exclude)Cargo.lock' ':(exclude)*.pdf' ':(exclude)*.png' ':(exclude)*.jpg'
`
The `issues.ignore.md` file will be used as the input for a second agent which will fix the issues which have been identified. It's okay if there are no issues to fix. Just leave the file empty or don't create it at all.

Expand Down
Loading