·

Run AI Coding Agents Non-Interactively in Parallel Worktrees

Prepared and checked using AI. Sources are linked in the article.

git-worktrees multi-agent workflow ai-coding

To run AI coding agents non-interactively in parallel worktrees, create one branch and worktree per task, start each CLI from its assigned directory, and wait for every process before reviewing the changes. Git worktrees let one repository have multiple working trees with separate checked-out branches. That gives each agent its own files to edit while keeping the results available for ordinary Git review.

Prepare one worktree per task

Start from the repository checkout you intend to use as the common base. Install and authenticate each CLI you select before launching the batch. Gemini headless mode uses cached credentials or requires authentication configured through environment variables; complete that setup before relying on an unattended run.

Decide the tasks before launching agents: each prompt should name the files or behavior it owns, the expected result, and any files it must leave alone. Separate tasks with little overlap make the resulting branches easier to review and merge. For more on choosing those boundaries, see how to split work for parallel agents.

For example, the script below uses four sibling worktrees for changes to authentication, caching, API handling, and the command line interface. Replace the task descriptions and paths with ones that fit your repository. The -b option creates a new branch at HEAD; distinct branch names also avoid Git’s safeguard against checking out the same branch in multiple worktrees.

Run the script from the repository root with a clean starting checkout. Its Bash path checks reject existing destinations, including empty directories and dangling symbolic links, and set -e exits when a setup command fails. Git also rejects an existing branch when creating it with -b. Because setup and launch are in one script, a failed setup cannot be followed by an agent starting in an old worktree. For another batch, choose fresh branch names and paths. Keep the logs outside all four worktrees so a transcript does not become part of an agent’s proposed change.

A worktree separates checked-out files and each tree’s HEAD and index, but the repository still shares Git data and, by default, repository configuration. It does not isolate credentials, processes, network access, databases, or services. If tasks run tests against a shared database, give them separate database names or instances; test database isolation needs its own plan.

Choose the non-interactive invocation

The four CLIs have different output and permission controls. Claude Code’s -p prints a response without an interactive session, and --output-format json returns structured output. Codex uses codex exec for scripted runs; --json emits newline-delimited events, while --sandbox workspace-write selects a workspace-writing sandbox. Gemini CLI enters headless mode with -p, and --output-format json returns one JSON object. Copilot CLI runs a single prompt with -p; -s suppresses decoration when capturing its text response.

For unattended editing, decide permissions separately from output format. Claude’s acceptEdits mode automatically accepts file edits and common filesystem commands in its working directory. Gemini’s auto_edit mode approves edit tools. In this headless workflow, calls that require user approval are denied unless separately authorized; the run cannot pause to display an approval prompt. Copilot’s --allow-tool=write grants its write tool for the session. A prompt asking an agent to avoid shell commands is useful task guidance, but it is not a permission boundary. If an agent must execute tests, configure the required command permissions for that CLI and environment before running it unattended.

Launch all four processes

This Bash script checks all destination paths, creates the worktrees, then gives each agent a distinct task. It redirects each agent’s output to a separate file and records its process ID. The prompts prohibit commits so the review commands below can inspect uncommitted changes directly. They also ask the agents to leave tests to the operator, because the example does not grant every CLI broad shell access.

#!/usr/bin/env bash
set -e

repo=$(pwd)
run_root="$(dirname "$repo")/agent-runs"

for path in "$run_root/auth" "$run_root/cache" "$run_root/api" "$run_root/cli"; do
  if [[ -e "$path" || -L "$path" ]]; then
    printf 'Worktree path already exists: %s\n' "$path" >&2
    exit 1
  fi
done

mkdir -p "$run_root/logs"
git -C "$repo" worktree add -b agent/auth "$run_root/auth" HEAD
git -C "$repo" worktree add -b agent/cache "$run_root/cache" HEAD
git -C "$repo" worktree add -b agent/api "$run_root/api" HEAD
git -C "$repo" worktree add -b agent/cli "$run_root/cli" HEAD

(
  cd "$run_root/auth"
  claude -p --permission-mode acceptEdits --output-format json \
    'Update authentication error handling. Edit authentication files only. Do not run shell commands or commit. Summarize changed files.' \
    >"$run_root/logs/auth.json" 2>"$run_root/logs/auth.err"
) & auth_pid=$!

(
  cd "$run_root/cache"
  codex exec --sandbox workspace-write --json \
    'Improve cache invalidation. Edit caching files only. Do not run tests or commit. Summarize changed files.' \
    >"$run_root/logs/cache.jsonl" 2>"$run_root/logs/cache.err"
) & cache_pid=$!

(
  cd "$run_root/api"
  gemini -p 'Handle missing API parameters. Edit API files only. Do not run shell commands or commit. Summarize changed files.' \
    --approval-mode auto_edit --output-format json \
    >"$run_root/logs/api.json" 2>"$run_root/logs/api.err"
) & api_pid=$!

(
  cd "$run_root/cli"
  copilot -p 'Improve CLI argument errors. Edit CLI files only. Do not run shell commands or commit. Summarize changed files.' \
    -s --allow-tool=write --no-ask-user \
    >"$run_root/logs/cli.txt" 2>"$run_root/logs/cli.err"
) & cli_pid=$!

