Your coding agents,
out loud.
Pintail turns Claude Code replies into short spoken summaries on your Mac. Keep your train of thought. Hear the useful part.
Planned: €15 once · 100 free read-outs · the voice runs on your own OpenRouter credits
› fix the empty state on the review screen
● Read screens/Review.tsx
● Edit components/EmptyState.tsx
● Run pnpm test ✓ 14 passed
The empty state now shows when a filter returns nothing. I moved the copy into EmptyState, added a Clear filters button and covered both cases in Review.test.tsx.
The empty state is fixed and all 14 tests pass.
Six tabs. A little less chaos.
I run five or six agent sessions at once, and they finish whenever they like. When a reply lands, Pintail says the short version and which project it came from, one at a time. A pill at the top of your screen shows the words as they’re said, and a little pet on your desktop shows who’s talking. Reading them automatically is optional.
Try it: switch tabs, then click the pet to hear the tab you’re in.
Illustration: a MacBook with iTerm2 and six Claude Code sessions. Three finish while you work in review-app, and Pintail reads each reply out in the order it came in. Try it: switch tabs, then use the pet button to hear the tab you’re in.
> the review screen is blank when a filter returns nothing, why?
Read(screens/Review.tsx)
Read 186 lines
Read(hooks/useReviews.ts)
Read 44 lines
The list renders nothing when items is empty, and there’s no fallback. useReviews also returns an empty array while it’s loading, so the screen can’t tell “loading” from “no results”.
I’d have useReviews return a status (loading, empty, ready) and show a proper empty state for empty. Want me to do that?
> yes
Update(hooks/useReviews.ts)
Updated hooks/useReviews.ts with 9 additions and 4 removals
Update(screens/Review.tsx)
Updated screens/Review.tsx with 21 additions
Bash(pnpm test)
Tests: 198 passed, 198 total
Done. useReviews returns a status now, and the review screen shows “No reviews match this filter” with a Clear filters button when it’s empty. Loading still shows the skeleton. Tests pass.
> add empty states to every list screen, and make them match the review one
Search(pattern: "<List", path: "screens")
Found 7 files
Write(components/EmptyState.tsx)
Wrote 48 lines to components/EmptyState.tsx
Update(screens/Inbox.tsx)
Updated screens/Inbox.tsx with 12 additions and 3 removals
Update(screens/Projects.tsx)
Updated screens/Projects.tsx with 10 additions
Bash(pnpm test)
Tests: 212 passed, 212 total
Every list screen now has an empty state, and they all use the same component. Here’s what changed, screen by screen:
- Review: I moved its empty state into components/EmptyState.tsx and made the title, text and button props.
- Inbox: “Nothing new” with a button to the archive, and its own message when a search finds nothing.
- Archive: “Nothing archived yet”. No button, there’s nothing useful to do from there.
- Projects: a Create project button, the same one as in the header, so there’s one clear next step.
- Team: explains that invites go out by email and links to the invite form.
- Tags: “No tags yet” and one line on what tags are for.
- Search: repeats the query and offers Clear search.
Two screens showed a header with nothing under it while loading, which looked broken. They now show the loading skeleton until the first fetch finishes.
I added a test for each empty state and one for the search case, 14 in total. The whole suite passes.
Two things to check: the wording is a first draft, and the Team link goes to /settings/invites, which only admins can open.
>
? for shortcuts
Less reading. Same conversation.
Agents write a lot. Pintail reads the reply and says the short version. Pick how much you hear, from one sentence to the whole reply. The full text stays in your terminal either way.
› why do people get logged out after a deploy?
I looked at the auth flow end to end. Here is what happens. The session cookie is signed with SESSION_SECRET, and the deploy script generates a new secret on every release. Every cookie signed with the old secret fails verification, so the middleware treats the user as logged out and clears it. It only shows up after deploys because that is the only time the secret changes. Local dev never rotates it. I moved the secret into the environment config so it stays the same across releases, added a fallback that accepts the previous secret for 24 hours, and wrote two tests: one for a cookie signed with the old secret, one for the new one. Both pass, along with the existing 212 tests. You may want to set SESSION_SECRET in production before the next deploy.
Fixed, just set SESSION_SECRET before the next deploy.
Deploys changed the session secret. Fixed. Set SESSION_SECRET before the next deploy.
Every deploy made a new session secret, so people got logged out. Local dev never rotates it, which is why you only saw it after deploys. The secret now stays the same across releases, and the old one still works for a day. Two new tests cover it, and all 214 pass. Set SESSION_SECRET in production before the next deploy.
I looked at the auth flow end to end. Here is what happens. The session cookie is signed with SESSION_SECRET, and the …
Set it up once. Then listen.
1 Connect your coding agent.
Pintail picks up replies from your terminal sessions through hooks. Codex asks you to trust them once.
2 Choose your voice setup.
Connect your own OpenRouter account. The voice runs on your credits; Pintail doesn’t sell any.
3 Ask, then listen.
Hear a short summary with the project’s name and live captions. Automatic reading is optional.
Pick your companion.
Four pets, one job: showing who’s talking. Click one to hear it. The rest of the page follows your pick.
One price, once.
Pintail isn’t on sale yet. This is the plan.
Planned
€15 once
- 100 free read-outs to try it first
- No subscription. No account.
- For Macs with Apple silicon
Not included: OpenRouter usage. The voice runs on your own OpenRouter account and credits.
Fair questions.
Which coding agents does it work with?
Claude Code in the terminal. Codex support is built; I’m still checking it in real use before I promise it.
Is it a dictation app?
No, it’s the other half. You dictate with whatever you like (I use FluidVoice, and Claude Code has /voice). Pintail speaks the answer back.
Does it use my Claude tokens?
No. The summary is written outside the agent, and the voice commands you type in the prompt (like “voice read”) are caught by the hook and never reach the agent.
Do I need an OpenRouter account?
Yes. The voice runs on your own OpenRouter account. Pintail doesn’t sell credits, so what you pay OpenRouter depends on how much you listen.
What does the voice cost?
I measured it in September 2026: about 1.5 to 3 cents per 1,000 spoken characters, depending on the voice. A spoken summary is a few hundred characters, so usually half a cent to 2 cents a reply. OpenRouter also has free voices with daily limits. Check OpenRouter for current prices.
Can it run without the cloud?
Partly. The summary can be written on your Mac with Apple Intelligence on macOS 26. The voice needs OpenRouter today.
What gets sent where?
To speak, Pintail sends the text it reads to OpenRouter, through your account. The short version can be written on your Mac with Apple Intelligence. If you pick a cloud summarizer instead, the reply goes to OpenRouter too. Pintail has no server of its own.
Does it read every reply out loud?
Only if you want it to. Automatic reading is off by default; you can turn it on, or play a reply when you’re ready.
Which languages?
English and German.
What will it cost?
The plan is €15 once, with 100 free read-outs to try it first. No subscription. OpenRouter usage is separate.
Which Macs?
Macs with Apple silicon.
Why I built it.
I’m Kevin. I tend to have five or six Claude Code tabs going at once. I already dictate my prompts with FluidVoice. Then the agent replies with a wall of text, and I’m back to reading, and trying to remember which tab I meant to check.
I built Pintail for the other half of that conversation: hearing the answer. A short summary, the project name, and a small companion on my Mac. The duck was not in the original requirements. The duck stayed.