Skip to content
Closed
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
28 changes: 23 additions & 5 deletions bin/gstack-codex-probe
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@
# - All functions prefix with _gstack_codex_.
# - No command execution at source time (only function defs).

# --- Own directory (bash or zsh) --------------------------------------------

_gstack_codex_bin_dir() {
# This file is sourced, often from a zsh tool shell (Claude Code on macOS),
# where BASH_SOURCE is empty and "${BASH_SOURCE[0]%/*}/../scripts/x.ts"
# collapses to "/../scripts/x.ts" (Module not found -> CODEX_MODEL invalid).
# zsh records each function's defining file in $functions_source.
local _gstack_codex_src="${BASH_SOURCE[0]:-}"
if [ -z "$_gstack_codex_src" ] && [ -n "${ZSH_VERSION:-}" ]; then
_gstack_codex_src="${functions_source[_gstack_codex_bin_dir]:-}"
fi
if [ -n "$_gstack_codex_src" ]; then
printf '%s\n' "${_gstack_codex_src%/*}"
else
printf '%s\n' "$HOME/.claude/skills/gstack/bin"
fi
}

# --- Auth probe -------------------------------------------------------------

_gstack_codex_auth_probe() {
Expand Down Expand Up @@ -72,9 +90,9 @@ _gstack_codex_select_model() {
return 1
fi
if [ -n "$_explicit" ]; then
_line=$(bun run "${BASH_SOURCE[0]%/*}/../scripts/resolve-codex-generation-model.ts" --runtime "$_kind" --explicit "$_explicit")
_line=$(bun run "$(_gstack_codex_bin_dir)/../scripts/resolve-codex-generation-model.ts" --runtime "$_kind" --explicit "$_explicit")
else
_line=$(bun run "${BASH_SOURCE[0]%/*}/../scripts/resolve-codex-generation-model.ts" --runtime "$_kind")
_line=$(bun run "$(_gstack_codex_bin_dir)/../scripts/resolve-codex-generation-model.ts" --runtime "$_kind")
fi
_rc=$?
_GSTACK_CODEX_SEL="${_line%%$'\t'*}"
Expand Down Expand Up @@ -135,7 +153,7 @@ _gstack_codex_first_use_notice() {
# and how to turn reviews off. Shown once per state root; never blocks and
# never prints a credential, only where it comes from.
local _root _marker _codex_home="${CODEX_HOME:-$HOME/.codex}" _account _provider
_root="$(. "${BASH_SOURCE[0]%/*}/gstack-state-root.sh" 2>/dev/null && gstack_state_root)" || return 0
_root="$(. "$(_gstack_codex_bin_dir)/gstack-state-root.sh" 2>/dev/null && gstack_state_root)" || return 0
[ -n "$_root" ] || return 0
_marker="$_root/.codex-review-notice-shown"
[ -e "$_marker" ] && return 0
Expand Down Expand Up @@ -194,7 +212,7 @@ _gstack_codex_model_probe() {
# State root from the shared twin (sourced in a subshell, so the caller's
# shell is untouched). A missing twin only disables the cache.
local _gstack_home _cache=""
_gstack_home="$(. "${BASH_SOURCE[0]%/*}/gstack-state-root.sh" 2>/dev/null && gstack_state_root)" || _gstack_home=""
_gstack_home="$(. "$(_gstack_codex_bin_dir)/gstack-state-root.sh" 2>/dev/null && gstack_state_root)" || _gstack_home=""
[ -n "$_gstack_home" ] && _cache="$_gstack_home/.codex-model-probe"
# Cache signature (#2787): everything that can change the round trip's
# outcome without the hour passing — the model, the Codex home and its
Expand Down Expand Up @@ -401,7 +419,7 @@ _gstack_codex_log_event() {
local _duration="${2:-0}"
[ "${_TEL:-off}" = "off" ] && return 0
local _root
_root="$(. "${BASH_SOURCE[0]%/*}/gstack-state-root.sh" 2>/dev/null && gstack_state_root)" || return 0
_root="$(. "$(_gstack_codex_bin_dir)/gstack-state-root.sh" 2>/dev/null && gstack_state_root)" || return 0
[ -n "$_root" ] || return 0
mkdir -p "$_root/analytics" 2>/dev/null || return 0
local _ts
Expand Down
20 changes: 20 additions & 0 deletions test/codex-model-probe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,26 @@ describe('codex model probe (#2477)', () => {
}
});

// Claude Code's tool shell is zsh on macOS. There BASH_SOURCE is empty, and a
// "${BASH_SOURCE[0]%/*}/../scripts/…" path collapsed to "/../scripts/…"
// (Module not found → CODEX_MODEL invalid → every outside review skipped).
// The fixture HOME has no gstack install, so the $HOME fallback cannot pass this.
test.skipIf(spawnSync('zsh', ['-c', 'true']).status !== 0)('selection works when the probe is sourced from zsh', () => {
const f = makeFixture();
try {
fs.writeFileSync(path.join(f.codexHome, 'config.toml'), 'model = "gpt-5.6-terra"\n');
const r = spawnSync('zsh', ['-c', `source "${PROBE}"\n_gstack_codex_bin_dir\n_gstack_codex_select_model exec; printf '%s\\n' "$_GSTACK_CODEX_SEL"`], {
encoding: 'utf8',
timeout: 10000,
env: { PATH: `${f.stubDir}:${process.env.PATH ?? ''}`, HOME: f.home, CODEX_HOME: f.codexHome, GSTACK_HOME: f.gstackHome, _TEL: 'off' },
});
expect(r.stderr).not.toContain('Module not found');
expect(r.stdout.trim().split('\n')).toEqual([path.dirname(PROBE), 'gpt-5.6-terra']);
} finally {
fs.rmSync(f.home, { recursive: true, force: true });
}
});

test('#2914: native review probes its own review_model selection', () => {
const f = makeFixture();
try {
Expand Down