Codex adapter
May 28, 2026 ยท View on GitHub
Aftertone speaks after each Codex turn via a global user Stop hook in ~/.codex/hooks.json and the shared local tts_daemon install.
Codex hook reference: https://developers.openai.com/codex/hooks
Layout
| Path | Purpose |
|---|---|
~/aftertone (default) | Runtime from scripts/install.sh - ONNX assets, daemon, shared config |
~/.codex/hooks.json | Codex Stop hook that calls Aftertone after each turn |
~/.codex/AGENTS.md | Codex-facing spoken-summary guidance |
~/.agents/skills/aftertone-* | Codex skills for $aftertone-on, $aftertone-off, and $aftertone-status |
~/.codex/prompts/aftertone-*.md | Compatibility prompt docs for on, off, and status |
~/.cursor/hooks/aftertone-codex-speak-on-stop.* | Wrapper script copied by install |
~/aftertone/.cursor/hooks/speak_summary.toml | Shared Aftertone config across adapters |
Install
Run the normal global install:
curl -fsSL https://raw.githubusercontent.com/omarelkhal/aftertone/main/scripts/install.sh | bash -s -- --install-uv --start-daemon
On Windows:
irm https://raw.githubusercontent.com/omarelkhal/aftertone/main/scripts/install.ps1 | iex
Global install registers Cursor, Claude Code, and Codex assets when their config locations are available.
Trust / Review
Codex requires non-managed hooks to be reviewed or trusted. After install, review the hook entry in ~/.codex/hooks.json. It should call an Aftertone wrapper named aftertone-codex-speak-on-stop.
The hook reads Codex Stop JSON on stdin and returns JSON or empty output, as required by Codex.
Daily Use
Start Codex normally:
codex
Enable speech for the current Codex session:
Type $aftertone-on, or open /skills and choose aftertone-on.
The skill runs python -m aftertone on, then replies with a short <spoken_summary> tag. The next Stop hook registers this Codex session id on the allowlist. Other sessions stay silent until enabled there too.
Turn off this session:
Type $aftertone-off, or open /skills and choose aftertone-off.
Check status:
Type $aftertone-status, or open /skills and choose aftertone-status.
Terminal fallback without a Codex turn:
uv run --directory ~/aftertone/py python -m aftertone on
uv run --directory ~/aftertone/py python -m aftertone off
uv run --directory ~/aftertone/py python -m aftertone status
Spoken Text
With default summary_mode = "tag_only" and only_speak_spoken_summary = true, Codex replies must end substantive messages with:
<spoken_summary>
The Codex session is ready to speak after each reply!!
</spoken_summary>
The tag language must match lang in speak_summary.toml. The hook does not translate.
Synthetic Smoke Test
From the install root:
cd "$(cat ~/.cursor/hooks/aftertone-install-dir)"
printf '%s' '{"hook_event_name":"Stop","session_id":"codex-smoke","turn_id":"turn-smoke","model":"gpt-5.1-codex","permission_mode":"default","cwd":"/tmp/aftertone","last_assistant_message":"Smoke test.\n\n<spoken_summary>The Codex Stop hook smoke test reached prepare!!</spoken_summary>"}' \
| AFTERTONE_INSTALL_DIR="$PWD" uv run --directory py python speak_summary_prepare.py
Expected output includes:
{"text":"The Codex Stop hook smoke test reached prepare!!"}
No-tag check:
printf '%s' '{"hook_event_name":"Stop","session_id":"codex-smoke","turn_id":"turn-smoke-2","model":"gpt-5.1-codex","permission_mode":"default","cwd":"/tmp/aftertone","last_assistant_message":"Smoke test without a spoken tag."}' \
| AFTERTONE_INSTALL_DIR="$PWD" uv run --directory py python speak_summary_prepare.py
Expected output:
{}
Real Codex Verification
-
Start or check the daemon:
uv run --directory ~/aftertone/py python -m aftertone status -
Enable this session:
Type
$aftertone-on, or open/skillsand chooseaftertone-on. -
In Codex, ask for a tiny response that ends with:
<spoken_summary> Codex and Aftertone are connected for this session!! </spoken_summary> -
Confirm the session id was registered:
uv run --directory ~/aftertone/py python -m aftertone session list
Troubleshooting
- Nothing speaks: run
uv run --directory ~/aftertone/py python -m aftertone doctor, then inspect.cursor/hooks/state/speak_summary-hook.log. - Hook not running: review or trust the
~/.codex/hooks.jsonentry. Codex project hooks may require trust per the Codex hooks docs. - No tag, no speech: default mode is tag-only.
- Wrong language: write the tag in the TOML
lang; run/aftertone-langorsync_spoken_rule_lang.pyafter language changes. - MCP is optional control only. Post-reply speech comes from the Codex
Stophook.
Status
Codex global Stop hooks, guidance, skills, and compatibility prompt docs ship with global install. MCP control snippets remain optional follow-up work.