failed=0
for entry in "auth:$auth_pid" "cache:$cache_pid" "api:$api_pid" "cli:$cli_pid"; do
  name=${entry%%:*}
  pid=${entry#*:}
  if wait "$pid"; then
    printf '%s: process completed\n' "$name"
  else
    printf '%s: process failed; inspect its log and error file\n' "$name" >&2
    failed=1
  fi
done
exit "$failed"

Save the script outside the worktrees before running it. If any path check or worktree creation fails, the script exits before launching an agent; worktrees already created by that attempt remain available for inspection. & starts each subshell in the background; the four wait calls preserve individual success or failure instead of reporting only the final process. A completed process is only a signal to begin review. It does not establish that the requested edit is correct, complete, or safe to merge.

Use the captured output according to its format: Claude and Gemini produce JSON files, Codex produces JSONL events, and this Copilot invocation produces plain text. Keep stderr separately so a failure does not get mixed into a JSON parser’s input. If you change Copilot to --output-format=json, its output becomes JSONL rather than one JSON document.

Review, test, and integrate

The saved script runs in a separate shell, so its repo and run_root variables are unavailable to your review shell. Initialize them again before reviewing or merging: an empty git -C path leaves the current directory unchanged. Replace the example repository path below with the absolute path of the checkout used to launch the script. The guard uses git rev-parse --show-toplevel and --git-common-dir to check the checkout paths and their shared repository. Keep this Bash shell open for the later commands, or rerun this setup if you switch shells.

Inspect every worktree, including failed runs. Git status reports tracked changes and untracked files. Plain git diff omits staged changes; git diff HEAD compares the working tree with the last commit, including staged changes, and --check reports whitespace errors. For example:

repo=/absolute/path/to/your/repository
run_root="$(dirname "$repo")/agent-runs"

review_paths_ok() {
  [[ -n "$repo" && -n "$run_root" ]] &&
  [[ -d "$repo" && -d "$run_root/auth" ]] &&
  [[ "$(git -C "$repo" rev-parse --show-toplevel 2>/dev/null)" == "$repo" ]] &&
  [[ "$(git -C "$run_root/auth" rev-parse --show-toplevel 2>/dev/null)" == "$run_root/auth" ]] &&
  [[ "$(git -C "$repo" rev-parse --path-format=absolute --git-common-dir 2>/dev/null)" == "$(git -C "$run_root/auth" rev-parse --path-format=absolute --git-common-dir 2>/dev/null)" ]]
}

if review_paths_ok; then
  git -C "$run_root/auth" status --short
  git -C "$run_root/auth" diff --check HEAD
  git -C "$run_root/auth" diff HEAD
else
  printf 'Repository or auth worktree path is invalid\n' >&2
fi

Repeat for the other directories. Inspect untracked files listed by status separately; they are not included in git diff HEAD. Read the actual patch and run the repository’s relevant tests in each candidate worktree. Keep test output and generated files local to that candidate, especially when multiple test processes would otherwise use the same port, database, or output path.

After accepting a candidate, commit it on its branch and integrate branches one at a time. Git merge combines the named branch into the current branch, so check that the destination checkout is on the intended branch and clean before merging. Run the relevant tests again after each integration: changes that worked separately may interact once combined. See merging parallel agent branches for the conflict-resolution workflow. Run this in the same validated Bash shell:

if review_paths_ok; then
  git -C "$repo" merge agent/auth
else
  printf 'Repository or auth worktree path is invalid\n' >&2
fi

Remove a linked worktree only after preserving or discarding its changes deliberately. git worktree remove refuses an unclean worktree unless forced; that safeguard is useful when a run failed after editing files. The worktree list remains available for checking what still exists:

if review_paths_ok; then
  git -C "$repo" worktree list
  git -C "$repo" worktree remove "$run_root/auth"
else
  printf 'Repository or auth worktree path is invalid\n' >&2
fi

Where Parallel Code fits

Parallel Code is our free, open-source MIT desktop app for macOS and Linux. It runs Claude Code, Codex CLI, Gemini CLI, Copilot CLI, and Antigravity CLI in parallel, each in its own Git worktree, and provides a diff-first review surface. It launches the agent CLIs you already use locally, so no extra subscription is required, your API keys stay yours, and nothing is proxied or collected.

Frequently asked questions

Do I need a separate clone for each agent?

No. Git worktrees let you check out separate branches from one repository, which is enough to separate their working files for this workflow. Use stronger isolation when tasks must also be separated by credentials, processes, network access, or external services.

Can I give every agent the same task?

Yes, if you want competing implementations; give each run its own branch and worktree, then review the alternatives before keeping one. If the goal is to combine all changes, assign distinct scopes and expect to resolve any overlap during integration.

What happens if one CLI exits with an error?

The script reports that process as failed and keeps its log, stderr, branch, and worktree for inspection. Review its status and diff before retrying; a failed run can still have edited files.

Can the agents run tests without prompting?

Only when the CLI’s permissions and the test environment allow the required commands. This example leaves tests to the operator; for unattended tests, grant narrowly scoped commands and separate any shared database, port, or generated output first.