@imdeadpool/guardex 5.0.2 → 5.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imdeadpool/guardex",
3
- "version": "5.0.2",
3
+ "version": "5.0.3",
4
4
  "description": "GuardeX: the Guardian T-Rex for your repo, with hardened multi-agent git guardrails.",
5
5
  "license": "MIT",
6
6
  "preferGlobal": true,
@@ -15,7 +15,7 @@
15
15
  "agent:codex": "bash ./scripts/codex-agent.sh",
16
16
  "agent:branch:start": "bash ./scripts/agent-branch-start.sh",
17
17
  "agent:branch:finish": "bash ./scripts/agent-branch-finish.sh",
18
- "agent:cleanup": "bash ./scripts/agent-worktree-prune.sh --base dev",
18
+ "agent:cleanup": "gx cleanup",
19
19
  "agent:hooks:install": "bash ./scripts/install-agent-git-hooks.sh",
20
20
  "agent:locks:claim": "python3 ./scripts/agent-file-locks.py claim",
21
21
  "agent:locks:allow-delete": "python3 ./scripts/agent-file-locks.py allow-delete",
@@ -12,11 +12,13 @@
12
12
  - For git isolation, each agent must start on a dedicated branch via `scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"`.
13
13
  - Treat the base branch (`main` or the user's current local base branch) as read-only while the agent branch is active.
14
14
  - Agent completion defaults to `scripts/codex-agent.sh`, which auto-finishes the branch (auto-commit changed files, push/create PR, attempt merge, and pull the local base branch after merge).
15
- - Auto-finish keeps the sandbox branch/worktree by default so conflict follow-ups and audits stay reproducible.
16
- - Use explicit cleanup when done: `gx cleanup --branch "<agent-branch>"` (or `gx cleanup` for all merged agent branches).
17
- - If codex-agent auto-finish cannot complete, immediately run `scripts/agent-branch-finish.sh --branch "<agent-branch>" --via-pr` and keep the branch open until checks/review pass.
15
+ - Auto-finish now waits for required checks/merge and then cleans merged sandbox branch/worktree by default.
16
+ - Use `--no-cleanup` only when you explicitly need to keep a merged sandbox for audit/debug follow-up.
17
+ - If codex-agent auto-finish cannot complete, immediately run `scripts/agent-branch-finish.sh --branch "<agent-branch>" --via-pr --wait-for-merge` and keep the branch open until checks/review pass.
18
18
  - If merge/rebase conflicts block auto-finish, run a conflict-resolution review pass in that sandbox branch, then rerun `agent-branch-finish.sh --via-pr` until merged.
19
+ - Completion is not valid until these are true: commit exists on the agent branch, branch is pushed to `origin`, and PR/merge status is produced by `agent-branch-finish.sh` or `codex-agent`.
19
20
  - Per-message loop is mandatory: for every new user message/task, start a fresh agent branch/worktree, claim ownership locks, implement and verify, finish via PR/merge cleanup, then repeat for the next message/task.
21
+ - If the change publishes or bumps a version, the same change must also update release notes/changelog entries.
20
22
 
21
23
  1. Explicit ownership before edits
22
24
 
@@ -10,6 +10,9 @@ DELETE_REMOTE_BRANCH_EXPLICIT=0
10
10
  MERGE_MODE="auto"
11
11
  GH_BIN="${MUSAFETY_GH_BIN:-gh}"
12
12
  CLEANUP_AFTER_MERGE_RAW="${MUSAFETY_FINISH_CLEANUP:-false}"
13
+ WAIT_FOR_MERGE_RAW="${MUSAFETY_FINISH_WAIT_FOR_MERGE:-false}"
14
+ WAIT_TIMEOUT_SECONDS_RAW="${MUSAFETY_FINISH_WAIT_TIMEOUT_SECONDS:-1800}"
15
+ WAIT_POLL_SECONDS_RAW="${MUSAFETY_FINISH_WAIT_POLL_SECONDS:-10}"
13
16
 
14
17
  normalize_bool() {
15
18
  local raw="${1:-}"
@@ -24,7 +27,27 @@ normalize_bool() {
24
27
  esac
25
28
  }
26
29
 
30
+ normalize_int() {
31
+ local raw="${1:-}"
32
+ local fallback="${2:-0}"
33
+ local min_value="${3:-0}"
34
+ local value="$raw"
35
+
36
+ if [[ -z "$value" || ! "$value" =~ ^[0-9]+$ ]]; then
37
+ value="$fallback"
38
+ fi
39
+
40
+ if (( value < min_value )); then
41
+ value="$min_value"
42
+ fi
43
+
44
+ printf '%s' "$value"
45
+ }
46
+
27
47
  CLEANUP_AFTER_MERGE="$(normalize_bool "$CLEANUP_AFTER_MERGE_RAW" "0")"
48
+ WAIT_FOR_MERGE="$(normalize_bool "$WAIT_FOR_MERGE_RAW" "0")"
49
+ WAIT_TIMEOUT_SECONDS="$(normalize_int "$WAIT_TIMEOUT_SECONDS_RAW" "1800" "30")"
50
+ WAIT_POLL_SECONDS="$(normalize_int "$WAIT_POLL_SECONDS_RAW" "10" "0")"
28
51
 
29
52
  while [[ $# -gt 0 ]]; do
30
53
  case "$1" in
@@ -59,6 +82,22 @@ while [[ $# -gt 0 ]]; do
59
82
  CLEANUP_AFTER_MERGE=0
60
83
  shift
61
84
  ;;
85
+ --wait-for-merge)
86
+ WAIT_FOR_MERGE=1
87
+ shift
88
+ ;;
89
+ --no-wait-for-merge)
90
+ WAIT_FOR_MERGE=0
91
+ shift
92
+ ;;
93
+ --wait-timeout-seconds)
94
+ WAIT_TIMEOUT_SECONDS="$(normalize_int "${2:-}" "1800" "30")"
95
+ shift 2
96
+ ;;
97
+ --wait-poll-seconds)
98
+ WAIT_POLL_SECONDS="$(normalize_int "${2:-}" "10" "0")"
99
+ shift 2
100
+ ;;
62
101
  --mode)
63
102
  MERGE_MODE="${2:-auto}"
64
103
  shift 2
@@ -73,7 +112,7 @@ while [[ $# -gt 0 ]]; do
73
112
  ;;
74
113
  *)
75
114
  echo "[agent-branch-finish] Unknown argument: $1" >&2
76
- echo "Usage: $0 [--base <branch>] [--branch <branch>] [--no-push] [--cleanup|--no-cleanup] [--keep-remote-branch|--delete-remote-branch] [--mode auto|direct|pr|--via-pr|--direct-only]" >&2
115
+ echo "Usage: $0 [--base <branch>] [--branch <branch>] [--no-push] [--cleanup|--no-cleanup] [--wait-for-merge|--no-wait-for-merge] [--wait-timeout-seconds <n>] [--wait-poll-seconds <n>] [--keep-remote-branch|--delete-remote-branch] [--mode auto|direct|pr|--via-pr|--direct-only]" >&2
77
116
  exit 1
78
117
  ;;
79
118
  esac
@@ -98,6 +137,14 @@ fi
98
137
 
99
138
  repo_root="$(git rev-parse --show-toplevel)"
100
139
  current_worktree="$(pwd -P)"
140
+ common_git_dir_raw="$(git -C "$repo_root" rev-parse --git-common-dir)"
141
+ if [[ "$common_git_dir_raw" == /* ]]; then
142
+ common_git_dir="$common_git_dir_raw"
143
+ else
144
+ common_git_dir="$(cd "$repo_root/$common_git_dir_raw" && pwd -P)"
145
+ fi
146
+ repo_common_root="$(cd "$common_git_dir/.." && pwd -P)"
147
+ agent_worktree_root="${repo_common_root}/.omx/agent-worktrees"
101
148
 
102
149
  if [[ -z "$SOURCE_BRANCH" ]]; then
103
150
  SOURCE_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
@@ -171,7 +218,7 @@ created_source_probe=0
171
218
  source_probe_path=""
172
219
 
173
220
  if [[ -z "$source_worktree" ]]; then
174
- source_probe_path="${repo_root}/.omx/agent-worktrees/__source-probe-${SOURCE_BRANCH//\//__}-$(date +%Y%m%d-%H%M%S)"
221
+ source_probe_path="${agent_worktree_root}/__source-probe-${SOURCE_BRANCH//\//__}-$(date +%Y%m%d-%H%M%S)"
175
222
  mkdir -p "$(dirname "$source_probe_path")"
176
223
  git -C "$repo_root" worktree add "$source_probe_path" "$SOURCE_BRANCH" >/dev/null
177
224
  source_worktree="$source_probe_path"
@@ -229,7 +276,7 @@ if [[ "$should_require_sync" -eq 1 ]] && git -C "$repo_root" show-ref --verify -
229
276
  fi
230
277
  fi
231
278
 
232
- integration_worktree="${repo_root}/.omx/agent-worktrees/__integrate-${BASE_BRANCH//\//__}-$(date +%Y%m%d-%H%M%S)"
279
+ integration_worktree="${agent_worktree_root}/__integrate-${BASE_BRANCH//\//__}-$(date +%Y%m%d-%H%M%S)"
233
280
  integration_branch="__agent_integrate_${BASE_BRANCH//\//_}_$(date +%Y%m%d_%H%M%S)"
234
281
  mkdir -p "$(dirname "$integration_worktree")"
235
282
 
@@ -289,6 +336,78 @@ is_local_branch_delete_error() {
289
336
  return 1
290
337
  }
291
338
 
339
+ read_pr_state() {
340
+ local state_line
341
+ state_line="$("$GH_BIN" pr view "$SOURCE_BRANCH" --json state,mergedAt,url --jq '[.state, (.mergedAt // ""), (.url // "")] | @tsv' 2>/dev/null || true)"
342
+ if [[ -z "$state_line" ]]; then
343
+ return 1
344
+ fi
345
+
346
+ local parsed_state=""
347
+ local parsed_merged_at=""
348
+ local parsed_url=""
349
+ IFS=$'\t' read -r parsed_state parsed_merged_at parsed_url <<< "$state_line"
350
+ PR_STATE="$parsed_state"
351
+ PR_MERGED_AT="$parsed_merged_at"
352
+ if [[ -n "$parsed_url" ]]; then
353
+ pr_url="$parsed_url"
354
+ fi
355
+ return 0
356
+ }
357
+
358
+ wait_for_pr_merge() {
359
+ local deadline
360
+ deadline=$(( $(date +%s) + WAIT_TIMEOUT_SECONDS ))
361
+ local wait_notice_printed=0
362
+ local merge_output=""
363
+
364
+ while true; do
365
+ if merge_output="$("$GH_BIN" pr merge "$SOURCE_BRANCH" --squash --delete-branch 2>&1)"; then
366
+ return 0
367
+ fi
368
+ if is_local_branch_delete_error "$merge_output"; then
369
+ echo "[agent-branch-finish] PR merged but gh could not delete the local branch (active worktree); continuing local cleanup." >&2
370
+ return 0
371
+ fi
372
+
373
+ PR_STATE=""
374
+ PR_MERGED_AT=""
375
+ if read_pr_state; then
376
+ if [[ "$PR_STATE" == "MERGED" || -n "$PR_MERGED_AT" ]]; then
377
+ return 0
378
+ fi
379
+ if [[ "$PR_STATE" == "CLOSED" ]]; then
380
+ echo "[agent-branch-finish] PR closed without merge; cannot continue auto-finish." >&2
381
+ if [[ -n "$pr_url" ]]; then
382
+ echo "[agent-branch-finish] PR: ${pr_url}" >&2
383
+ fi
384
+ if [[ -n "$merge_output" ]]; then
385
+ echo "$merge_output" >&2
386
+ fi
387
+ return 1
388
+ fi
389
+ fi
390
+
391
+ if [[ "$wait_notice_printed" -eq 0 ]]; then
392
+ echo "[agent-branch-finish] Waiting for required checks/reviews, then retrying merge automatically (timeout ${WAIT_TIMEOUT_SECONDS}s)." >&2
393
+ if [[ -n "$pr_url" ]]; then
394
+ echo "[agent-branch-finish] PR: ${pr_url}" >&2
395
+ fi
396
+ wait_notice_printed=1
397
+ fi
398
+
399
+ if (( $(date +%s) >= deadline )); then
400
+ echo "[agent-branch-finish] Timed out waiting for PR merge after ${WAIT_TIMEOUT_SECONDS}s." >&2
401
+ if [[ -n "$merge_output" ]]; then
402
+ echo "$merge_output" >&2
403
+ fi
404
+ return 2
405
+ fi
406
+
407
+ sleep "$WAIT_POLL_SECONDS"
408
+ done
409
+ }
410
+
292
411
  run_pr_flow() {
293
412
  if ! command -v "$GH_BIN" >/dev/null 2>&1; then
294
413
  echo "[agent-branch-finish] PR fallback requested but GitHub CLI not found: ${GH_BIN}" >&2
@@ -320,6 +439,11 @@ run_pr_flow() {
320
439
  return 0
321
440
  fi
322
441
 
442
+ if [[ "$WAIT_FOR_MERGE" -eq 1 ]]; then
443
+ wait_for_pr_merge
444
+ return $?
445
+ fi
446
+
323
447
  auto_output=""
324
448
  if auto_output="$("$GH_BIN" pr merge "$SOURCE_BRANCH" --squash --delete-branch --auto 2>&1)"; then
325
449
  echo "[agent-branch-finish] PR auto-merge enabled; waiting for required checks/reviews." >&2
@@ -365,6 +489,10 @@ if [[ "$PUSH_ENABLED" -eq 1 ]]; then
365
489
  if [[ -n "$pr_url" ]]; then
366
490
  echo "[agent-branch-finish] PR: ${pr_url}" >&2
367
491
  fi
492
+ if [[ "$WAIT_FOR_MERGE" -eq 1 ]]; then
493
+ echo "[agent-branch-finish] Merge did not complete within wait window; keeping branch open." >&2
494
+ exit 1
495
+ fi
368
496
  echo "[agent-branch-finish] Merge pending review/check policy. Branch cleanup skipped for now." >&2
369
497
  exit 0
370
498
  fi
@@ -390,16 +518,21 @@ fi
390
518
  if [[ "$CLEANUP_AFTER_MERGE" -eq 1 ]]; then
391
519
  if [[ "$source_worktree" == "$repo_root" ]]; then
392
520
  if is_clean_worktree "$source_worktree"; then
393
- git -C "$source_worktree" checkout "$BASE_BRANCH" >/dev/null 2>&1 || true
394
- if [[ "$PUSH_ENABLED" -eq 1 ]] && git -C "$repo_root" show-ref --verify --quiet "refs/remotes/origin/${BASE_BRANCH}"; then
521
+ switched_to_base=0
522
+ if git -C "$source_worktree" checkout "$BASE_BRANCH" >/dev/null 2>&1; then
523
+ switched_to_base=1
524
+ else
525
+ git -C "$source_worktree" checkout --detach >/dev/null 2>&1 || true
526
+ fi
527
+ if [[ "$switched_to_base" -eq 1 && "$PUSH_ENABLED" -eq 1 ]] && git -C "$repo_root" show-ref --verify --quiet "refs/remotes/origin/${BASE_BRANCH}"; then
395
528
  git -C "$source_worktree" pull --ff-only origin "$BASE_BRANCH" >/dev/null 2>&1 || true
396
529
  fi
397
530
  fi
398
- elif [[ "$source_worktree" == "$current_worktree" && "$source_worktree" == "${repo_root}/.omx/agent-worktrees"/* ]]; then
531
+ elif [[ "$source_worktree" == "$current_worktree" && "$source_worktree" == "${agent_worktree_root}"/* ]]; then
399
532
  git -C "$source_worktree" checkout --detach >/dev/null 2>&1 || true
400
533
  fi
401
534
 
402
- if [[ "$source_worktree" != "$current_worktree" && "$source_worktree" == "${repo_root}/.omx/agent-worktrees"/* ]]; then
535
+ if [[ "$source_worktree" != "$current_worktree" && "$source_worktree" == "${agent_worktree_root}"/* ]]; then
403
536
  git -C "$repo_root" worktree remove "$source_worktree" --force >/dev/null 2>&1 || true
404
537
  fi
405
538
 
@@ -423,7 +556,7 @@ if [[ "$CLEANUP_AFTER_MERGE" -eq 1 ]]; then
423
556
  fi
424
557
 
425
558
  echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/worktree."
426
- if [[ "$source_worktree" == "$current_worktree" && "$source_worktree" == "${repo_root}/.omx/agent-worktrees"/* ]]; then
559
+ if [[ "$source_worktree" == "$current_worktree" && "$source_worktree" == "${agent_worktree_root}"/* ]]; then
427
560
  echo "[agent-branch-finish] Current worktree '${source_worktree}' still exists because it is the active shell cwd." >&2
428
561
  echo "[agent-branch-finish] Leave this directory, then run: bash scripts/agent-worktree-prune.sh --base ${BASE_BRANCH} --delete-branches" >&2
429
562
  fi
@@ -152,6 +152,36 @@ is_protected_branch_name() {
152
152
  return 1
153
153
  }
154
154
 
155
+ hydrate_local_helper_in_worktree() {
156
+ local repo="$1"
157
+ local worktree="$2"
158
+ local relative_path="$3"
159
+ local worktree_target="${worktree}/${relative_path}"
160
+ local source_path=""
161
+
162
+ if [[ -e "$worktree_target" ]]; then
163
+ return 0
164
+ fi
165
+
166
+ if [[ -f "${repo}/${relative_path}" ]]; then
167
+ source_path="${repo}/${relative_path}"
168
+ elif [[ -f "${repo}/templates/${relative_path}" ]]; then
169
+ source_path="${repo}/templates/${relative_path}"
170
+ fi
171
+
172
+ if [[ -z "$source_path" ]]; then
173
+ return 0
174
+ fi
175
+
176
+ mkdir -p "$(dirname "$worktree_target")"
177
+ cp "$source_path" "$worktree_target"
178
+ if [[ -x "$source_path" ]]; then
179
+ chmod +x "$worktree_target"
180
+ fi
181
+
182
+ echo "[agent-branch-start] Hydrated local helper in worktree: ${relative_path}"
183
+ }
184
+
155
185
  if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
156
186
  echo "[agent-branch-start] Not inside a git repository." >&2
157
187
  exit 1
@@ -195,15 +225,17 @@ snapshot_name="$(resolve_active_codex_snapshot_name)"
195
225
  snapshot_slug="$(sanitize_slug "$snapshot_name" "")"
196
226
  timestamp="$(date +%Y%m%d-%H%M%S)"
197
227
  if [[ -n "$snapshot_slug" ]]; then
198
- branch_name="agent/${agent_slug}/${timestamp}-${snapshot_slug}-${task_slug}"
228
+ branch_name_base="agent/${agent_slug}/${snapshot_slug}-${task_slug}"
199
229
  else
200
- branch_name="agent/${agent_slug}/${timestamp}-${task_slug}"
230
+ branch_name_base="agent/${agent_slug}/${task_slug}"
201
231
  fi
202
232
 
203
- if git show-ref --verify --quiet "refs/heads/${branch_name}"; then
204
- echo "[agent-branch-start] Branch already exists: ${branch_name}" >&2
205
- exit 1
206
- fi
233
+ branch_name="$branch_name_base"
234
+ branch_suffix=2
235
+ while git show-ref --verify --quiet "refs/heads/${branch_name}"; do
236
+ branch_name="${branch_name_base}-${branch_suffix}"
237
+ branch_suffix=$((branch_suffix + 1))
238
+ done
207
239
 
208
240
  if [[ "$WORKTREE_MODE" -eq 0 ]]; then
209
241
  if [[ "$ALLOW_IN_PLACE" -ne 1 ]]; then
@@ -280,6 +312,8 @@ if [[ -n "$auto_transfer_stash_ref" ]]; then
280
312
  fi
281
313
  fi
282
314
 
315
+ hydrate_local_helper_in_worktree "$repo_root" "$worktree_path" "scripts/codex-agent.sh"
316
+
283
317
  echo "[agent-branch-start] Created branch: ${branch_name}"
284
318
  echo "[agent-branch-start] Worktree: ${worktree_path}"
285
319
  echo "[agent-branch-start] Next steps:"
@@ -8,7 +8,8 @@ BASE_BRANCH_EXPLICIT=0
8
8
  CODEX_BIN="${MUSAFETY_CODEX_BIN:-codex}"
9
9
  AUTO_FINISH_RAW="${MUSAFETY_CODEX_AUTO_FINISH:-true}"
10
10
  AUTO_REVIEW_ON_CONFLICT_RAW="${MUSAFETY_CODEX_AUTO_REVIEW_ON_CONFLICT:-true}"
11
- AUTO_CLEANUP_RAW="${MUSAFETY_CODEX_AUTO_CLEANUP:-false}"
11
+ AUTO_CLEANUP_RAW="${MUSAFETY_CODEX_AUTO_CLEANUP:-true}"
12
+ AUTO_WAIT_FOR_MERGE_RAW="${MUSAFETY_CODEX_WAIT_FOR_MERGE:-true}"
12
13
 
13
14
  normalize_bool() {
14
15
  local raw="${1:-}"
@@ -25,7 +26,8 @@ normalize_bool() {
25
26
 
26
27
  AUTO_FINISH="$(normalize_bool "$AUTO_FINISH_RAW" "1")"
27
28
  AUTO_REVIEW_ON_CONFLICT="$(normalize_bool "$AUTO_REVIEW_ON_CONFLICT_RAW" "1")"
28
- AUTO_CLEANUP="$(normalize_bool "$AUTO_CLEANUP_RAW" "0")"
29
+ AUTO_CLEANUP="$(normalize_bool "$AUTO_CLEANUP_RAW" "1")"
30
+ AUTO_WAIT_FOR_MERGE="$(normalize_bool "$AUTO_WAIT_FOR_MERGE_RAW" "1")"
29
31
 
30
32
  if [[ -n "$BASE_BRANCH" ]]; then
31
33
  BASE_BRANCH_EXPLICIT=1
@@ -74,6 +76,14 @@ while [[ $# -gt 0 ]]; do
74
76
  AUTO_CLEANUP=0
75
77
  shift
76
78
  ;;
79
+ --wait-for-merge)
80
+ AUTO_WAIT_FOR_MERGE=1
81
+ shift
82
+ ;;
83
+ --no-wait-for-merge)
84
+ AUTO_WAIT_FOR_MERGE=0
85
+ shift
86
+ ;;
77
87
  --)
78
88
  shift
79
89
  break
@@ -306,6 +316,9 @@ run_finish_flow() {
306
316
  if [[ "$AUTO_CLEANUP" -eq 1 ]]; then
307
317
  finish_args+=(--cleanup)
308
318
  fi
319
+ if [[ "$AUTO_WAIT_FOR_MERGE" -eq 1 ]]; then
320
+ finish_args+=(--wait-for-merge)
321
+ fi
309
322
 
310
323
  if has_origin_remote; then
311
324
  if command -v gh >/dev/null 2>&1 || command -v "${MUSAFETY_GH_BIN:-gh}" >/dev/null 2>&1; then
@@ -368,7 +381,11 @@ auto_finish_completed=0
368
381
  worktree_branch="$(git -C "$worktree_path" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
369
382
 
370
383
  if [[ "$AUTO_FINISH" -eq 1 && -n "$worktree_branch" && "$worktree_branch" != "HEAD" ]]; then
371
- if [[ "$AUTO_CLEANUP" -eq 1 ]]; then
384
+ if [[ "$AUTO_WAIT_FOR_MERGE" -eq 1 && "$AUTO_CLEANUP" -eq 1 ]]; then
385
+ echo "[codex-agent] Auto-finish enabled: commit -> push/PR -> wait for merge -> cleanup."
386
+ elif [[ "$AUTO_WAIT_FOR_MERGE" -eq 1 ]]; then
387
+ echo "[codex-agent] Auto-finish enabled: commit -> push/PR -> wait for merge (keep branch/worktree)."
388
+ elif [[ "$AUTO_CLEANUP" -eq 1 ]]; then
372
389
  echo "[codex-agent] Auto-finish enabled: commit -> push/PR -> merge -> cleanup."
373
390
  else
374
391
  echo "[codex-agent] Auto-finish enabled: commit -> push/PR -> merge (keep branch/worktree)."
@@ -401,7 +418,7 @@ if [[ -x "${repo_root}/scripts/agent-worktree-prune.sh" ]]; then
401
418
  if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 ]]; then
402
419
  prune_args+=(--base "$BASE_BRANCH")
403
420
  fi
404
- if [[ "$AUTO_CLEANUP" -eq 1 ]]; then
421
+ if [[ "$AUTO_CLEANUP" -eq 1 && "$auto_finish_completed" -eq 1 ]]; then
405
422
  prune_args+=(--delete-branches --delete-remote-branches)
406
423
  fi
407
424
  if ! bash "${repo_root}/scripts/agent-worktree-prune.sh" "${prune_args[@]}"; then