Skip to content

Split run commands into a gh-style options/run seam (stream, transcribe, agent, speak, llm)#100

Merged
alexkroman merged 6 commits into
mainfrom
claude/hopeful-allen-phne89
Jun 12, 2026
Merged

Split run commands into a gh-style options/run seam (stream, transcribe, agent, speak, llm)#100
alexkroman merged 6 commits into
mainfrom
claude/hopeful-allen-phne89

Conversation

@alexkroman

@alexkroman alexkroman commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Separate CLI parsing from execution for all five flag-heavy run commands, following gh CLI's Options + run-function pattern. Each Typer command function now only parses argv into a frozen <Cmd>Options dataclass and hands it to a module-level run_<cmd>(opts, state, *, json_mode) — so tests drive validation, flag mapping, and session wiring by constructing options directly instead of round-tripping argv through CliRunner.

Changes

  • aai_cli/stream_exec.py (new) — StreamOptions + run_stream, plus the --show-code and audio-source dispatch logic (the pilot / reference implementation)
  • aai_cli/transcribe_exec.py — gains TranscribeOptions + run_transcribe alongside the existing run helpers (transcribe_batch is imported lazily there; it imports this module at load time)
  • aai_cli/agent_exec.py (new) — AgentOptions + run_agent, absorbing _resolve_system_prompt / _open_audio; --list-voices keeps its own auth-free body in the command
  • aai_cli/speak_exec.py (new) — SpeakOptions + run_speak with the synthesize/emit helpers and the voice/language defaults
  • aai_cli/llm_exec.py (new) — LlmOptions + run_llm covering one-shot and --follow; --list-models stays in the command (aai_cli/llm.py remains the rich-free gateway client per the import-linter contract)
  • Command modules are now pure flag surface: transcribe 490→404 lines, stream 449→340, speak 267→82, agent 213→113, llm 210→113
  • New exec modules registered in the import-linter "core must not import commands" contract
  • Tests: monkeypatch targets move with the code (aai_cli.commands.<cmd>.*aai_cli.<cmd>_exec.*); new tests/test_stream_exec.py and tests/test_command_options_seam.py drive each run function directly off a defaults Options instance (including agent Ctrl-C/BrokenPipe paths that previously had no coverage)
  • AGENTS.md documents the convention for future commands (small commands keep the inline closure)

Behavior

None changed: flag definitions, help text (snapshots untouched), validation ordering, error messages, and exit codes are byte-identical.

Gate

Full scripts/check.sh green locally on the merged tree, including 100% patch coverage and 70/70 diff-scoped mutants killed. The branch includes a merge of current main (the mutually_exclusive validator consolidation and tightened test fakes), with the new tests from #98 repointed at the exec modules — which fixes the earlier CI failure on the merge ref.

https://claude.ai/code/session_01TevyxEQHkkjBv4wrQKkAQV

The Typer function now only parses argv into a frozen StreamOptions
dataclass and hands it to the module-level run_stream(opts, state,
json_mode) in the new aai_cli/stream_exec.py (mirroring the
transcribe_exec precedent), instead of closing over ~37 flag locals in
a nested body(). Because the run path is a plain function of data,
tests can drive validation, flag mapping, and session wiring by
constructing options directly (tests/test_stream_exec.py) with no
CliRunner argv round-trip.

No behavior change: flag definitions, help text, validation ordering
(sources before credentials), and --show-code output are untouched.
Test monkeypatch targets move with the code
(aai_cli.commands.stream.* -> aai_cli.stream_exec.*), StreamSession /
llm.run_chain patches now point at their home modules, and the
convention is documented in AGENTS.md for future flag-heavy commands.

https://claude.ai/code/session_01TevyxEQHkkjBv4wrQKkAQV
@alexkroman alexkroman enabled auto-merge (squash) June 12, 2026 03:59
alexkroman and others added 4 commits June 11, 2026 21:00
Apply the seam piloted on stream (frozen <Cmd>Options dataclass parsed
by the Typer function, module-level run_<cmd>(opts, state, json_mode)
holding the body) to the remaining flag-heavy commands:

- transcribe -> TranscribeOptions + run_transcribe in transcribe_exec.py
  (the existing home of the command's run helpers; transcribe_batch is
  imported lazily there since it imports this module at load time)
- agent -> agent_exec.py (also absorbs _resolve_system_prompt and
  _open_audio; --list-voices keeps its own auth-free body in the command)
- speak -> speak_exec.py (with the emit/synthesize helpers and the
  DEFAULT_VOICE/DEFAULT_LANGUAGE constants)
- llm -> llm_exec.py (one-shot + --follow; --list-models stays in the
  command; the gateway client aai_cli/llm.py stays rich-free per the
  architecture contract)

The command modules are now pure flag surface (transcribe 490->404
lines, agent 213->113, speak 267->82, llm 210->113). New exec modules
are registered in the import-linter "core must not import commands"
contract. Test monkeypatch targets move with the code; the helpers that
commands still import (commands.llm.gateway, commands.transcribe.llm)
keep their existing targets. tests/test_command_options_seam.py drives
each run function directly off a defaults Options instance, including
the agent Ctrl-C/BrokenPipe paths that previously had no coverage.

No behavior change: flag definitions, help text, validation ordering,
messages, and exit codes are untouched.

https://claude.ai/code/session_01TevyxEQHkkjBv4wrQKkAQV
Resolves the import-block conflict in transcribe_exec.py (union of the
options/run-split imports and main's mutually_exclusive import) and
repoints the patch targets of tests added on main at the exec modules
the run logic moved to (aai_cli.commands.stream.* -> aai_cli.stream_exec.*),
fixing the CI failure on the PR merge ref.

https://claude.ai/code/session_01TevyxEQHkkjBv4wrQKkAQV
@alexkroman alexkroman changed the title Extract stream command execution logic to testable module Split run commands into a gh-style options/run seam (stream, transcribe, agent, speak, llm) Jun 12, 2026
@alexkroman alexkroman merged commit bfb36e9 into main Jun 12, 2026
12 checks passed
@alexkroman alexkroman deleted the claude/hopeful-allen-phne89 branch June 12, 2026 04:16
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.

2 participants