| name | speak-swiftly-codex-hooks |
| description | Use when a user wants Speak Swiftly to read Codex replies aloud automatically through Codex lifecycle hooks, including plugin-managed hook setup, hook command path diagnosis, hook doctor interpretation, duplicate-hook diagnosis, centralized hook logs, final-reply TTS troubleshooting, or validation from another working directory. |
Speak Swiftly Codex Hooks
Use this skill when the task is about Codex lifecycle hooks that send final assistant replies to the local Speak Swiftly service.
Start Here
- Read docs/codex-hooks-tts.md before changing guidance or setup. It is the repo source of truth for the plugin hook, command path behavior, logs, state, and known Codex payload behavior.
- Verify the live service before changing hook config: read
speak-swiftly://overview or run node scripts/codex-hooks-doctor.mjs --repair-plan from the repository root.
- Treat
speak-swiftly@socket as the preferred plugin entry when both Socket and standalone marketplaces are present.
- Keep the legacy
speak-swiftly-server@socket config entry disabled unless the user explicitly asks for legacy-plugin investigation.
- For plugin-managed hooks, confirm both
features.hooks = true and features.plugin_hooks = true in ~/.codex/config.toml; current Codex CLI source gates runnable plugin hooks behind the separate plugin_hooks feature.
- Codex 0.129.0 stores per-hook review decisions under
[hooks.state] in ~/.codex/config.toml; if Codex says hooks need review, use the hooks settings panel to approve the Speak Swiftly Stop command instead of adding a user-level fallback hook.
Setup Model
- Preferred install surface: the Speak Swiftly plugin manifest declares
hooks: "./hooks/hooks.json", and installed plugins can bundle lifecycle config through that manifest.
- Do not copy the repo-local
.codex/hooks.json into ~/.codex/; that command sets CODEX_HOOK_TTS_DATA_DIR for checkout-scoped development logs and state. Do not add a user-level ~/.codex/hooks.json Speak Swiftly hook for normal installs.
- Plugin-managed hook commands must target the installed Socket Codex cache payload path at
~/.codex/plugins/cache/socket/speak-swiftly/12.0.0/hooks/.... Do not keep stale standalone SpeakSwiftlyServer cache commands in the Socket-managed manifest.
- Keep the shipped plugin hook set limited to the
Stop hook. Do not add a PermissionRequest probe unless the release explicitly reintroduces approval-prompt diagnostics.
Doctor Interpretation
- Warn on user-level hooks that point at
.codex/hooks/stop-tts.mjs, include CODEX_HOOK_TTS_DATA_DIR, or duplicate the plugin-managed Stop hook. Treat them as duplicate or legacy repair targets, not fallback hooks.
- Warn on duplicate enabled plugin entries. Keep the canonical Socket entry and disable or remove duplicate standalone or legacy plugin entries after confirmation.
- Warn when the expected
speak-swiftly@socket:hooks/hooks.json:stop:0:0 entry is missing from [hooks.state]; that means the new Codex per-hook review gate probably still needs operator approval.
- Runtime default voice mismatch is not automatically a hook failure. The hook uses the runtime default voice unless
CODEX_HOOK_TTS_PROFILE_NAME is set; when an override is configured, confirm that profile exists in the cached voice inventory.
Validation
- Validate syntax and focused fixtures with
sh scripts/repo-maintenance/validations/25-codex-plugin.sh.
- Run
node scripts/codex-hooks-doctor.mjs --repair-plan and inspect both centralized and repo-local recent stop-hook log summaries.
- To prove all-directory coverage, run a tiny Codex CLI turn from a different working directory and then tail
~/.codex/speak-swiftly-server/hooks/logs/stop-tts.jsonl.
- Treat a queued log entry with the probe
cwd, sessionId, turnId, and request.request_id as the proof that the hook fired and the service accepted the speech request.
- If the installed plugin manifest is correct but no
Stop row appears, check features.plugin_hooks before adding any user-level fallback.
Troubleshooting
- If a direct manual run of the installed plugin's
hooks/stop-tts.mjs queues speech but normal assistant final replies do not add a fresh row to ~/.codex/speak-swiftly-server/hooks/logs/stop-tts.jsonl, inspect whether the plugin-managed commands still include stale direct commands for absent cache paths before blaming the live service.
speech-route-unreachable means the hook could not reach the local HTTP route.
- HTTP
503 from /speech/live means the service is reachable but not ready for speech work yet.
duplicate-turn means shared dedupe blocked a repeated session_id + turn_id; that is expected when more than one matching hook source starts.
structured-assistant-metadata means Codex produced compact UI or automation metadata instead of speakable final prose.
- If the live service itself is unhealthy, switch to
$speak-swiftly-launchagent-setup for service setup or $speak-swiftly-runtime-operator for runtime readiness, queue, playback, or backend state.