@sabaiway/agent-workflow-kit 1.16.0 → 1.18.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/CHANGELOG.md +65 -0
- package/README.md +2 -1
- package/SKILL.md +37 -7
- package/bridges/codex-cli-bridge/SKILL.md +81 -28
- package/bridges/codex-cli-bridge/bin/codex-exec.sh +337 -44
- package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +593 -0
- package/bridges/codex-cli-bridge/bin/codex-review.sh +279 -19
- package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +542 -0
- package/bridges/codex-cli-bridge/capability.json +1 -1
- package/bridges/codex-cli-bridge/references/driving-codex.md +67 -40
- package/bridges/codex-cli-bridge/references/sandbox-and-flags.md +148 -43
- package/bridges/codex-cli-bridge/setup/README.md +6 -2
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/templates/orchestration.json +8 -3
- package/tools/commands.mjs +7 -0
- package/tools/family-registry.mjs +3 -1
- package/tools/inject-methodology.mjs +90 -16
- package/tools/orchestration-config.mjs +297 -0
- package/tools/orchestration-write.mjs +97 -0
- package/tools/procedures.mjs +17 -102
- package/tools/set-recipe.mjs +217 -0
- package/tools/setup-backends.mjs +71 -5
|
@@ -5,27 +5,78 @@
|
|
|
5
5
|
# decides what to act on; codex never applies them itself.
|
|
6
6
|
#
|
|
7
7
|
# Project-agnostic wrapper for the codex-cli-bridge skill. Codex reads the target
|
|
8
|
-
# project's Hard Constraints
|
|
8
|
+
# project's Hard Constraints from the root AGENTS.md auto-merged into its context.
|
|
9
9
|
#
|
|
10
10
|
# Modes:
|
|
11
11
|
# codex-review plan <plan-file> # critique an implementation plan
|
|
12
12
|
# codex-review code [extra focus...] # review the current working-tree diff
|
|
13
13
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
14
|
+
# For `code`, the wrapper PRECOMPUTES the full change set (repo map, git status,
|
|
15
|
+
# staged + unstaged diffs, untracked file CONTENTS — binaries skipped, symlinks
|
|
16
|
+
# shown as targets, other non-regular paths skipped) and feeds it in, so codex does
|
|
17
|
+
# not burn a run rediscovering it by roaming the filesystem. A clean tree exits 0
|
|
18
|
+
# BEFORE a run is spent; an oversized payload goes via a git-dir-local temp file
|
|
19
|
+
# (never silently truncated). Optionally (CODEX_REVIEW_SCHEMA=1) the findings come
|
|
20
|
+
# back as a validated JSON object, with a raw-text fallback.
|
|
21
|
+
#
|
|
22
|
+
# Auth/policy: subscription-only, identical to codex-exec.sh. Quality-first: the
|
|
23
|
+
# review runs on the frontier model at max effort (advisory findings still bear on
|
|
24
|
+
# what ships, so the same no-downgrade guard applies; CODEX_PROBE=1 relaxes it for
|
|
25
|
+
# a throwaway probe only).
|
|
26
|
+
#
|
|
27
|
+
# Best-effort read hygiene: the review runs under a temporary HOME / XDG_* with an
|
|
28
|
+
# ABSOLUTE CODEX_HOME so codex auth + history still resolve, but its default config
|
|
29
|
+
# / cache / skill-scan roots point at a throwaway dir instead of the real $HOME.
|
|
30
|
+
# This trims roaming noise; it is NOT a security boundary (absolute paths anywhere
|
|
31
|
+
# on disk remain readable under read-only — read-scoping is a prompt concern).
|
|
17
32
|
set -euo pipefail
|
|
18
33
|
|
|
19
|
-
|
|
20
|
-
|
|
34
|
+
DEFAULT_CODEX_MODEL="gpt-5.5"
|
|
35
|
+
DEFAULT_CODEX_EFFORT="xhigh"
|
|
36
|
+
CODEX_MODEL="${CODEX_MODEL:-$DEFAULT_CODEX_MODEL}"
|
|
37
|
+
CODEX_EFFORT="${CODEX_EFFORT:-$DEFAULT_CODEX_EFFORT}"
|
|
38
|
+
# Generous hard cap for a slow xhigh review (subscription latency varies).
|
|
39
|
+
CODEX_HARD_TIMEOUT="${CODEX_HARD_TIMEOUT:-1800}"
|
|
40
|
+
# Above this assembled-payload size (bytes), the diff goes via a git-dir-local temp
|
|
41
|
+
# file instead of inline — never truncated.
|
|
42
|
+
CODEX_REVIEW_MAX_TOTAL_BYTES="${CODEX_REVIEW_MAX_TOTAL_BYTES:-1500000}"
|
|
21
43
|
CHATGPT_LOGIN_GUARD="Logged in using ChatGPT"
|
|
22
44
|
|
|
45
|
+
# --- Quality-first guard: refuse a silent model/effort downgrade ---------------
|
|
46
|
+
if [[ "${CODEX_PROBE:-}" == "1" ]]; then
|
|
47
|
+
echo "warning: CODEX_PROBE=1 — THROWAWAY PROBE MODE. Quality guards relaxed; do NOT use this run's" >&2
|
|
48
|
+
echo " output as a real review (model='$CODEX_MODEL' effort='$CODEX_EFFORT')." >&2
|
|
49
|
+
else
|
|
50
|
+
if [[ "$CODEX_MODEL" != "$DEFAULT_CODEX_MODEL" ]]; then
|
|
51
|
+
echo "error: CODEX_MODEL='$CODEX_MODEL' is not the pinned frontier model '$DEFAULT_CODEX_MODEL'." >&2
|
|
52
|
+
echo " A delegated review must run on the frontier model at max effort (quality-first)." >&2
|
|
53
|
+
echo " For a throwaway probe whose result is effort-independent, set CODEX_PROBE=1." >&2
|
|
54
|
+
exit 2
|
|
55
|
+
fi
|
|
56
|
+
if [[ "$CODEX_EFFORT" != "$DEFAULT_CODEX_EFFORT" ]]; then
|
|
57
|
+
echo "error: CODEX_EFFORT='$CODEX_EFFORT' is not the pinned max effort '$DEFAULT_CODEX_EFFORT'." >&2
|
|
58
|
+
echo " A delegated review must run at max reasoning effort (quality-first)." >&2
|
|
59
|
+
echo " For a throwaway probe whose result is effort-independent, set CODEX_PROBE=1." >&2
|
|
60
|
+
exit 2
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
|
|
23
64
|
# --- Subscription-only guard (see codex-exec.sh) -----------------------------
|
|
24
65
|
unset OPENAI_API_KEY CODEX_API_KEY OPENAI_BASE_URL 2>/dev/null || true
|
|
25
66
|
while IFS= read -r _api_key_var; do
|
|
26
67
|
unset "$_api_key_var" 2>/dev/null || true
|
|
27
68
|
done < <(compgen -v 2>/dev/null | grep '_API_KEY$' || true)
|
|
28
69
|
|
|
70
|
+
# Resolve the real CODEX_HOME (where the cached ChatGPT login lives) to an ABSOLUTE
|
|
71
|
+
# path BEFORE the env fence repoints HOME — auth must keep resolving.
|
|
72
|
+
real_codex_home="${CODEX_HOME:-$HOME/.codex}"
|
|
73
|
+
case "$real_codex_home" in
|
|
74
|
+
"~") real_codex_home="$HOME" ;; # literal tilde
|
|
75
|
+
"~/"*) real_codex_home="$HOME/${real_codex_home#"~/"}" ;; # unexpanded ~/...
|
|
76
|
+
/*) : ;; # already absolute
|
|
77
|
+
*) real_codex_home="$PWD/$real_codex_home" ;; # relative → anchor
|
|
78
|
+
esac
|
|
79
|
+
|
|
29
80
|
# --- Environment preflight (fail fast) ---------------------------------------
|
|
30
81
|
if ! command -v codex >/dev/null 2>&1; then
|
|
31
82
|
echo "error: 'codex' (OpenAI Codex CLI) not found on PATH. See this skill's setup/README.md." >&2
|
|
@@ -44,6 +95,71 @@ if [[ ! -f AGENTS.md ]]; then
|
|
|
44
95
|
exit 2
|
|
45
96
|
fi
|
|
46
97
|
|
|
98
|
+
# Register the cleanup trap up front (before allocating any temp path), so a
|
|
99
|
+
# failure mid-assembly cannot leak the diff temp file, the schema, or the fence.
|
|
100
|
+
fence_home=""
|
|
101
|
+
out=""
|
|
102
|
+
trace=""
|
|
103
|
+
schema_file=""
|
|
104
|
+
review_diff_file=""
|
|
105
|
+
trap 'rm -rf "$fence_home" 2>/dev/null; rm -f "$out" "$trace" "$schema_file" "$review_diff_file" 2>/dev/null; true' EXIT
|
|
106
|
+
|
|
107
|
+
# Output-format instruction — schema-aware so it never contradicts the structured
|
|
108
|
+
# mode. When CODEX_REVIEW_SCHEMA=1 the model must return schema-shaped JSON; else
|
|
109
|
+
# the classic one-finding-per-line text.
|
|
110
|
+
if [[ "${CODEX_REVIEW_SCHEMA:-}" == "1" ]]; then
|
|
111
|
+
_json_fmt="Return your review STRICTLY as a JSON object matching the provided output schema: a \"findings\" array (each item: \"severity\" one of blocker|major|minor|nit, plus \"location\", \"issue\", \"suggested_change\", and optional \"evidence\"), an overall \"verdict\" (ship|revise|rethink), and optional free-text \"notes\"."
|
|
112
|
+
OUTPUT_FORMAT_CODE="$_json_fmt"
|
|
113
|
+
OUTPUT_FORMAT_PLAN="$_json_fmt"
|
|
114
|
+
else
|
|
115
|
+
OUTPUT_FORMAT_CODE="Output findings ONLY, one per line, as: [blocker|major|minor|nit] — file:line — issue — suggested fix. End with a one-line overall verdict (ship / revise / rethink)."
|
|
116
|
+
OUTPUT_FORMAT_PLAN="Output findings ONLY, one per line, as: [blocker|major|minor|nit] — location — issue — suggested change. End with a one-line overall verdict (ship / revise / rethink)."
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# True (exit 0) when $1 looks BINARY: a NUL byte in the first 8 KiB (git's own
|
|
120
|
+
# heuristic). `tr -dc` keeps ONLY NUL bytes, `wc -c` counts them — never captures
|
|
121
|
+
# NUL into a variable. Empty / text files → not binary.
|
|
122
|
+
is_binary() {
|
|
123
|
+
local nul
|
|
124
|
+
nul="$(LC_ALL=C head -c 8192 -- "$1" 2>/dev/null | LC_ALL=C tr -dc '\000' | wc -c)"
|
|
125
|
+
[[ "${nul:-0}" -gt 0 ]]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# Emit the full review surface to stdout: repo map, status, staged + unstaged
|
|
129
|
+
# diffs, and the CONTENTS of every untracked REGULAR file (NUL-safe iteration).
|
|
130
|
+
# Symlinks are shown as their target (never followed — no out-of-repo leak); other
|
|
131
|
+
# non-regular paths (FIFO/socket/device) are skipped (a `cat` on a FIFO would hang
|
|
132
|
+
# BEFORE the codex hard timeout applies).
|
|
133
|
+
assemble_code_diff() {
|
|
134
|
+
echo "=== repo file map (git ls-files) ==="
|
|
135
|
+
git ls-files
|
|
136
|
+
echo
|
|
137
|
+
echo "=== git status (porcelain) ==="
|
|
138
|
+
git status --porcelain=v1
|
|
139
|
+
echo
|
|
140
|
+
echo "=== staged diff (git diff --cached) ==="
|
|
141
|
+
git diff --cached --no-ext-diff
|
|
142
|
+
echo
|
|
143
|
+
echo "=== unstaged diff (git diff) ==="
|
|
144
|
+
git diff --no-ext-diff
|
|
145
|
+
echo
|
|
146
|
+
echo "=== untracked file contents ==="
|
|
147
|
+
local path
|
|
148
|
+
while IFS= read -r -d '' path; do
|
|
149
|
+
if [[ -L "$path" ]]; then
|
|
150
|
+
printf '=== untracked (symlink): %s -> %s ===\n' "$path" "$(readlink -- "$path" 2>/dev/null || echo '?')"
|
|
151
|
+
elif [[ ! -f "$path" ]]; then
|
|
152
|
+
printf '=== untracked (non-regular, skipped): %s ===\n' "$path"
|
|
153
|
+
elif is_binary "$path"; then
|
|
154
|
+
printf '=== untracked (binary, skipped): %s ===\n' "$path"
|
|
155
|
+
else
|
|
156
|
+
printf '=== untracked: %s ===\n' "$path"
|
|
157
|
+
cat -- "$path"
|
|
158
|
+
printf '\n'
|
|
159
|
+
fi
|
|
160
|
+
done < <(git ls-files --others --exclude-standard -z)
|
|
161
|
+
}
|
|
162
|
+
|
|
47
163
|
mode="${1:-}"
|
|
48
164
|
shift || true
|
|
49
165
|
|
|
@@ -59,15 +175,36 @@ case "$mode" in
|
|
|
59
175
|
echo "error: unexpected arguments after plan file: $*" >&2
|
|
60
176
|
exit 2
|
|
61
177
|
fi
|
|
62
|
-
|
|
63
|
-
|
|
178
|
+
fence_plan="Do not read files outside this git working tree; the plan above plus the in-repo code it references are your whole surface."
|
|
179
|
+
directive="You are REVIEWING an implementation plan — ADVISORY ONLY. You are in a read-only sandbox: do NOT edit, create, or delete any file, and do NOT rewrite the plan. Obey the project's Hard Constraints from its root AGENTS.md (already merged into your context). Read the plan below and the relevant repository code it references. ${fence_plan} ${OUTPUT_FORMAT_PLAN} Cover: correctness risks, missing or mis-ordered steps, ambiguities a cold executor would trip on, violated project Hard Constraints, scope creep, and missing verification/gates."
|
|
180
|
+
prompt="${directive}"$'\n\nPLAN:\n'"$(cat -- "$target")"
|
|
64
181
|
;;
|
|
65
182
|
code)
|
|
66
|
-
|
|
67
|
-
if
|
|
68
|
-
|
|
183
|
+
# No-diff preflight — never spend a subscription run on a clean tree.
|
|
184
|
+
if git diff --quiet && git diff --cached --quiet \
|
|
185
|
+
&& [[ -z "$(git ls-files --others --exclude-standard)" ]]; then
|
|
186
|
+
echo "codex-review: no uncommitted changes to review — the working tree is clean." >&2
|
|
187
|
+
exit 0
|
|
188
|
+
fi
|
|
189
|
+
# Assemble DIRECTLY to a git-dir-local temp file (git-invisible + worktree /
|
|
190
|
+
# submodule safe; 600 perms). Measuring size from the file keeps the whole
|
|
191
|
+
# change set off the bash-variable path (no NUL drop, byte-accurate size).
|
|
192
|
+
git_dir="$(git rev-parse --absolute-git-dir 2>/dev/null || git rev-parse --git-dir)"
|
|
193
|
+
review_diff_file="$git_dir/codex-review-diff.$$"
|
|
194
|
+
( umask 077; assemble_code_diff >"$review_diff_file" )
|
|
195
|
+
payload_bytes="$(wc -c <"$review_diff_file")"
|
|
196
|
+
base_directive="You are REVIEWING the uncommitted working-tree changes of a git repository — ADVISORY ONLY. You are in a read-only sandbox: do NOT edit, create, or delete any file and do NOT run any git write command. The COMPLETE change set (repo file map, git status, staged + unstaged diffs, and untracked file contents — binaries noted but skipped, symlinks shown as targets) has been assembled FOR you; you do NOT need to run git yourself, though you MAY read other in-repo files for surrounding context. Obey the project's Hard Constraints from its root AGENTS.md (already merged into your context)."
|
|
197
|
+
if [[ "$payload_bytes" -gt "$CODEX_REVIEW_MAX_TOTAL_BYTES" ]]; then
|
|
198
|
+
fence_code="Do not read files outside this git working tree, with ONE exception — the precomputed-diff file at ${review_diff_file}; read it IN FULL before reviewing. That file plus the in-repo code are your whole surface."
|
|
199
|
+
directive="${base_directive} ${fence_code} ${OUTPUT_FORMAT_CODE}"
|
|
200
|
+
if [[ $# -gt 0 ]]; then directive="${directive} Extra focus: $*"; fi
|
|
201
|
+
prompt="$directive"
|
|
202
|
+
else
|
|
203
|
+
fence_code="Do not read files outside this git working tree; the assembled change set below plus the in-repo code are your whole surface."
|
|
204
|
+
directive="${base_directive} ${fence_code} ${OUTPUT_FORMAT_CODE}"
|
|
205
|
+
if [[ $# -gt 0 ]]; then directive="${directive} Extra focus: $*"; fi
|
|
206
|
+
prompt="${directive}"$'\n\nASSEMBLED CHANGE SET:\n'"$(cat -- "$review_diff_file")"
|
|
69
207
|
fi
|
|
70
|
-
prompt="$directive"
|
|
71
208
|
;;
|
|
72
209
|
*)
|
|
73
210
|
echo "usage: $0 plan <plan-file> | code [extra focus...]" >&2
|
|
@@ -75,10 +212,133 @@ case "$mode" in
|
|
|
75
212
|
;;
|
|
76
213
|
esac
|
|
77
214
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
215
|
+
# --- Best-effort env read-fence ----------------------------------------------
|
|
216
|
+
# A throwaway HOME + XDG roots so codex's default config / cache / skill-scan land
|
|
217
|
+
# in an empty dir (trims the ~MB roaming + skill-scan noise), while an ABSOLUTE
|
|
218
|
+
# CODEX_HOME keeps the subscription login + history reachable (probe-confirmed).
|
|
219
|
+
fence_home="$(mktemp -d)"
|
|
220
|
+
out="$(mktemp)"
|
|
221
|
+
trace="$(mktemp)"
|
|
222
|
+
mkdir -p "$fence_home/.config" "$fence_home/.cache" "$fence_home/.local/share"
|
|
223
|
+
|
|
224
|
+
# --- Optional structured findings (CODEX_REVIEW_SCHEMA=1) ---------------------
|
|
225
|
+
# A FLEXIBLE schema (optional evidence, free-text notes) so codex can almost always
|
|
226
|
+
# conform (a live probe confirmed --output-schema CONSTRAINS the output rather than
|
|
227
|
+
# failing); a raw-text retry covers the rare validation/run failure. Default OFF.
|
|
228
|
+
codex_flags=(codex exec
|
|
229
|
+
--ignore-user-config
|
|
230
|
+
--sandbox read-only
|
|
231
|
+
-c approval_policy="never"
|
|
232
|
+
-c model_reasoning_effort="$CODEX_EFFORT"
|
|
233
|
+
-c hide_agent_reasoning=true
|
|
234
|
+
-c model_reasoning_summary=none
|
|
235
|
+
--color never
|
|
236
|
+
-o "$out"
|
|
237
|
+
--json
|
|
238
|
+
-m "$CODEX_MODEL")
|
|
239
|
+
if [[ "${CODEX_REVIEW_SCHEMA:-}" == "1" ]]; then
|
|
240
|
+
schema_file="$(mktemp)"
|
|
241
|
+
cat >"$schema_file" <<'SCHEMA'
|
|
242
|
+
{
|
|
243
|
+
"type": "object",
|
|
244
|
+
"additionalProperties": false,
|
|
245
|
+
"required": ["findings", "verdict"],
|
|
246
|
+
"properties": {
|
|
247
|
+
"findings": {
|
|
248
|
+
"type": "array",
|
|
249
|
+
"items": {
|
|
250
|
+
"type": "object",
|
|
251
|
+
"additionalProperties": false,
|
|
252
|
+
"required": ["severity", "location", "issue", "suggested_change"],
|
|
253
|
+
"properties": {
|
|
254
|
+
"severity": { "type": "string", "enum": ["blocker", "major", "minor", "nit"] },
|
|
255
|
+
"location": { "type": "string" },
|
|
256
|
+
"issue": { "type": "string" },
|
|
257
|
+
"suggested_change": { "type": "string" },
|
|
258
|
+
"evidence": { "type": "string" }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
"verdict": { "type": "string", "enum": ["ship", "revise", "rethink"] },
|
|
263
|
+
"notes": { "type": "string" }
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
SCHEMA
|
|
267
|
+
codex_flags+=(--output-schema "$schema_file")
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
fence_env=(env
|
|
271
|
+
HOME="$fence_home"
|
|
272
|
+
XDG_CONFIG_HOME="$fence_home/.config"
|
|
273
|
+
XDG_CACHE_HOME="$fence_home/.cache"
|
|
274
|
+
XDG_DATA_HOME="$fence_home/.local/share"
|
|
275
|
+
CODEX_HOME="$real_codex_home")
|
|
276
|
+
|
|
277
|
+
# --- Hard wall-clock cap via timeout(1) (gtimeout on macOS) -------------------
|
|
278
|
+
timeout_bin=""
|
|
279
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
280
|
+
timeout_bin="timeout"
|
|
281
|
+
elif command -v gtimeout >/dev/null 2>&1; then
|
|
282
|
+
timeout_bin="gtimeout"
|
|
283
|
+
fi
|
|
284
|
+
if [[ -z "$timeout_bin" ]]; then
|
|
285
|
+
echo "warning: no 'timeout'/'gtimeout' on PATH — running codex WITHOUT a hard wall-clock cap" >&2
|
|
286
|
+
echo " (install coreutils to enable CODEX_HARD_TIMEOUT=$CODEX_HARD_TIMEOUT)." >&2
|
|
287
|
+
fi
|
|
288
|
+
|
|
289
|
+
# Run codex with the current ${codex_cmd[@]}; capture rc, stream → $trace.
|
|
290
|
+
invoke_codex() {
|
|
291
|
+
set +e
|
|
292
|
+
if [[ -n "$timeout_bin" ]]; then
|
|
293
|
+
printf '%s' "$prompt" \
|
|
294
|
+
| "${fence_env[@]}" "$timeout_bin" --kill-after=15s "$CODEX_HARD_TIMEOUT" "${codex_cmd[@]}" >"$trace" 2>&1
|
|
295
|
+
rc=$?
|
|
296
|
+
else
|
|
297
|
+
printf '%s' "$prompt" \
|
|
298
|
+
| "${fence_env[@]}" "${codex_cmd[@]}" >"$trace" 2>&1
|
|
299
|
+
rc=$?
|
|
300
|
+
fi
|
|
301
|
+
set -e
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
codex_cmd=("${codex_flags[@]}" -)
|
|
305
|
+
invoke_codex
|
|
306
|
+
|
|
307
|
+
# Raw-text fallback: if the structured-findings run failed (not a timeout), retry
|
|
308
|
+
# once WITHOUT the schema rather than lose the review — loud, never silent.
|
|
309
|
+
if [[ -n "$schema_file" && $rc -ne 0 && $rc -ne 124 && $rc -ne 137 ]]; then
|
|
310
|
+
echo "warning: the --output-schema run failed (exit $rc) — retrying once without the schema constraint." >&2
|
|
311
|
+
codex_cmd=()
|
|
312
|
+
for _f in "${codex_flags[@]}"; do
|
|
313
|
+
[[ "$_f" == "--output-schema" || "$_f" == "$schema_file" ]] && continue
|
|
314
|
+
codex_cmd+=("$_f")
|
|
315
|
+
done
|
|
316
|
+
codex_cmd+=(-)
|
|
317
|
+
invoke_codex
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
if [[ $rc -eq 124 || $rc -eq 137 ]]; then
|
|
321
|
+
echo "error: codex review exceeded the hard cap CODEX_HARD_TIMEOUT=${CODEX_HARD_TIMEOUT}s and was terminated." >&2
|
|
322
|
+
echo " Raise CODEX_HARD_TIMEOUT for a known-healthy slow review, or narrow the focus, then retry." >&2
|
|
323
|
+
exit $rc
|
|
324
|
+
fi
|
|
325
|
+
if [[ $rc -ne 0 ]]; then
|
|
326
|
+
echo "error: codex review failed (exit $rc). Last lines of the run trace:" >&2
|
|
327
|
+
tail -n 40 "$trace" >&2
|
|
328
|
+
exit $rc
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
# Surface the session id (stderr only — a review session is one-shot; never write
|
|
332
|
+
# the shared .codex-last-session sidecar, which is codex-exec's resume target).
|
|
333
|
+
session_id="$(grep -m1 '"type":"thread.started"' "$trace" 2>/dev/null \
|
|
334
|
+
| grep -o '"thread_id":"[^"]*"' | cut -d'"' -f4 || true)"
|
|
335
|
+
if [[ -n "$session_id" ]]; then
|
|
336
|
+
echo "session: $session_id" >&2
|
|
337
|
+
fi
|
|
338
|
+
|
|
339
|
+
if [[ -f "$out" && -s "$out" ]]; then
|
|
340
|
+
cat "$out"
|
|
341
|
+
else
|
|
342
|
+
echo "warning: codex produced no final-message file — printing the run-trace tail instead." >&2
|
|
343
|
+
tail -n 40 "$trace"
|
|
344
|
+
fi
|