---
name: gpt-advisor
description: Second-opinion reviewer from Codex/GPT on the CURRENT conversation. Triggers on /gpt-advisor, "ask gpt to review this", "gpt second opinion on my approach", "what would gpt say about this plan". Reads this session's transcript and puts it to a GPT model; use alongside or instead of `advisor`/`/dead-advisor` when you want a different model's critique.
---

# gpt-advisor

audience: AI coding agents first.

Cross-model reviewer. `advisor` and `[[dead-advisor]]` are Claude reviewing Claude; this
is GPT reviewing Claude — a genuinely independent prior, not a fallback. Reconstructs the
conversation from the on-disk transcript, sends it to Codex via `ask-gpt`, prints the critique.

## When to use

Same trigger discipline as `advisor`: before substantive work, when stuck, before declaring
done. Use it INSTEAD of `advisor` when you want a different model's take; use it IN ADDITION
when the decision is expensive to get wrong and two independent priors are worth the tokens.

Not a fallback for a dead `advisor` — that is `[[dead-advisor]]`, which is Claude-family.

## Procedure (main thread — keep it tiny)

1. **Build the MARKER.** VERBATIM distinctive **ASCII** substring of the most recent real
   *user* turn (their words — not a tool result, not yours). No quotes/newlines/unicode:
   they are JSON-escaped on disk and break the literal match. This is what identifies
   your transcript among concurrent sessions — cwd is not, so a session that moved into
   a worktree resolves fine, but a MARKER matching sessions in two projects is REFUSED.
   Trivial or generic last turn → use a longer or earlier distinctive substring.

2. **Build the QUESTION.** Your current turn is NOT yet on disk, so the reviewer cannot see
   the decision you are about to make — which is exactly what it exists to challenge. State
   verbatim and concretely the approach you are about to commit to. Skipping it reviews the
   state BEFORE your decision: useless as a pre-commit check.

3. **Run it — one bash call, foreground:**
   ```
   ~/.claude/skills/gpt-advisor/gpt-advisor.sh "<MARKER>" "<QUESTION> + APPROACH I'M ABOUT TO COMMIT TO: <verbatim>"
   ```
   Stdout is the critique, nothing else. Runs against the owner's ChatGPT session
   (`ask-gpt`), so the model is whatever that session is set to — there is no model flag.
   - `--effort <E>` — `instant|medium|high|xhigh|pro`, default `xhigh`; `pro` = highest tier.
     Any other value is rejected by `ask-gpt` before the run starts.
   - `--full` resends the whole transcript; default sends only turns since your last call.
   - `--budget-tokens N` overrides the 900K attachment cap.
   - Exit 3 → MARKER matched nothing; redo step 1 with a more unique/earlier substring.
   - Any other nonzero → report the stderr. NEVER advise on an unverified transcript.

   The stderr line names the rung the slice landed on and what was elided. Rung 0 =
   nothing removed. Any higher rung → say so when you relay the critique.

4. **Relay the critique.** Give it the weight you give `advisor`: follow it unless a step
   fails empirically or you hold primary-source evidence against a specific claim. Surface
   its verdict in your final response.

## Rules

- **FOREGROUND — this is a pre-commit GATE.** Hold the critique IN HAND before doing the work
  it reviews. NEVER background it, NEVER treat launching it as advice-received.
- NEVER paste transcript content into the QUESTION — the script attaches the transcript itself.
  You pass MARKER + QUESTION only.
- The read cursor advances only on success, so a failed run re-sends the same turns next call
  instead of silently skipping a range nobody reviewed.
- Disagreement between `advisor` and `gpt-advisor` is signal, not noise: surface BOTH verdicts
  to the user and say which constraint you think breaks the tie. NEVER silently pick one.

## Budget (900K tokens, enforced by `transcript-budget.py`)

An over-cap attachment is rejected by the model, so the slice is shrunk before it is
sent and the run FAILS rather than send an over-cap one. Tokens are counted with the
real BPE encoder (`o200k_base`, via `uv run --with tiktoken` when tiktoken is absent),
never estimated from bytes — measured 1.8–2.9 bytes/token, so a byte heuristic either
busts the cap or wastes a third of it.

Ladder — first rung that fits wins:

0. verbatim
1. drop harness plumbing (`hook_success`, `skill_listing`, `ai-title`, …), dedup
   repeated attachments (newest copy kept), strip per-record metadata (`uuid`,
   `sessionId`, `cwd`, …) and opaque `thinking.signature` blobs, clip tool payloads
   to 8000 chars recent / 2000 older
2. clip 2000 / 600
3. clip 600 / 200
4. clip 400 / 120

From rung 2 up, whole records are dropped from the MIDDLE (oldest first, newest always
kept) when that retains ≥40% of records — detail on surviving turns beats uniform
clipping of every turn. Reasoning text and prose are NEVER clipped; only tool results,
tool inputs, and attachment payloads are, always head+tail with an explicit
`...[N chars elided]...` marker, and only when the payload actually exceeds the cap —
a payload under it keeps its JSON structure. The filter targets 99% of the cap, since
the receiving side frames the attachment.

## Why (secondary)

The transcript JSONL is the same ground truth `[[dead-advisor]]` and `[[fix-rot]]` read — full
and un-summarized. Reconstruction is lossy the same way: no system prompt, no injected memory,
trimmed tool output, and your unflushed current turn is absent — hence the QUESTION rule.
