Skip to content

feat: transcription history + output polish - #20

Merged
mariuspruvot merged 17 commits into
mainfrom
feat/history-and-polish
May 28, 2026
Merged

feat: transcription history + output polish#20
mariuspruvot merged 17 commits into
mainfrom
feat/history-and-polish

Conversation

@mariuspruvot

Copy link
Copy Markdown
Owner

Summary

  • Single morphing spinner during recording → transcribing → formatting (fixes the duplicate "Recording…" line and the verbose multi-line output).
  • New shh history interactive picker (prompt_toolkit) with substring filter and preview; shh history clear to purge.
  • TTY-aware UI selection: new PipeUI for shh | … and redirects (raw text only, no spinner, no colors); RichUI / QuietUI selection unchanged for TTY.
  • --no-history flag on the record path to skip persisting a single transcription.

What changed

  • Service layer: RecordingService now takes ui + history_store as mandatory deps, emits processing steps, gates the Formatting step on real LLM work, persists a HistoryEntry on success (logs + continues on OSError).
  • Adapters: new HistoryStore (JSONL, size-bounded rotation, default retention 200). Whisper client switched to verbose_json to expose detected_lang.
  • CLI: new shh history Typer subcommand group; _select_ui helper picks Pipe/Quiet/Rich based on sys.stdout.isatty() + flags + config.
  • Config: history_enabled (bool, default true) and history_retention (int, 1–10_000, default 200) exposed through shh config set/get/show.
  • RichUI refactor: single Live(transient=True) morphs through phases, drops the result Panel; final output is text + dim ✓ copied to clipboard.
  • Recording loop fix: stdin in cbreak mode while waiting for Enter, so the echoed newline no longer breaks Rich Live's cursor anchor.

Test plan

  • uv run poe check — 95 tests pass, mypy strict + ruff clean
  • Manual smoke (recorded, shh history, shh history clear) — single morphing spinner, panel removed, clipboard confirmation, picker copies on Enter
  • shh | cat and shh > out.txt (raw text only) — to verify on a fresh checkout
  • --no-history skips the entry — to verify on a fresh checkout

Replace unconditional RichUI with _select_ui helper (PipeUI when not a
TTY, QuietUI on --quiet/quiet_mode, RichUI otherwise). Add --no-history
flag threaded through to RecordingService.transcribe_and_format.
CLAUDE.md updates:
- Tech stack: add prompt_toolkit (transitive via ipython).
- Architecture diagram: surface PipeUI and the history picker.
- Directory tree: adapters/history/, commands/history.py, the three
  UI implementations.
- Config JSON: add history_enabled, history_retention.
- UI Output Layer: document the TTY-aware _select_ui priority.
- New section "Transcription History": store, model, persistence
  policy, --no-history, picker, shh history / shh history clear.
- CLI entry point: note Whisper verbose_json detected_lang flow.
The previous _wait_for_enter used sys.stdin.readline in canonical mode,
so the terminal echoed a newline the moment the user pressed Enter to
stop recording. That newline moved the cursor below rich.live.Live's
anchor, breaking transient cleanup — the "Recording …" line stayed
on screen and subsequent processing-step spinners rendered on new
lines instead of morphing in place.

Put stdin in cbreak mode while waiting, read one character at a time,
and return on \n or \r. Original terminal settings are restored in
finally. Non-TTY stdin (tests, pipes) falls back to readline so
existing tests keep passing.
- Track CLAUDE.md (remove from .gitignore) so the doc is shared.
- Rewrite CLAUDE.md to match the current code: services layer,
  Protocol-based UI (RichUI/QuietUI), Textual TUI, --quiet/--verbose,
  4o-mini formatter, untracked TUI note.
- Declare textual>=7.0 as a [tui] optional extra and pull it into [dev]
  (uv.lock updated accordingly) so `pip install shh-cli[tui]` resolves.
The Textual TUI was incomplete: `shh tui` was never wired into the
Typer app, and the recording screen only animated a fake waveform
(no real audio capture or Whisper call - see the TODO at
shh/tui/screens/recording.py finish_recording). Remove rather than
finish, since the CLI covers the same use cases.

- Delete shh/tui/, shh/cli/commands/tui.py, TUI_README.md (all
  untracked, nothing to revert from git history).
- Drop the [tui] optional extra and textual from [dev] in
  pyproject.toml; re-resolve uv.lock (removes textual + linkify-it-py
  + mdit-py-plugins + uc-micro-py).
- Strip TUI sections from CLAUDE.md. Keep the services layer and
  UIOutput Protocol docs - both remain valid CLI-only patterns.
Spec covers two coordinated changes:
- Output polish: single morphing spinner in RichUI, fix duplicate
  "Recording" line, drop the result Panel for a text + dim
  confirmation line, auto-route to a new PipeUI when stdout is not
  a TTY.
- History feature: JSONL store at <config_dir>/history.jsonl with
  rotation, mandatory HistoryStore in RecordingService, a new
  `shh history` interactive picker built on prompt_toolkit (already
  transitive via ipython), `shh history clear`, and a --no-history
  flag on record.

Replay was considered and dropped (lossy chain-of-LLM trade-off was
not worth the added storage complexity).
14 bite-sized tasks with TDD steps, exact code, and commit boundaries.
Covers settings, models, Whisper verbose_json refactor, HistoryStore,
RecordingService refactor (mandatory ui + history_store), PipeUI,
RichUI single-Live refactor, prompt_toolkit picker, history command
group, TTY-aware UI selection, --no-history flag, config exposure,
CLAUDE.md update via revise-claude-md, and final verification.
@helprs-prod

helprs-prod Bot commented May 28, 2026

Copy link
Copy Markdown

helPRs session created for this PR.

Skill: challenge-me | Open session

@mariuspruvot
mariuspruvot merged commit f05af9a into main May 28, 2026
3 checks passed
@mariuspruvot
mariuspruvot deleted the feat/history-and-polish branch May 28, 2026 16:13
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