Skip to content

feat(ai): add a say command that speaks any text - #236

Merged
arthware-dev merged 5 commits into
mainfrom
feat/ai-say
Oct 16, 2026
Merged

arthware-dev merged 5 commits into
mainfrom
feat/ai-say

Conversation

@arthware-dev

@arthware-dev arthware-dev commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • ./stack ai say "<text>" speaks any text on this Mac through the ai stacklet's speech server, in the default voice for [ai] language; --voice <name> picks one of the voices in the speech server's voice map. An unknown voice, no text, or a speech server that does not answer is refused with what to do, and nothing plays.
  • The agent can speak on every instance: ai say joins the core API's built-in commands, like the memory lookups, so it needs no [agent] allow entry. ai switch and ai download stay out of its reach. An always-loaded skill tells it to write plain spoken sentences (the speech server reads markdown marks aloud), to read private content aloud only when its owner asks, and to say in chat what it said.
  • The shared TTS helper is split into synthesize and play; ./stack ai hello and ./stack ai intro are unchanged.

Test added 5 commits October 16, 2026 14:58
`./stack ai say "<text>"` speaks the text on this Mac through the ai
stacklet's speech server, in the default voice for `[ai] language`
(the same rule as `./stack ai hello`). `--voice <name>` picks one of
the voices in the speech server's voice map. An unknown voice is
refused with the list of voices, and a speech server that does not
answer is reported with `./stack up ai` as the fix. With `--json` the
result names the voice that spoke.

The shared TTS helper is split into synthesize and play so the
command can tell those failures apart; `speak()` keeps its behaviour
for `./stack ai intro`. `STACK_SPEECH_URL` points the helper at another
speech server, which the tests use for a stand-in.
The driver tests put `tests/driver` on `sys.path` and kept its modules
in `sys.modules` for the rest of the run. Their bare names clash with
stacklet helpers: a later `stack ai say` test imported the driver's
`speech` instead of the ai stacklet's. The test file now removes the
path and forgets the driver modules once it has imported them.
The help line is also what the agent reads for an allowed command, so
it now says how to write the text: plain sentences, no markdown, lists,
links or emoji. The speech server reads markdown marks aloud.
The say command printed "Speaking as <voice>" before the request, so an unreachable speech server showed that line followed by the error.
Speaking is one of the agent's own abilities, like a memory lookup, so
`ai say` joins the core API's built-in commands instead of needing an
`[agent] allow` entry. The rest of the ai stacklet (`ai switch`,
`ai download`) stays out of the agent's reach.

An always-loaded skill tells the agent when to speak and how to write
for the ear: plain sentences, no markdown, private content only when its
owner asks, and a note in chat of what was said.
@arthware-dev
arthware-dev merged commit 4fd7514 into main Oct 16, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant