@imdeadpool/guardex 6.0.1 → 7.0.0
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/README.md +63 -39
- package/bin/multiagent-safety.js +368 -280
- package/package.json +3 -5
- package/templates/AGENTS.multiagent-safety.md +9 -81
- package/templates/claude/commands/guardex.md +6 -12
- package/templates/codex/skills/guardex/SKILL.md +18 -64
- package/templates/githooks/post-merge +39 -3
- package/templates/githooks/pre-commit +27 -193
- package/templates/githooks/pre-push +0 -0
- package/templates/scripts/agent-branch-finish.sh +70 -702
- package/templates/scripts/agent-branch-start.sh +76 -877
- package/templates/scripts/agent-worktree-prune.sh +65 -353
- package/templates/scripts/codex-agent.sh +626 -238
- package/templates/scripts/install-agent-git-hooks.sh +4 -27
- package/templates/scripts/openspec/init-change-workspace.sh +4 -50
- package/templates/scripts/openspec/init-plan-workspace.sh +48 -495
- package/templates/scripts/review-bot-watch.sh +11 -11
|
@@ -6,11 +6,33 @@ AGENT_NAME="${GUARDEX_AGENT_NAME:-agent}"
|
|
|
6
6
|
BASE_BRANCH="${GUARDEX_BASE_BRANCH:-}"
|
|
7
7
|
BASE_BRANCH_EXPLICIT=0
|
|
8
8
|
CODEX_BIN="${GUARDEX_CODEX_BIN:-codex}"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
AUTO_FINISH_RAW="${GUARDEX_CODEX_AUTO_FINISH:-true}"
|
|
10
|
+
AUTO_REVIEW_ON_CONFLICT_RAW="${GUARDEX_CODEX_AUTO_REVIEW_ON_CONFLICT:-true}"
|
|
11
|
+
AUTO_CLEANUP_RAW="${GUARDEX_CODEX_AUTO_CLEANUP:-true}"
|
|
12
|
+
AUTO_WAIT_FOR_MERGE_RAW="${GUARDEX_CODEX_WAIT_FOR_MERGE:-true}"
|
|
13
|
+
OPENSPEC_AUTO_INIT_RAW="${GUARDEX_OPENSPEC_AUTO_INIT:-true}"
|
|
14
|
+
OPENSPEC_PLAN_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_PLAN_SLUG:-}"
|
|
15
|
+
OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
|
|
16
|
+
OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
|
|
17
|
+
|
|
18
|
+
normalize_bool() {
|
|
19
|
+
local raw="${1:-}"
|
|
20
|
+
local fallback="${2:-0}"
|
|
21
|
+
local lowered
|
|
22
|
+
lowered="$(printf '%s' "$raw" | tr '[:upper:]' '[:lower:]')"
|
|
23
|
+
case "$lowered" in
|
|
24
|
+
1|true|yes|on) printf '1' ;;
|
|
25
|
+
0|false|no|off) printf '0' ;;
|
|
26
|
+
'') printf '%s' "$fallback" ;;
|
|
27
|
+
*) printf '%s' "$fallback" ;;
|
|
28
|
+
esac
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
AUTO_FINISH="$(normalize_bool "$AUTO_FINISH_RAW" "1")"
|
|
32
|
+
AUTO_REVIEW_ON_CONFLICT="$(normalize_bool "$AUTO_REVIEW_ON_CONFLICT_RAW" "1")"
|
|
33
|
+
AUTO_CLEANUP="$(normalize_bool "$AUTO_CLEANUP_RAW" "1")"
|
|
34
|
+
AUTO_WAIT_FOR_MERGE="$(normalize_bool "$AUTO_WAIT_FOR_MERGE_RAW" "1")"
|
|
35
|
+
OPENSPEC_AUTO_INIT="$(normalize_bool "$OPENSPEC_AUTO_INIT_RAW" "1")"
|
|
14
36
|
|
|
15
37
|
if [[ -n "$BASE_BRANCH" ]]; then
|
|
16
38
|
BASE_BRANCH_EXPLICIT=1
|
|
@@ -35,25 +57,37 @@ while [[ $# -gt 0 ]]; do
|
|
|
35
57
|
CODEX_BIN="${2:-$CODEX_BIN}"
|
|
36
58
|
shift 2
|
|
37
59
|
;;
|
|
38
|
-
--
|
|
39
|
-
|
|
40
|
-
shift
|
|
60
|
+
--auto-finish)
|
|
61
|
+
AUTO_FINISH=1
|
|
62
|
+
shift
|
|
41
63
|
;;
|
|
42
|
-
--
|
|
43
|
-
|
|
44
|
-
shift
|
|
64
|
+
--no-auto-finish)
|
|
65
|
+
AUTO_FINISH=0
|
|
66
|
+
shift
|
|
45
67
|
;;
|
|
46
|
-
--
|
|
47
|
-
|
|
68
|
+
--auto-review-on-conflict)
|
|
69
|
+
AUTO_REVIEW_ON_CONFLICT=1
|
|
48
70
|
shift
|
|
49
71
|
;;
|
|
50
|
-
--no-
|
|
51
|
-
|
|
72
|
+
--no-auto-review-on-conflict)
|
|
73
|
+
AUTO_REVIEW_ON_CONFLICT=0
|
|
52
74
|
shift
|
|
53
75
|
;;
|
|
54
|
-
--
|
|
55
|
-
|
|
56
|
-
shift
|
|
76
|
+
--cleanup)
|
|
77
|
+
AUTO_CLEANUP=1
|
|
78
|
+
shift
|
|
79
|
+
;;
|
|
80
|
+
--no-cleanup)
|
|
81
|
+
AUTO_CLEANUP=0
|
|
82
|
+
shift
|
|
83
|
+
;;
|
|
84
|
+
--wait-for-merge)
|
|
85
|
+
AUTO_WAIT_FOR_MERGE=1
|
|
86
|
+
shift
|
|
87
|
+
;;
|
|
88
|
+
--no-wait-for-merge)
|
|
89
|
+
AUTO_WAIT_FOR_MERGE=0
|
|
90
|
+
shift
|
|
57
91
|
;;
|
|
58
92
|
--)
|
|
59
93
|
shift
|
|
@@ -96,6 +130,160 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
|
96
130
|
fi
|
|
97
131
|
repo_root="$(git rev-parse --show-toplevel)"
|
|
98
132
|
|
|
133
|
+
sanitize_slug() {
|
|
134
|
+
local raw="$1"
|
|
135
|
+
local fallback="${2:-task}"
|
|
136
|
+
local slug
|
|
137
|
+
slug="$(printf '%s' "$raw" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//; s/-{2,}/-/g')"
|
|
138
|
+
if [[ -z "$slug" ]]; then
|
|
139
|
+
slug="$fallback"
|
|
140
|
+
fi
|
|
141
|
+
printf '%s' "$slug"
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
resolve_openspec_plan_slug() {
|
|
145
|
+
local branch_name="$1"
|
|
146
|
+
local task_slug
|
|
147
|
+
task_slug="$(sanitize_slug "$TASK_NAME" "task")"
|
|
148
|
+
if [[ -n "$OPENSPEC_PLAN_SLUG_OVERRIDE" ]]; then
|
|
149
|
+
sanitize_slug "$OPENSPEC_PLAN_SLUG_OVERRIDE" "$task_slug"
|
|
150
|
+
return 0
|
|
151
|
+
fi
|
|
152
|
+
sanitize_slug "${branch_name//\//-}" "$task_slug"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
resolve_openspec_change_slug() {
|
|
156
|
+
local branch_name="$1"
|
|
157
|
+
local task_slug
|
|
158
|
+
task_slug="$(sanitize_slug "$TASK_NAME" "task")"
|
|
159
|
+
if [[ -n "$OPENSPEC_CHANGE_SLUG_OVERRIDE" ]]; then
|
|
160
|
+
sanitize_slug "$OPENSPEC_CHANGE_SLUG_OVERRIDE" "$task_slug"
|
|
161
|
+
return 0
|
|
162
|
+
fi
|
|
163
|
+
sanitize_slug "${branch_name//\//-}" "$task_slug"
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
resolve_openspec_capability_slug() {
|
|
167
|
+
local task_slug
|
|
168
|
+
task_slug="$(sanitize_slug "$TASK_NAME" "task")"
|
|
169
|
+
if [[ -n "$OPENSPEC_CAPABILITY_SLUG_OVERRIDE" ]]; then
|
|
170
|
+
sanitize_slug "$OPENSPEC_CAPABILITY_SLUG_OVERRIDE" "$task_slug"
|
|
171
|
+
return 0
|
|
172
|
+
fi
|
|
173
|
+
sanitize_slug "$task_slug" "general-behavior"
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
hydrate_local_helper_in_worktree() {
|
|
177
|
+
local worktree="$1"
|
|
178
|
+
local relative_path="$2"
|
|
179
|
+
local worktree_target="${worktree}/${relative_path}"
|
|
180
|
+
local source_path=""
|
|
181
|
+
|
|
182
|
+
if [[ -e "$worktree_target" ]]; then
|
|
183
|
+
return 0
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
if [[ -f "${repo_root}/${relative_path}" ]]; then
|
|
187
|
+
source_path="${repo_root}/${relative_path}"
|
|
188
|
+
elif [[ -f "${repo_root}/templates/${relative_path}" ]]; then
|
|
189
|
+
source_path="${repo_root}/templates/${relative_path}"
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
if [[ -z "$source_path" ]]; then
|
|
193
|
+
return 0
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
mkdir -p "$(dirname "$worktree_target")"
|
|
197
|
+
cp "$source_path" "$worktree_target"
|
|
198
|
+
if [[ -x "$source_path" ]]; then
|
|
199
|
+
chmod +x "$worktree_target"
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
echo "[codex-agent] Hydrated local helper in sandbox: ${relative_path}"
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
resolve_start_base_branch() {
|
|
206
|
+
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -n "$BASE_BRANCH" ]]; then
|
|
207
|
+
printf '%s' "$BASE_BRANCH"
|
|
208
|
+
return 0
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
local configured_base
|
|
212
|
+
configured_base="$(git -C "$repo_root" config --get multiagent.baseBranch || true)"
|
|
213
|
+
if [[ -n "$configured_base" ]]; then
|
|
214
|
+
printf '%s' "$configured_base"
|
|
215
|
+
return 0
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
printf 'dev'
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
resolve_start_ref() {
|
|
222
|
+
local base_branch="$1"
|
|
223
|
+
git -C "$repo_root" fetch origin "$base_branch" --quiet >/dev/null 2>&1 || true
|
|
224
|
+
if git -C "$repo_root" show-ref --verify --quiet "refs/remotes/origin/${base_branch}"; then
|
|
225
|
+
printf 'origin/%s' "$base_branch"
|
|
226
|
+
return 0
|
|
227
|
+
fi
|
|
228
|
+
if git -C "$repo_root" show-ref --verify --quiet "refs/heads/${base_branch}"; then
|
|
229
|
+
printf '%s' "$base_branch"
|
|
230
|
+
return 0
|
|
231
|
+
fi
|
|
232
|
+
return 1
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
restore_repo_branch_if_changed() {
|
|
236
|
+
local expected_branch="$1"
|
|
237
|
+
if [[ -z "$expected_branch" || "$expected_branch" == "HEAD" ]]; then
|
|
238
|
+
return 0
|
|
239
|
+
fi
|
|
240
|
+
local current_branch
|
|
241
|
+
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
242
|
+
if [[ -z "$current_branch" || "$current_branch" == "$expected_branch" ]]; then
|
|
243
|
+
return 0
|
|
244
|
+
fi
|
|
245
|
+
git -C "$repo_root" checkout "$expected_branch" >/dev/null 2>&1
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
start_sandbox_fallback() {
|
|
249
|
+
local base_branch start_ref timestamp task_slug agent_slug branch_name_base branch_name suffix
|
|
250
|
+
local worktree_root worktree_path
|
|
251
|
+
|
|
252
|
+
base_branch="$(resolve_start_base_branch)"
|
|
253
|
+
if ! start_ref="$(resolve_start_ref "$base_branch")"; then
|
|
254
|
+
echo "[codex-agent] Unable to resolve base ref for fallback sandbox start: ${base_branch}" >&2
|
|
255
|
+
return 1
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
timestamp="$(date +%Y%m%d-%H%M%S)"
|
|
259
|
+
task_slug="$(sanitize_slug "$TASK_NAME" "task")"
|
|
260
|
+
agent_slug="$(sanitize_slug "$AGENT_NAME" "agent")"
|
|
261
|
+
branch_name_base="agent/${agent_slug}/${timestamp}-${task_slug}"
|
|
262
|
+
branch_name="$branch_name_base"
|
|
263
|
+
suffix=2
|
|
264
|
+
while git -C "$repo_root" show-ref --verify --quiet "refs/heads/${branch_name}"; do
|
|
265
|
+
branch_name="${branch_name_base}-${suffix}"
|
|
266
|
+
suffix=$((suffix + 1))
|
|
267
|
+
done
|
|
268
|
+
|
|
269
|
+
worktree_root="${repo_root}/.omx/agent-worktrees"
|
|
270
|
+
mkdir -p "$worktree_root"
|
|
271
|
+
worktree_path="${worktree_root}/${branch_name//\//__}"
|
|
272
|
+
if [[ -e "$worktree_path" ]]; then
|
|
273
|
+
echo "[codex-agent] Fallback worktree path already exists: $worktree_path" >&2
|
|
274
|
+
return 1
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
git -C "$repo_root" worktree add -b "$branch_name" "$worktree_path" "$start_ref" >/dev/null
|
|
278
|
+
git -C "$repo_root" config "branch.${branch_name}.guardexBase" "$base_branch" >/dev/null 2>&1 || true
|
|
279
|
+
if git -C "$repo_root" show-ref --verify --quiet "refs/remotes/origin/${base_branch}"; then
|
|
280
|
+
git -C "$worktree_path" branch --set-upstream-to="origin/${base_branch}" "$branch_name" >/dev/null 2>&1 || true
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
printf '[agent-branch-start] Created branch: %s\n' "$branch_name"
|
|
284
|
+
printf '[agent-branch-start] Worktree: %s\n' "$worktree_path"
|
|
285
|
+
}
|
|
286
|
+
|
|
99
287
|
if [[ ! -x "${repo_root}/scripts/agent-branch-start.sh" ]]; then
|
|
100
288
|
echo "[codex-agent] Missing scripts/agent-branch-start.sh. Run: gx setup" >&2
|
|
101
289
|
exit 1
|
|
@@ -105,291 +293,489 @@ start_args=("$TASK_NAME" "$AGENT_NAME")
|
|
|
105
293
|
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 ]]; then
|
|
106
294
|
start_args+=("$BASE_BRANCH")
|
|
107
295
|
fi
|
|
108
|
-
|
|
109
|
-
|
|
296
|
+
|
|
297
|
+
initial_repo_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
298
|
+
start_output=""
|
|
299
|
+
start_status=0
|
|
300
|
+
set +e
|
|
301
|
+
start_output="$(GUARDEX_OPENSPEC_AUTO_INIT=0 bash "${repo_root}/scripts/agent-branch-start.sh" "${start_args[@]}" 2>&1)"
|
|
302
|
+
start_status=$?
|
|
303
|
+
set -e
|
|
304
|
+
|
|
305
|
+
worktree_path="$(printf '%s\n' "$start_output" | sed -n 's/^\[agent-branch-start\] Worktree: //p' | tail -n1)"
|
|
306
|
+
current_repo_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
307
|
+
resolved_repo_root="$(cd "$repo_root" && pwd -P)"
|
|
308
|
+
resolved_worktree_path=""
|
|
309
|
+
if [[ -n "$worktree_path" && -d "$worktree_path" ]]; then
|
|
310
|
+
resolved_worktree_path="$(cd "$worktree_path" && pwd -P)"
|
|
311
|
+
fi
|
|
312
|
+
|
|
313
|
+
fallback_reason=""
|
|
314
|
+
if [[ "$start_status" -ne 0 ]]; then
|
|
315
|
+
fallback_reason="starter exited with status ${start_status}"
|
|
316
|
+
elif [[ -z "$worktree_path" ]]; then
|
|
317
|
+
fallback_reason="starter did not report worktree path"
|
|
318
|
+
elif [[ -n "$resolved_worktree_path" && "$resolved_worktree_path" == "$resolved_repo_root" ]]; then
|
|
319
|
+
fallback_reason="starter pointed to active checkout path"
|
|
320
|
+
elif [[ -n "$initial_repo_branch" && -n "$current_repo_branch" && "$current_repo_branch" != "$initial_repo_branch" ]]; then
|
|
321
|
+
fallback_reason="starter switched active checkout branch"
|
|
110
322
|
fi
|
|
111
|
-
|
|
112
|
-
|
|
323
|
+
|
|
324
|
+
if [[ -n "$fallback_reason" ]]; then
|
|
325
|
+
if ! restore_repo_branch_if_changed "$initial_repo_branch"; then
|
|
326
|
+
echo "[codex-agent] agent-branch-start changed the active checkout branch and restore failed." >&2
|
|
327
|
+
echo "[codex-agent] Run 'gx setup --target ${repo_root}' and 'gx doctor --target ${repo_root}', then retry." >&2
|
|
328
|
+
exit 1
|
|
329
|
+
fi
|
|
330
|
+
if [[ -n "$start_output" ]]; then
|
|
331
|
+
printf '%s\n' "$start_output" >&2
|
|
332
|
+
fi
|
|
333
|
+
echo "[codex-agent] Unsafe starter output (${fallback_reason}); creating sandbox worktree directly." >&2
|
|
334
|
+
start_output="$(start_sandbox_fallback)"
|
|
335
|
+
printf '%s\n' "$start_output"
|
|
336
|
+
worktree_path="$(printf '%s\n' "$start_output" | sed -n 's/^\[agent-branch-start\] Worktree: //p' | tail -n1)"
|
|
337
|
+
else
|
|
338
|
+
printf '%s\n' "$start_output"
|
|
113
339
|
fi
|
|
114
|
-
|
|
115
|
-
|
|
340
|
+
|
|
341
|
+
if [[ -z "$worktree_path" ]]; then
|
|
342
|
+
echo "[codex-agent] Could not determine sandbox worktree path from sandbox startup output." >&2
|
|
343
|
+
echo "[codex-agent] Run 'gx setup --target ${repo_root}' and 'gx doctor --target ${repo_root}', then retry." >&2
|
|
344
|
+
exit 1
|
|
116
345
|
fi
|
|
117
346
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
347
|
+
if [[ ! -d "$worktree_path" ]]; then
|
|
348
|
+
echo "[codex-agent] Reported worktree path does not exist: $worktree_path" >&2
|
|
349
|
+
exit 1
|
|
350
|
+
fi
|
|
351
|
+
|
|
352
|
+
has_origin_remote() {
|
|
353
|
+
git -C "$repo_root" remote get-url origin >/dev/null 2>&1
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
resolve_worktree_base_branch() {
|
|
357
|
+
local _wt="$1"
|
|
358
|
+
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -n "$BASE_BRANCH" ]]; then
|
|
359
|
+
printf '%s' "$BASE_BRANCH"
|
|
360
|
+
return 0
|
|
361
|
+
fi
|
|
362
|
+
|
|
363
|
+
local configured_base
|
|
364
|
+
configured_base="$(git -C "$repo_root" config --get multiagent.baseBranch || true)"
|
|
365
|
+
if [[ -n "$configured_base" ]]; then
|
|
366
|
+
printf '%s' "$configured_base"
|
|
367
|
+
return 0
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
printf 'dev'
|
|
371
|
+
}
|
|
121
372
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
373
|
+
sync_worktree_with_base() {
|
|
374
|
+
local wt="$1"
|
|
375
|
+
if ! has_origin_remote; then
|
|
376
|
+
return 0
|
|
126
377
|
fi
|
|
127
378
|
|
|
128
|
-
|
|
129
|
-
|
|
379
|
+
local base_branch
|
|
380
|
+
base_branch="$(resolve_worktree_base_branch "$wt")"
|
|
381
|
+
if [[ -z "$base_branch" ]]; then
|
|
382
|
+
return 0
|
|
130
383
|
fi
|
|
131
384
|
|
|
132
|
-
if
|
|
133
|
-
|
|
385
|
+
if ! git -C "$wt" fetch origin "$base_branch" --quiet; then
|
|
386
|
+
echo "[codex-agent] Warning: could not fetch origin/${base_branch} before task start." >&2
|
|
387
|
+
return 0
|
|
134
388
|
fi
|
|
135
389
|
|
|
136
|
-
|
|
390
|
+
if ! git -C "$wt" show-ref --verify --quiet "refs/remotes/origin/${base_branch}"; then
|
|
391
|
+
return 0
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
local behind_count
|
|
395
|
+
behind_count="$(git -C "$wt" rev-list --left-right --count "HEAD...origin/${base_branch}" 2>/dev/null | awk '{print $2}')"
|
|
396
|
+
behind_count="${behind_count:-0}"
|
|
397
|
+
if [[ "$behind_count" -le 0 ]]; then
|
|
398
|
+
return 0
|
|
399
|
+
fi
|
|
400
|
+
|
|
401
|
+
local branch
|
|
402
|
+
branch="$(git -C "$wt" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
403
|
+
echo "[codex-agent] Task sync: '${branch}' is behind origin/${base_branch} by ${behind_count} commit(s). Rebasing before launch..."
|
|
404
|
+
if ! git -C "$wt" rebase "origin/${base_branch}"; then
|
|
405
|
+
echo "[codex-agent] Task sync failed. Resolve and continue in sandbox:" >&2
|
|
406
|
+
echo " git -C \"$wt\" rebase --continue" >&2
|
|
407
|
+
echo " # or abort" >&2
|
|
408
|
+
echo " git -C \"$wt\" rebase --abort" >&2
|
|
409
|
+
return 1
|
|
410
|
+
fi
|
|
411
|
+
echo "[codex-agent] Task sync complete."
|
|
412
|
+
return 0
|
|
137
413
|
}
|
|
138
414
|
|
|
139
|
-
|
|
140
|
-
local
|
|
141
|
-
local
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
415
|
+
ensure_openspec_plan_workspace() {
|
|
416
|
+
local wt="$1"
|
|
417
|
+
local branch="$2"
|
|
418
|
+
|
|
419
|
+
if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
|
|
420
|
+
return 0
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
hydrate_local_helper_in_worktree "$wt" "scripts/openspec/init-plan-workspace.sh"
|
|
424
|
+
|
|
425
|
+
local openspec_script="${wt}/scripts/openspec/init-plan-workspace.sh"
|
|
426
|
+
if [[ ! -f "$openspec_script" ]]; then
|
|
427
|
+
echo "[codex-agent] Missing OpenSpec init script in sandbox: ${openspec_script}" >&2
|
|
428
|
+
echo "[codex-agent] Run 'gx setup --target ${repo_root}' and retry." >&2
|
|
429
|
+
return 1
|
|
430
|
+
fi
|
|
431
|
+
if [[ ! -x "$openspec_script" ]]; then
|
|
432
|
+
chmod +x "$openspec_script" 2>/dev/null || true
|
|
433
|
+
fi
|
|
434
|
+
|
|
435
|
+
local plan_slug
|
|
436
|
+
plan_slug="$(resolve_openspec_plan_slug "$branch")"
|
|
437
|
+
local init_output=""
|
|
438
|
+
if ! init_output="$(
|
|
439
|
+
cd "$wt"
|
|
440
|
+
bash "scripts/openspec/init-plan-workspace.sh" "$plan_slug" 2>&1
|
|
441
|
+
)"; then
|
|
442
|
+
printf '%s\n' "$init_output" >&2
|
|
443
|
+
echo "[codex-agent] OpenSpec workspace initialization failed for plan '${plan_slug}'." >&2
|
|
444
|
+
return 1
|
|
445
|
+
fi
|
|
446
|
+
if [[ -n "$init_output" ]]; then
|
|
447
|
+
printf '%s\n' "$init_output"
|
|
448
|
+
fi
|
|
449
|
+
echo "[codex-agent] OpenSpec plan workspace: ${wt}/openspec/plan/${plan_slug}"
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
ensure_openspec_change_workspace() {
|
|
453
|
+
local wt="$1"
|
|
454
|
+
local branch="$2"
|
|
145
455
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if [[ -f "$notepad_path" ]]; then
|
|
149
|
-
export OMX_NOTEPAD_PATH="$notepad_path"
|
|
456
|
+
if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
|
|
457
|
+
return 0
|
|
150
458
|
fi
|
|
151
|
-
|
|
152
|
-
|
|
459
|
+
|
|
460
|
+
hydrate_local_helper_in_worktree "$wt" "scripts/openspec/init-change-workspace.sh"
|
|
461
|
+
|
|
462
|
+
local openspec_script="${wt}/scripts/openspec/init-change-workspace.sh"
|
|
463
|
+
if [[ ! -f "$openspec_script" ]]; then
|
|
464
|
+
echo "[codex-agent] Missing OpenSpec change init script in sandbox: ${openspec_script}" >&2
|
|
465
|
+
echo "[codex-agent] Run 'gx setup --target ${repo_root}' and retry." >&2
|
|
466
|
+
return 1
|
|
153
467
|
fi
|
|
154
|
-
if [[ -
|
|
155
|
-
|
|
468
|
+
if [[ ! -x "$openspec_script" ]]; then
|
|
469
|
+
chmod +x "$openspec_script" 2>/dev/null || true
|
|
156
470
|
fi
|
|
157
471
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
472
|
+
local change_slug capability_slug init_output=""
|
|
473
|
+
change_slug="$(resolve_openspec_change_slug "$branch")"
|
|
474
|
+
capability_slug="$(resolve_openspec_capability_slug)"
|
|
475
|
+
if ! init_output="$(
|
|
476
|
+
cd "$wt"
|
|
477
|
+
bash "scripts/openspec/init-change-workspace.sh" "$change_slug" "$capability_slug" 2>&1
|
|
478
|
+
)"; then
|
|
479
|
+
printf '%s\n' "$init_output" >&2
|
|
480
|
+
echo "[codex-agent] OpenSpec workspace initialization failed for change '${change_slug}'." >&2
|
|
481
|
+
return 1
|
|
482
|
+
fi
|
|
483
|
+
if [[ -n "$init_output" ]]; then
|
|
484
|
+
printf '%s\n' "$init_output"
|
|
161
485
|
fi
|
|
486
|
+
echo "[codex-agent] OpenSpec change workspace: ${wt}/openspec/changes/${change_slug}"
|
|
487
|
+
}
|
|
162
488
|
|
|
163
|
-
|
|
489
|
+
worktree_has_changes() {
|
|
490
|
+
local wt="$1"
|
|
491
|
+
if ! git -C "$wt" diff --quiet -- . ":(exclude).omx/state/agent-file-locks.json"; then
|
|
492
|
+
return 0
|
|
493
|
+
fi
|
|
494
|
+
if ! git -C "$wt" diff --cached --quiet -- . ":(exclude).omx/state/agent-file-locks.json"; then
|
|
495
|
+
return 0
|
|
496
|
+
fi
|
|
497
|
+
if [[ -n "$(git -C "$wt" ls-files --others --exclude-standard)" ]]; then
|
|
498
|
+
return 0
|
|
499
|
+
fi
|
|
500
|
+
return 1
|
|
164
501
|
}
|
|
165
502
|
|
|
166
|
-
|
|
167
|
-
local
|
|
168
|
-
local
|
|
503
|
+
claim_changed_files() {
|
|
504
|
+
local wt="$1"
|
|
505
|
+
local branch="$2"
|
|
506
|
+
local lock_script="${repo_root}/scripts/agent-file-locks.py"
|
|
169
507
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
printf '%s' "$stored_base"
|
|
173
|
-
return
|
|
508
|
+
if [[ ! -x "$lock_script" ]]; then
|
|
509
|
+
return 0
|
|
174
510
|
fi
|
|
175
511
|
|
|
176
|
-
|
|
177
|
-
|
|
512
|
+
local changed_raw deleted_raw
|
|
513
|
+
changed_raw="$({
|
|
514
|
+
git -C "$wt" diff --name-only -- . ":(exclude).omx/state/agent-file-locks.json";
|
|
515
|
+
git -C "$wt" diff --cached --name-only -- . ":(exclude).omx/state/agent-file-locks.json";
|
|
516
|
+
git -C "$wt" ls-files --others --exclude-standard;
|
|
517
|
+
} | sed '/^$/d' | sort -u)"
|
|
518
|
+
|
|
519
|
+
if [[ -n "$changed_raw" ]]; then
|
|
520
|
+
mapfile -t changed_files < <(printf '%s\n' "$changed_raw")
|
|
521
|
+
python3 "$lock_script" claim --branch "$branch" "${changed_files[@]}" >/dev/null 2>&1 || true
|
|
522
|
+
fi
|
|
523
|
+
|
|
524
|
+
deleted_raw="$({
|
|
525
|
+
git -C "$wt" diff --name-only --diff-filter=D -- . ":(exclude).omx/state/agent-file-locks.json";
|
|
526
|
+
git -C "$wt" diff --cached --name-only --diff-filter=D -- . ":(exclude).omx/state/agent-file-locks.json";
|
|
527
|
+
} | sed '/^$/d' | sort -u)"
|
|
528
|
+
|
|
529
|
+
if [[ -n "$deleted_raw" ]]; then
|
|
530
|
+
mapfile -t deleted_files < <(printf '%s\n' "$deleted_raw")
|
|
531
|
+
python3 "$lock_script" allow-delete --branch "$branch" "${deleted_files[@]}" >/dev/null 2>&1 || true
|
|
178
532
|
fi
|
|
179
533
|
}
|
|
180
534
|
|
|
181
|
-
|
|
182
|
-
local
|
|
183
|
-
local
|
|
184
|
-
local hint=""
|
|
535
|
+
auto_commit_worktree_changes() {
|
|
536
|
+
local wt="$1"
|
|
537
|
+
local branch="$2"
|
|
185
538
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
hint="${hint} --base \"${base}\""
|
|
539
|
+
if ! worktree_has_changes "$wt"; then
|
|
540
|
+
return 0
|
|
189
541
|
fi
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
542
|
+
|
|
543
|
+
claim_changed_files "$wt" "$branch"
|
|
544
|
+
git -C "$wt" add -A
|
|
545
|
+
|
|
546
|
+
if git -C "$wt" diff --cached --quiet -- . ":(exclude).omx/state/agent-file-locks.json"; then
|
|
547
|
+
return 0
|
|
193
548
|
fi
|
|
194
|
-
|
|
195
|
-
|
|
549
|
+
|
|
550
|
+
local default_message="Auto-finish: ${TASK_NAME}"
|
|
551
|
+
local commit_message="${GUARDEX_CODEX_AUTO_COMMIT_MESSAGE:-$default_message}"
|
|
552
|
+
local commit_output=""
|
|
553
|
+
|
|
554
|
+
if commit_output="$(git -C "$wt" commit -m "$commit_message" 2>&1)"; then
|
|
555
|
+
echo "[codex-agent] Auto-committed sandbox changes on '${branch}'."
|
|
556
|
+
return 0
|
|
196
557
|
fi
|
|
197
558
|
|
|
198
|
-
|
|
559
|
+
if auto_sync_for_commit_retry "$wt" "$branch"; then
|
|
560
|
+
claim_changed_files "$wt" "$branch"
|
|
561
|
+
git -C "$wt" add -A
|
|
562
|
+
if commit_output="$(git -C "$wt" commit -m "$commit_message" 2>&1)"; then
|
|
563
|
+
echo "[codex-agent] Auto-committed sandbox changes on '${branch}' after sync retry."
|
|
564
|
+
return 0
|
|
565
|
+
fi
|
|
566
|
+
fi
|
|
567
|
+
|
|
568
|
+
echo "[codex-agent] Auto-commit failed in sandbox. Keeping branch for manual review: $branch" >&2
|
|
569
|
+
if [[ -n "$commit_output" ]]; then
|
|
570
|
+
printf '%s\n' "$commit_output" >&2
|
|
571
|
+
fi
|
|
572
|
+
return 1
|
|
199
573
|
}
|
|
200
574
|
|
|
201
|
-
|
|
202
|
-
local
|
|
203
|
-
local
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
575
|
+
auto_sync_for_commit_retry() {
|
|
576
|
+
local wt="$1"
|
|
577
|
+
local branch="$2"
|
|
578
|
+
|
|
579
|
+
if ! has_origin_remote; then
|
|
580
|
+
return 1
|
|
581
|
+
fi
|
|
582
|
+
|
|
583
|
+
local base_branch
|
|
584
|
+
base_branch="$(resolve_worktree_base_branch "$wt")"
|
|
585
|
+
if [[ -z "$base_branch" ]]; then
|
|
586
|
+
return 1
|
|
587
|
+
fi
|
|
588
|
+
|
|
589
|
+
if ! git -C "$wt" fetch origin "$base_branch" --quiet; then
|
|
590
|
+
return 1
|
|
591
|
+
fi
|
|
592
|
+
|
|
593
|
+
if ! git -C "$wt" show-ref --verify --quiet "refs/remotes/origin/${base_branch}"; then
|
|
594
|
+
return 1
|
|
595
|
+
fi
|
|
596
|
+
|
|
597
|
+
local behind_count
|
|
598
|
+
behind_count="$(git -C "$wt" rev-list --left-right --count "HEAD...origin/${base_branch}" 2>/dev/null | awk '{print $2}')"
|
|
599
|
+
behind_count="${behind_count:-0}"
|
|
600
|
+
if [[ "$behind_count" -le 0 ]]; then
|
|
601
|
+
return 1
|
|
602
|
+
fi
|
|
603
|
+
|
|
604
|
+
echo "[codex-agent] Auto-commit retry: '${branch}' is behind origin/${base_branch} by ${behind_count} commit(s). Syncing and retrying..."
|
|
605
|
+
|
|
606
|
+
local stash_ref=""
|
|
607
|
+
local stash_output=""
|
|
608
|
+
if worktree_has_changes "$wt"; then
|
|
609
|
+
if ! stash_output="$(git -C "$wt" stash push --include-untracked -m "codex-agent-autocommit-sync-${branch}-$(date +%s)" 2>&1)"; then
|
|
610
|
+
return 1
|
|
611
|
+
fi
|
|
612
|
+
stash_ref="$(printf '%s\n' "$stash_output" | grep -o 'stash@{[0-9]\+}' | head -n 1 || true)"
|
|
613
|
+
fi
|
|
614
|
+
|
|
615
|
+
if ! git -C "$wt" rebase "origin/${base_branch}" >/dev/null 2>&1; then
|
|
616
|
+
git -C "$wt" rebase --abort >/dev/null 2>&1 || true
|
|
617
|
+
if [[ -n "$stash_ref" ]]; then
|
|
618
|
+
git -C "$wt" stash pop "$stash_ref" >/dev/null 2>&1 || true
|
|
619
|
+
fi
|
|
620
|
+
return 1
|
|
621
|
+
fi
|
|
622
|
+
|
|
623
|
+
if [[ -n "$stash_ref" ]]; then
|
|
624
|
+
if ! git -C "$wt" stash pop "$stash_ref" >/dev/null 2>&1; then
|
|
625
|
+
echo "[codex-agent] Auto-commit retry could not re-apply local changes after sync. Manual resolution required in: $wt" >&2
|
|
626
|
+
return 1
|
|
627
|
+
fi
|
|
628
|
+
fi
|
|
629
|
+
|
|
630
|
+
return 0
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
looks_like_conflict_failure() {
|
|
634
|
+
local output="$1"
|
|
635
|
+
if grep -qiE 'preflight conflict detected|merge conflict detected|auto-sync failed while rebasing|rebase --continue|rebase --abort' <<< "$output"; then
|
|
636
|
+
return 0
|
|
637
|
+
fi
|
|
638
|
+
return 1
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
run_finish_flow() {
|
|
642
|
+
local wt="$1"
|
|
643
|
+
local branch="$2"
|
|
644
|
+
local finish_base_branch=""
|
|
645
|
+
local finish_output=""
|
|
646
|
+
local -a finish_args
|
|
647
|
+
|
|
648
|
+
finish_args=(--branch "$branch")
|
|
649
|
+
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -n "$BASE_BRANCH" ]]; then
|
|
650
|
+
finish_base_branch="$BASE_BRANCH"
|
|
651
|
+
else
|
|
652
|
+
finish_base_branch="$(resolve_worktree_base_branch "$wt")"
|
|
653
|
+
fi
|
|
654
|
+
if [[ -n "$finish_base_branch" ]]; then
|
|
655
|
+
finish_args+=(--base "$finish_base_branch")
|
|
656
|
+
fi
|
|
657
|
+
if [[ "$AUTO_CLEANUP" -eq 1 ]]; then
|
|
658
|
+
finish_args+=(--cleanup)
|
|
659
|
+
fi
|
|
660
|
+
if [[ "$AUTO_WAIT_FOR_MERGE" -eq 1 ]]; then
|
|
661
|
+
finish_args+=(--wait-for-merge)
|
|
662
|
+
fi
|
|
663
|
+
|
|
664
|
+
if has_origin_remote; then
|
|
665
|
+
if ! command -v "${GUARDEX_GH_BIN:-gh}" >/dev/null 2>&1 && ! command -v gh >/dev/null 2>&1; then
|
|
666
|
+
echo "[codex-agent] Auto-finish requires GitHub CLI for PR flow; command not found: ${GUARDEX_GH_BIN:-gh}" >&2
|
|
667
|
+
return 2
|
|
668
|
+
fi
|
|
669
|
+
finish_args+=(--via-pr)
|
|
670
|
+
else
|
|
671
|
+
echo "[codex-agent] No origin remote detected; skipping auto-finish merge/PR pipeline." >&2
|
|
672
|
+
return 2
|
|
673
|
+
fi
|
|
674
|
+
|
|
675
|
+
if finish_output="$(bash "${repo_root}/scripts/agent-branch-finish.sh" "${finish_args[@]}" 2>&1)"; then
|
|
676
|
+
printf '%s\n' "$finish_output"
|
|
677
|
+
return 0
|
|
678
|
+
fi
|
|
679
|
+
|
|
680
|
+
printf '%s\n' "$finish_output" >&2
|
|
681
|
+
|
|
682
|
+
if [[ "$AUTO_REVIEW_ON_CONFLICT" -eq 1 ]] && looks_like_conflict_failure "$finish_output"; then
|
|
683
|
+
echo "[codex-agent] Auto-finish hit conflicts. Launching Codex conflict-review pass in sandbox..." >&2
|
|
684
|
+
local review_prompt
|
|
685
|
+
review_prompt="Resolve git conflicts for branch ${branch} against ${finish_base_branch:-dev}, then commit the resolution in this sandbox worktree and exit."
|
|
686
|
+
|
|
687
|
+
(
|
|
688
|
+
cd "$wt"
|
|
689
|
+
set +e
|
|
690
|
+
"$CODEX_BIN" "$review_prompt"
|
|
691
|
+
review_exit="$?"
|
|
692
|
+
set -e
|
|
693
|
+
if [[ "$review_exit" -ne 0 ]]; then
|
|
694
|
+
echo "[codex-agent] Conflict-review Codex pass exited with status ${review_exit}." >&2
|
|
695
|
+
fi
|
|
270
696
|
)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
697
|
+
|
|
698
|
+
if finish_output="$(bash "${repo_root}/scripts/agent-branch-finish.sh" "${finish_args[@]}" 2>&1)"; then
|
|
699
|
+
printf '%s\n' "$finish_output"
|
|
700
|
+
return 0
|
|
701
|
+
fi
|
|
702
|
+
|
|
703
|
+
printf '%s\n' "$finish_output" >&2
|
|
704
|
+
fi
|
|
705
|
+
|
|
706
|
+
return 1
|
|
274
707
|
}
|
|
275
708
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
resolved_source=""
|
|
279
|
-
role_config_path="${OMX_ROLE_MODEL_CONFIG:-${repo_root}/.agents/role-model-tiers.json}"
|
|
280
|
-
|
|
281
|
-
if [[ -n "${OMX_ROLE_MODEL_OVERRIDE:-}" ]]; then
|
|
282
|
-
resolved_model="$OMX_ROLE_MODEL_OVERRIDE"
|
|
283
|
-
resolved_tier="${ROLE:-n/a}"
|
|
284
|
-
resolved_source="env-override"
|
|
285
|
-
elif [[ -n "$ROLE" ]]; then
|
|
286
|
-
if ! lookup_output="$(resolve_role_model "$ROLE" "$role_config_path")"; then
|
|
287
|
-
exit 2
|
|
288
|
-
fi
|
|
289
|
-
resolved_tier="${lookup_output%%$'\t'*}"
|
|
290
|
-
resolved_model="${lookup_output##*$'\t'}"
|
|
291
|
-
resolved_source="config"
|
|
709
|
+
if ! sync_worktree_with_base "$worktree_path"; then
|
|
710
|
+
exit 1
|
|
292
711
|
fi
|
|
293
712
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
713
|
+
worktree_branch="$(git -C "$worktree_path" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
714
|
+
if [[ -z "$worktree_branch" || "$worktree_branch" == "HEAD" ]]; then
|
|
715
|
+
echo "[codex-agent] Could not determine sandbox branch for worktree: $worktree_path" >&2
|
|
716
|
+
exit 1
|
|
297
717
|
fi
|
|
298
718
|
|
|
299
|
-
|
|
300
|
-
printf '%s\n' "$start_output"
|
|
301
|
-
|
|
302
|
-
worktree_path="$(printf '%s\n' "$start_output" | sed -n 's/^\[agent-branch-start\] Worktree: //p' | tail -n1)"
|
|
303
|
-
if [[ -z "$worktree_path" ]]; then
|
|
304
|
-
echo "[codex-agent] Could not determine sandbox worktree path from agent-branch-start output." >&2
|
|
719
|
+
if ! ensure_openspec_change_workspace "$worktree_path" "$worktree_branch"; then
|
|
305
720
|
exit 1
|
|
306
721
|
fi
|
|
307
722
|
|
|
308
|
-
if
|
|
309
|
-
echo "[codex-agent] Reported worktree path does not exist: $worktree_path" >&2
|
|
723
|
+
if ! ensure_openspec_plan_workspace "$worktree_path" "$worktree_branch"; then
|
|
310
724
|
exit 1
|
|
311
725
|
fi
|
|
312
726
|
|
|
313
|
-
export_worktree_mem0_env "$worktree_path"
|
|
314
|
-
|
|
315
727
|
echo "[codex-agent] Launching ${CODEX_BIN} in sandbox: $worktree_path"
|
|
316
728
|
cd "$worktree_path"
|
|
317
729
|
set +e
|
|
318
|
-
"$CODEX_BIN" "
|
|
730
|
+
"$CODEX_BIN" "$@"
|
|
319
731
|
codex_exit="$?"
|
|
320
732
|
set -e
|
|
321
733
|
|
|
322
734
|
cd "$repo_root"
|
|
323
|
-
|
|
324
735
|
final_exit="$codex_exit"
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if [[
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if [[ -x "${repo_root}/scripts/agent-branch-finish.sh" ]]; then
|
|
335
|
-
if [[ -n "$worktree_branch" && "$worktree_branch" != "HEAD" ]]; then
|
|
336
|
-
finish_args=(--branch "$worktree_branch")
|
|
337
|
-
if [[ -n "$finish_base_branch" ]]; then
|
|
338
|
-
finish_args+=(--base "$finish_base_branch")
|
|
339
|
-
fi
|
|
340
|
-
finish_args+=(--via-pr --wait-for-merge --cleanup)
|
|
341
|
-
if [[ -n "$GH_PR_REF" ]]; then
|
|
342
|
-
finish_args+=(--pr "$GH_PR_REF")
|
|
343
|
-
fi
|
|
344
|
-
if [[ -n "$GH_REPO_REF" ]]; then
|
|
345
|
-
finish_args+=(--repo "$GH_REPO_REF")
|
|
346
|
-
fi
|
|
347
|
-
|
|
348
|
-
echo "[codex-agent] Codex finished successfully. Auto-finishing branch via PR merge + cleanup..."
|
|
349
|
-
if ! bash "${repo_root}/scripts/agent-branch-finish.sh" "${finish_args[@]}"; then
|
|
350
|
-
echo "[codex-agent] Auto-finish failed. Sandbox is kept for manual resolve/retry." >&2
|
|
351
|
-
if [[ -n "$finish_hint" ]]; then
|
|
352
|
-
echo "[codex-agent] Retry with: ${finish_hint}" >&2
|
|
353
|
-
fi
|
|
354
|
-
final_exit=1
|
|
355
|
-
fi
|
|
356
|
-
else
|
|
357
|
-
echo "[codex-agent] Could not determine sandbox branch name; skipping auto-finish." >&2
|
|
358
|
-
final_exit=1
|
|
359
|
-
fi
|
|
736
|
+
auto_finish_completed=0
|
|
737
|
+
|
|
738
|
+
if [[ "$AUTO_FINISH" -eq 1 && -n "$worktree_branch" && "$worktree_branch" != "HEAD" ]]; then
|
|
739
|
+
if [[ "$AUTO_WAIT_FOR_MERGE" -eq 1 && "$AUTO_CLEANUP" -eq 1 ]]; then
|
|
740
|
+
echo "[codex-agent] Auto-finish enabled: commit -> push/PR -> wait for merge -> cleanup."
|
|
741
|
+
elif [[ "$AUTO_WAIT_FOR_MERGE" -eq 1 ]]; then
|
|
742
|
+
echo "[codex-agent] Auto-finish enabled: commit -> push/PR -> wait for merge (keep branch/worktree)."
|
|
743
|
+
elif [[ "$AUTO_CLEANUP" -eq 1 ]]; then
|
|
744
|
+
echo "[codex-agent] Auto-finish enabled: commit -> push/PR -> merge -> cleanup."
|
|
360
745
|
else
|
|
361
|
-
echo "[codex-agent]
|
|
362
|
-
final_exit=1
|
|
746
|
+
echo "[codex-agent] Auto-finish enabled: commit -> push/PR -> merge (keep branch/worktree)."
|
|
363
747
|
fi
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
if [[ -n "$finish_base_branch" ]]; then
|
|
369
|
-
fallback_args+=(--base "$finish_base_branch")
|
|
370
|
-
fi
|
|
371
|
-
if [[ -n "$GH_PR_REF" ]]; then
|
|
372
|
-
fallback_args+=(--pr "$GH_PR_REF")
|
|
373
|
-
fi
|
|
374
|
-
if [[ -n "$GH_REPO_REF" ]]; then
|
|
375
|
-
fallback_args+=(--repo "$GH_REPO_REF")
|
|
376
|
-
fi
|
|
377
|
-
if bash "${repo_root}/scripts/agent-branch-finish.sh" "${fallback_args[@]}"; then
|
|
378
|
-
echo "[codex-agent] Fallback push succeeded: branch pushed + PR created/updated for manual review." >&2
|
|
748
|
+
if auto_commit_worktree_changes "$worktree_path" "$worktree_branch"; then
|
|
749
|
+
if run_finish_flow "$worktree_path" "$worktree_branch"; then
|
|
750
|
+
auto_finish_completed=1
|
|
751
|
+
echo "[codex-agent] Auto-finish completed for '${worktree_branch}'."
|
|
379
752
|
else
|
|
380
|
-
|
|
753
|
+
finish_status="$?"
|
|
754
|
+
if [[ "$finish_status" -eq 2 ]]; then
|
|
755
|
+
echo "[codex-agent] Auto-finish skipped for '${worktree_branch}' (no mergeable remote context)." >&2
|
|
756
|
+
else
|
|
757
|
+
echo "[codex-agent] Auto-finish did not complete; keeping sandbox for manual review: $worktree_path" >&2
|
|
758
|
+
if [[ "$final_exit" -eq 0 ]]; then
|
|
759
|
+
final_exit=1
|
|
760
|
+
fi
|
|
761
|
+
fi
|
|
381
762
|
fi
|
|
382
763
|
else
|
|
383
|
-
|
|
764
|
+
if [[ "$final_exit" -eq 0 ]]; then
|
|
765
|
+
final_exit=1
|
|
766
|
+
fi
|
|
384
767
|
fi
|
|
385
768
|
fi
|
|
386
769
|
|
|
387
770
|
if [[ -x "${repo_root}/scripts/agent-worktree-prune.sh" ]]; then
|
|
388
|
-
echo "[codex-agent] Session ended (exit=${
|
|
771
|
+
echo "[codex-agent] Session ended (exit=${codex_exit}). Running worktree cleanup..."
|
|
389
772
|
prune_args=()
|
|
390
773
|
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 ]]; then
|
|
391
774
|
prune_args+=(--base "$BASE_BRANCH")
|
|
392
775
|
fi
|
|
776
|
+
if [[ "$AUTO_CLEANUP" -eq 1 && "$auto_finish_completed" -eq 1 ]]; then
|
|
777
|
+
prune_args+=(--only-dirty-worktrees --delete-branches --delete-remote-branches)
|
|
778
|
+
fi
|
|
393
779
|
if ! bash "${repo_root}/scripts/agent-worktree-prune.sh" "${prune_args[@]}"; then
|
|
394
780
|
echo "[codex-agent] Warning: automatic worktree cleanup failed." >&2
|
|
395
781
|
fi
|
|
@@ -398,13 +784,15 @@ fi
|
|
|
398
784
|
if [[ ! -d "$worktree_path" ]]; then
|
|
399
785
|
echo "[codex-agent] Auto-cleaned sandbox worktree: $worktree_path"
|
|
400
786
|
else
|
|
787
|
+
worktree_branch="$(git -C "$worktree_path" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
401
788
|
echo "[codex-agent] Sandbox worktree kept: $worktree_path"
|
|
402
789
|
if [[ -n "$worktree_branch" && "$worktree_branch" != "HEAD" ]]; then
|
|
403
|
-
if [[
|
|
404
|
-
|
|
405
|
-
|
|
790
|
+
if [[ "$auto_finish_completed" -eq 1 ]]; then
|
|
791
|
+
echo "[codex-agent] Branch kept intentionally. Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
|
|
792
|
+
else
|
|
793
|
+
echo "[codex-agent] If finished, merge with: bash scripts/agent-branch-finish.sh --branch \"${worktree_branch}\" --base dev --via-pr --wait-for-merge"
|
|
794
|
+
echo "[codex-agent] Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
|
|
406
795
|
fi
|
|
407
|
-
echo "[codex-agent] If finished, merge + clean with: ${finish_hint}"
|
|
408
796
|
fi
|
|
409
797
|
fi
|
|
410
798
|
|