Local, open-source read-aloud for Codex chats. Append a 1p / 1r line to
inbox/inbox.md and a background watcher speaks it out loud via Microsoft
Edge neural TTS (free, no key, no quota). Toggle it off with one CLI command.
GitHub: otpayt02/a-loud-reader
Reading chat with your eyes is slow. This turns Codex into a podcast. Tweak the voice, the rate, and which turns get archived. Clipboard listening is opt-in (off by default).
cd C:\Users\olive\Projects\a-loud-reader
python -m pip install -r requirements.txtThen either run the script directly:
.\src\loud-reader.ps1 statusOr install the shim on PATH:
.\bin\install_path.cmd # copies loud-reader.cmd to %USERPROFILE%\bin
$env:PATH = "$env:USERPROFILE\bin;$env:PATH"
loud-reader status# 1. turn the watcher on
loud-reader on
loud-reader start # boot the background Python process
# 2. append a turn. Index 1, kind 'p' (prompt) or 'r' (response).
Add-Content inbox\inbox.md "1p Hello from Codex, this is turn one."
# 3. hear it. The watcher reads any new line, then advances its cursor.
# Re-running the same line won't re-speak it (the cursor moved past it).
# 4. when you're done
loud-reader off
loud-reader stopTo speak a specific turn by index, even if the watcher is off:
loud-reader read 1p # speak turn "1p" right now
loud-reader read 12r # speak turn "12r" right nowTo start over from the beginning:
loud-reader reset-cursor| Command | What it does |
|---|---|
loud-reader on |
Master switch on (Codex speaking enabled, clipboard off) |
loud-reader off |
Silent (watcher keeps running, just doesn't speak) |
loud-reader status |
Print flags, cursor, voice, archive state |
loud-reader codex on|off |
Toggle Codex-inbox speaking |
loud-reader clipboard on|off |
Toggle clipboard listening (off by default) |
loud-reader archive on|off |
Toggle MP3 archival (default on; daily reset) |
loud-reader voice en-US-GuyNeural |
Change the Edge voice |
loud-reader rate +10% |
Faster / slower (-5%, +25%, etc.) |
loud-reader read 1p |
Speak one specific turn now |
loud-reader tail |
Live-tail the inbox file |
loud-reader reset-cursor |
Forget progress, re-read from oldest |
loud-reader pin <id> / unpin <id> |
Mark a thread keep-forever |
loud-reader inbox |
Print the inbox path (for Codex to append to) |
loud-reader start / stop |
Boot / kill the Python watcher |
Tell Codex to append every turn to the inbox. Paste this into your
CODEX_INSTRUCTIONS.md (or your project's notes) and Codex will do it
automatically:
# a-loud-reader hook
After writing your final assistant response in any conversation, append a
single line to C:\Users\olive\Projects\a-loud-reader\inbox\inbox.md in the
format `<N>p <body>` for prompts and `<N>r <body>` for responses, where <N>
is the turn number in this conversation. Use the absolute path returned by
`loud-reader inbox` if the repo lives elsewhere.
CODEX_INSTRUCTIONS.md is shipped in this repo and contains that exact text.
a-loud-reader/
+- inbox/
| +- inbox.md # Codex appends <N>p / <N>r lines here
+- data/
| +- clipboard.md # daily log of clipboard captures (rotates at midnight)
+- archive/
| +- YYYY-MM-DD/*.mp3 # spoken turns (one MP3 per turn)
+- state/
| +- flags.json # on/off, voice, archive_mp3, etc.
| +- positions.json # per-thread cursor (next turn to speak)
| +- pinned.json # threads marked keep-forever
| +- watcher.pid # pid of the running Python watcher
| +- watcher.log # stdout/stderr from the watcher
+- src/
| +- say.py # Edge TTS wrapper (single-shot)
| +- watcher.py # inbox + clipboard loop, writes/reads state JSON
| +- loud-reader.ps1 # the operator CLI
+- bin/
| +- loud-reader.cmd # PATH shim
| +- install_path.cmd # copy the shim into %USERPROFILE%\bin
+- scripts/
| +- smoke_test.ps1 # one-shot end-to-end check
+- .gitignore
+- README.md
+- CODEX_INSTRUCTIONS.md
+- requirements.txt
state/flags.json� rebuilt with defaults if missing.state/positions.json� cursor only; deleting it = re-read everything.state/pinned.json� list of threads the user marked keep-forever.state/watcher.pid� only present while the watcher is alive.
MP3s land in archive/YYYY-MM-DD/. Older days are kept unless you delete
them manually. Pinning a thread moves its logs to a archive/pinned/<id>/
folder (TODO; works as a flag now, folder wiring is the next pass).
The clipboard log data/clipboard.md is rotated at local midnight. Pinning
prevents rotation for that thread.
- "watcher not running" but flags say
master=true� runloud-reader start. - No sound but MP3 exists in
archive/� open the MP3 manually; your media player may be muted. - TTS errors with no internet � Edge TTS is online-only. The watcher auto-falls back to Windows SAPI (robotic but always works).
powershell -ExecutionPolicy Bypass -File .\scripts\smoke_test.ps1Should print OK: smoke test produced an MP3 at ...\archive\smoke.mp3.
loud-reader engine edge(default) -- Microsoft Edge neural voices, online, free, no key.loud-reader engine piper-- Piper offline neural TTS, works without internet. Piper binary path is set viaA_LOUD_READER_PIPER(default points at the existing yt_auto install). Voice model viaA_LOUD_READER_MODEL. Both are picked up automatically.
If the active engine fails (e.g. offline + edge), the watcher falls back to Windows SAPI
(robotic, always works) and prints a warning to state/watcher.log.
Lines in inbox.md may include a thread prefix: alpha:1p Hello routes to thread alpha.
Pin a thread so its MP3s land in archive/pinned/<thread>/ (survives daily rotation):
loud-reader pin alpha
loud-reader unpin alpha
alpha: and work-thread: style prefixes are both supported.
See ONBOARDING.md for the full step-by-step. If anything feels off, run scripts\\diagnose.ps1 for an evidence-based health check.
Run loud-reader tray to get a system tray icon. Right-click for On/Off, Pause, Codex/Clipboard/Engine toggles, voice cycle, rate, archive folder, quit.
Every rough prompt you hand Codex can be routed through the local Prompt Refinery first.
loud-reader refine "ship a feature that does X"Returns the canonical execution prompt + critique template + suggested next prompt. The
Refinery logs every interaction under C:\Users\olive\Projects\portfolio_hub\prompt-refinery\conversations\.
Drop the AGENTS.md snippet from this repo into any Codex project so Codex auto-runs the
refinery pass before answering.
Every project should have a one-sentence DoD before code is written. Run loud-reader dod
to scaffold one interactively, or copy scripts\DEFINITION_OF_DONE_TEMPLATE.md into your
project root.