Skip to content

feat: SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals - #92

Merged
GregHolmes merged 16 commits into
mainfrom
feat/sdk-7.7.0
Aug 17, 2026
Merged

feat: SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals#92
GregHolmes merged 16 commits into
mainfrom
feat/sdk-7.7.0

Conversation

@GregHolmes

@GregHolmes GregHolmes commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Brings the CLI up to date with Deepgram Python SDK 7.6.0 + 7.7.0 in one release. The API-facing changes are additive and existing v1 behavior remains covered.

SDK and package constraints

  • deepgram-sdk is bounded at >=7.7.0,<8 across the workspace.
  • Internal minimums ensure upgrades install compatible components: core >=0.2.15, Speak >=0.0.4, and Listen >=0.0.14.
  • uv.lock is intentionally gitignored; local uv lock --check resolves 143 packages.
  • The release PR must publish core before Speak, Listen, and the root package.

Speak — Flux TTS streaming controls (7.7.0)

  • New dg speak --speed (0.85–1.15, 0.05 steps) and beta --expressivity (-2..2), forwarded to speak.v2.connect() only when set.
  • Both controls are validated up front and rejected for non-flux-* models.
  • Routing now uses the documented flux- boundary; bare names and typos stay on the v1 pass-through path.
  • send_interrupt() / send_configure() barge-in remain intentionally unexposed because they target live agent pipelines, not one-shot dg speak.

Listen — redact / numerals + Flux STT fix (7.6.0 + 7.7.0)

  • New repeatable dg listen --redact and --numerals, applied to prerecorded and live paths.
  • Flux validates its numbers / aggressive_numbers vocabulary up front; v1 continues to accept categories such as pci and ssn.
  • Flux STT (listen v2) streaming is fixed:
    • v2 no longer receives v1-only params (language, smart_format, punctuate, channels, diarize, interim_results) that caused HTTP 400.
    • TurnInfo events are assembled per turn and finite streams flush the final in-flight turn.
    • Fatal Error frames propagate code + description and exit non-zero.
    • Flux rejects multichannel raw audio before streaming instead of sending stereo bytes as mono.
    • --diarize warns and is cleared consistently from forwarding, UI, and result metadata.
    • Partial/null Flux word timings are backfilled for valid saved captions.
  • Flux file/URL and invalid-redact misuse now produce visible non-zero Click errors.

Docs

  • Added Aura-2 Spanish voice examples and a pointer to dg models.
  • Expressivity's beta/default status is consistent in README, CLI help/examples, client docs, and generated developer guidance.
  • Regenerated package READMEs now match the SDK constraint.

Live e2e safety

  • Six live tests cover Flux TTS controls, Aura Spanish, Flux numerals, Flux redaction, nova-3 v1, and Flux SRT timestamps.
  • An exported API key alone never enables network tests.
  • Live runs require RUN_LIVE_E2E=1 plus either an explicit DEEPGRAM_BASE_URL or RUN_LIVE_E2E_PRODUCTION=1.
  • Seven deterministic tests cover the opt-in gate without making network calls.

Verification

  • Full non-live suite: 1026 passed, 6 skipped.
  • Exported key with live opt-ins unset: 7 passed, 6 skipped, no network calls.
  • make check: Ruff format/check + mypy clean across 115 source files.
  • Generated README check: clean.
  • uv lock --check: 143 packages resolved.
  • Root/core/Speak/Listen wheels and sdists build; all pass Twine checks.
  • Built wheel metadata contains the expected SDK bound and cross-package minimums.

Production behavior was verified during development for Flux TTS controls, Aura Spanish, Flux STT numerals/redaction, nova-3 v1, and Flux SRT timestamps. Version and changelog generation remain owned by release-please.

Bump deepgram-sdk 7.5.0 -> 7.7.0 across the workspace and expose the new
Flux (Speak v2) streaming controls on `dg speak`:

- `--speed` (0.85-1.15, 0.05 steps) and `--expressivity` (-2..2) forward to
  `speak.v2.connect()` via `speak_text_stream()`; only sent when set.
- Both are validated up front and rejected for Aura (v1) models, so misuse
  fails with a clear message instead of a mid-stream server error.
- Update --help, examples, agent_help, the skill-generator snippet, README,
  and add tests (forwarding, Aura rejection, invalid speed/expressivity).
Expose --redact (numbers, aggressive_numbers, or v1 values like pci/ssn)
and --numerals on `dg listen`, applied to both prerecorded and live paths.

Also fixes Flux STT (listen v2) streaming, which was broken since it was
added:

- `_ws_url` sent v1-only params (language, smart_format, punctuate,
  channels, diarize, interim_results) to the v2 endpoint, which rejected
  them with HTTP 400. Build the param set per API version instead.
- The message parser only understood v1 `Results`; Flux emits turn-based
  `TurnInfo` events. Add `_handle_v2_turn` with per-turn state, plus
  `_flush_v2` to emit the final turn when a finite stream closes before an
  `EndOfTurn` (common for files/stdin).

Verified against staging: `--model flux-general-en --numerals` yields
digit transcripts, `--redact numbers` redacts them, and nova-3 (v1) is
unaffected. Adds tests for _ws_url params and v2 turn finalization.
SDK 7.6.0 added Aura-2 Spanish voices (e.g. aura-2-selena-es). These need
no CLI code change — the model is passed through as a string — so just add
an example and a pointer to `dg models` for the current list.
The Flux STT (listen v2) HTTP 400 fix hinges on _ws_url NOT sending
v1-only params (language, smart_format, punctuate, channels, diarize,
interim_results) to the v2 endpoint, but nothing asserted that directly —
a revert would silently rebreak Flux STT. Add explicit v2-omits /
v1-includes assertions.
Add tests/e2e/ that drive command handle() against the real Deepgram API
in-process, covering the release's new functionality end to end:

- speak Flux --speed/--expressivity -> valid WAV
- speak aura-2-selena-es (multilingual) -> MP3 over REST
- listen Flux STT (v2) --numerals -> digits in transcript
- listen Flux STT (v2) --numerals --redact numbers -> redacted
- listen nova-3 (v1) baseline -> guards the v2-fix regression

Gated on DEEPGRAM_API_KEY (skipped otherwise), so they never run in the
CI matrix (no secret) but run locally/manually with a key exported. Set
DEEPGRAM_BASE_URL to target staging. Verified: 5 skip without a key, 5
pass against production.
…(v2)

Address review findings on PR #92:
- listen: emit a stderr note when --diarize is set against a Flux STT
  (v2) model, instead of silently dropping it. v1 paths unaffected.
- disambiguate user-facing copy now that both Flux surfaces exist:
  "Flux TTS" in speak, "Flux STT" in listen (help text, the Aura
  rejection error, and README comments).
- bound the SDK pin to >=7.7.0,<8 across all packages so a future
  breaking major isn't pulled in automatically.

Gate: make check clean; full suite + live e2e (production) green.
Two upfront guards in `listen` so Flux (v2) misuse fails with a clear
message instead of an opaque server HTTP 400:

- Reject `--redact` values outside {numbers, aggressive_numbers} on Flux
  models, mirroring speak's Flux-flag validation.
- Reject Flux model + pre-recorded file/URL (v2 is streaming-only) before
  it routes to /v1/listen and surfaces a wrapped header dump.

Also reformat test_speak_command.py to satisfy ruff format.
The v1 API accepts multiple redact categories (redact=pci&redact=numbers)
but the flag was single-valued. Mark --redact multiple=True and normalise
to a tuple.

- REST path passes a list so Fern's query encoder expands it into repeated
  params (a tuple is left unexpanded).
- WebSocket path uses urlencode(doseq=True) to expand the sequence.
- Flux (v2) validation now checks every supplied value against the
  numbers/aggressive_numbers vocabulary.
The S2 guard now blocks flux model + file before dispatch, so the two
TestFluxModelAutoVersion tests that asserted flux+file reaching
_prerecorded with api_version=2 crashed (mock never called).

Split them to match the new behaviour: flux+file now asserts the
streaming-only error result, and the flux→v2 assertion moves to the mic
streaming path (which is where a Flux model is actually valid).
Rewrite dict() calls as literals (C408) and rename ambiguous `l` loop
variable to `line` (E741). No behaviour change.
…rrors

Review of the Flux STT work surfaced three issues:

- Captions crashed / mis-timed on Flux STT. TurnInfo words carry only
  {word, confidence} (no per-word start/end), so the live cue span fell
  back to 00:00:00 and the end-of-stream batch save raised
  KeyError: 'start'. Key caption timing off the turn's guaranteed
  audio_window_start/end and backfill per-word timings from it so both the
  live cues and captions_from_words() produce valid output.
- Flux STT fatal errors (control frame type "Error") were silently
  dropped, leaving the user with an empty transcript. Surface code +
  description to stderr.
- speak: note that --expressivity is beta and fixed for the connection.

Adds unit coverage for the audio-window caption timing (incl. the batch
save that used to KeyError) and the error-frame surface.
Close the two coverage gaps left from the Flux STT review:

- Unit: assert _ws_mic catches a KeyboardInterrupt at the gather await,
  runs _flush_v2, and returns the in-flight turn's transcript (rather than
  losing the final partial turn on Ctrl-C).
- Live e2e: Flux STT (v2) --srt captions must carry well-formed, non-zero
  timestamps (keyed off TurnInfo.audio_window_*), guarding the caption fix
  and the batch-save path that previously raised KeyError: 'start'.
…n edges)

Fill the unit-coverage gaps the review surfaced in the new Flux/redact logic:

- _prerecorded builds redact as a list + numerals "true" (Fern repeats the
  query param) — previously only the routing-to-_prerecorded was tested.
- _timed_v2_words: synth-on-empty, window-spread, and real-timing passthrough.
- v2 Update interim print, and EndOfTurn with an empty transcript (no-op).

All new logic paths now unit-covered; the only remaining misses in
command.py are pre-existing branches and the real mic/stdin/websocket I/O
loops, which the live e2e suite exercises.

@dg-coreylweathers dg-coreylweathers left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — verified via two independent review passes (details in review notes). All prior findings resolved at 074090d; CI green 38/38; live-verified against production. Three small non-blocking should-fixes noted for follow-up: (1) bare '-m flux' on dg speak exits 0 on failure, (2) invalid --redact on v1 prerecorded is silent + exit 0, (3) Ctrl-C on 'dg listen -' drops the transcript/--save-to output that the mic path preserves. Please confirm publish ordering: core 0.2.15 must land before speak/listen/root.

… on Ctrl-C

Three error-contract fixes surfaced in review:

- speak: the REST (Speak v1) handler returned BaseResult(status="error")
  instead of raising, which the framework prints but exits 0. Now that a
  bare/typo'd `flux` model routes to REST (not the raising v2 path), its
  failure regressed to exit 0. Raise ClickException so it exits non-zero.
- listen: the prerecorded REST handler had the same return-not-raise sink;
  a --redact value the v1 endpoint refuses printed nothing and exited 0.
  Raise instead so the rejection is visible and non-zero.
- listen: `dg listen -` (stdin) dropped the partial transcript and --save-to
  on Ctrl-C because _ws_stdin re-raised the interrupt. Mirror the mic path:
  catch KeyboardInterrupt/CancelledError, drain, and fall through to the
  flush + return so the transcript survives (fatal errors still propagate).

Regression tests: REST API error exits non-zero (speak flux/aura, listen v1);
stdin Ctrl-C preserves transcript and writes --save-to. Full suite 1037 passed.
@GregHolmes
GregHolmes merged commit 50d96cf into main Aug 17, 2026
38 checks passed
@GregHolmes
GregHolmes deleted the feat/sdk-7.7.0 branch August 17, 2026 11:23
GregHolmes added a commit that referenced this pull request Aug 17, 2026
## Problem

`BaseCommand.output_result` reads the output format from the
process-global `deepctl_core.output._output_config` (via
`get_output_format()`), **not** from `config.get()`. But five
`TestBaseCommand.test_output_result_*` JSON tests only stubbed
`config.get.return_value = "json"` — a value the code never reads.

As a result those tests depended on an earlier CLI-invoking test having
left the global on `"json"` (the CLI entrypoint `src/deepctl/main.py`
calls `setup_output()`, which mutates the global and nothing resets it).
They **pass in the full suite by collection-order luck and fail in
isolation**:

```
# on main
$ pytest packages/deepctl-core/tests/unit/test_base.py
5 failed, 83 passed
```

In isolation the format is its default `"default"`, so `output_result`
returns early and `_output_json` / `print_json` is never called → the
assertions fail.

## Fix (both halves)

1. **Tests set what the code reads.** The 5 JSON tests now
`@patch("deepctl_core.output._output_config", {...})` — the same pattern
the yaml/table/csv tests already use — instead of the no-op `config.get`
stub. All 11 format patch sites now spread a shared
`_OUTPUT_CONFIG_DEFAULTS` constant (`{**_OUTPUT_CONFIG_DEFAULTS,
"format": "json"}`) that mirrors the real global's full key set, so the
stand-in can't drift from `_output_config` (previously the dicts omitted
`agentic`).
2. **Stop the leak.** New autouse fixture in
`packages/deepctl-core/tests/conftest.py` restores the pristine
`_output_config` around every core test, so a format set by one test can
no longer bleed into the next.

Half 1 makes the tests correct on their own; half 2 keeps the latent
ordering bug from silently returning.

## Verification

```
# in isolation (was 5 failed)
$ pytest packages/deepctl-core/tests/unit/test_base.py   → 88 passed
# whole core unit dir (was 5 failed)
$ pytest packages/deepctl-core/tests/unit                → 354 passed
# full suite
$ pytest                                                 → 976 passed
```
ruff / format clean (only test files touched). No `src/` changes.

## Note on the diff

Includes ~90 lines of `ruff format` reflow in `TestConfirmPromptGating`
/ `TestIsGuided` (the `with patch(A), patch(B):` blocks became
parenthesized `with ( … ):` form, plus one line-wrap). All
behavior-neutral formatter output, no logic change — called out here so
it doesn't read as unexplained scope.

## Scope

Pre-existing core test hygiene — independent of #92. Reset fixture is
intentionally core-scoped to limit blast radius; a root-level version
(covering cross-package leakage) can follow if wanted.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@github-actions github-actions Bot mentioned this pull request Aug 17, 2026
GregHolmes pushed a commit that referenced this pull request Aug 17, 2026
🤖 I have created a release *beep* *boop*
---

## 📦 deepctl 0.2.27 — what's in this release

Ships the Deepgram Python SDK **7.7.0** upgrade, makes **Flux** the
default text-to-speech model, expands redaction/formatting controls for
speech-to-text, and hardens the MCP proxy against host disconnects. All
feature areas were exercised end-to-end against the live API before
cutting this release.

### ✨ Features

- **`dg speak` now defaults to Flux TTS (`flux-alexis-en`).**
Text-to-speech streams via Flux (Speak v2 WebSocket) by default instead
of Aura 2, with new Flux-only controls: `--speed` (0.85–1.15) and
`--expressivity` (−2…2, beta). Aura voices remain available via `-m
aura-2-*` (REST batch). (#89, #92)
- **`dg listen` redaction & numerals on files *and* live streams.** New
`--redact` and `--numerals`. Flux STT (v2) accepts `--redact numbers` /
`aggressive_numbers`; v1 models also accept `pci`, `ssn`, etc. and are
repeatable. (#92)
- **Flux STT streaming guardrail.** Flux STT is streaming-only — a file
or URL source now fails fast with guidance to use `--mic`, stdin (`-`),
or a v1 model (e.g. `nova-3`) for pre-recorded audio, instead of
erroring opaquely. (#92)
- **Deepgram SDK bumped to 7.7.0.** (#92)

### 🐛 Bug Fixes

- **MCP pipe-safety.** `dg mcp` now swallows broken/closed-pipe errors
during startup notifications and on the error path, so a host disconnect
exits cleanly with no Python traceback. (#88)

### 🔧 Internal

- Test isolation: the output-format global no longer leaks across the
test suite. (#93)

### ⚠️ Upgrade note

`dg speak` output now comes from Flux (`flux-alexis-en`) by default
rather than Aura 2 — expect a different default voice and raw `linear16`
(auto-wrapped to WAV) instead of Aura's MP3 default. To keep the
previous behavior, pass `-m aura-2-asteria-en` (add `--encoding mp3` for
MP3 output).

---

<sub>The machine-generated release manifest below is required by Release
Please and is left unchanged.</sub>


<details><summary>0.2.27</summary>

## [0.2.27](v0.2.26...v0.2.27)
(2026-08-17)


### Features

* SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals
([#92](#92))
([50d96cf](50d96cf))
* **speak:** default to Flux TTS (flux-alexis-en) instead of Aura 2
([#89](#89))
([5a0b698](5a0b698))


### Bug Fixes

* **mcp:** swallow broken/closed-pipe on dg mcp startup notifications
and error path ([#88](#88))
([b24396e](b24396e))
</details>

<details><summary>deepctl-core: 0.2.15</summary>

##
[0.2.15](deepctl-core-v0.2.14...deepctl-core-v0.2.15)
(2026-08-17)


### Features

* SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals
([#92](#92))
([50d96cf](50d96cf))
</details>

<details><summary>deepctl-telemetry: 0.0.6</summary>

##
[0.0.6](deepctl-telemetry-v0.0.5...deepctl-telemetry-v0.0.6)
(2026-08-17)


### Bug Fixes

* **mcp:** swallow broken/closed-pipe on dg mcp startup notifications
and error path ([#88](#88))
([b24396e](b24396e))
</details>

<details><summary>deepctl-cmd-login: 0.1.17</summary>

##
[0.1.17](deepctl-cmd-login-v0.1.16...deepctl-cmd-login-v0.1.17)
(2026-08-17)


### Features

* SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals
([#92](#92))
([50d96cf](50d96cf))
</details>

<details><summary>deepctl-cmd-projects: 0.1.13</summary>

##
[0.1.13](deepctl-cmd-projects-v0.1.12...deepctl-cmd-projects-v0.1.13)
(2026-08-17)


### Features

* SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals
([#92](#92))
([50d96cf](50d96cf))
</details>

<details><summary>deepctl-cmd-usage: 0.1.13</summary>

##
[0.1.13](deepctl-cmd-usage-v0.1.12...deepctl-cmd-usage-v0.1.13)
(2026-08-17)


### Features

* SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals
([#92](#92))
([50d96cf](50d96cf))
</details>

<details><summary>deepctl-cmd-update: 0.2.6</summary>

##
[0.2.6](deepctl-cmd-update-v0.2.5...deepctl-cmd-update-v0.2.6)
(2026-08-17)


### Bug Fixes

* **mcp:** swallow broken/closed-pipe on dg mcp startup notifications
and error path ([#88](#88))
([b24396e](b24396e))
</details>

<details><summary>deepctl-cmd-skills: 0.0.7</summary>

##
[0.0.7](deepctl-cmd-skills-v0.0.6...deepctl-cmd-skills-v0.0.7)
(2026-08-17)


### Bug Fixes

* **mcp:** swallow broken/closed-pipe on dg mcp startup notifications
and error path ([#88](#88))
([b24396e](b24396e))
</details>

<details><summary>deepctl-cmd-speak: 0.0.4</summary>

##
[0.0.4](deepctl-cmd-speak-v0.0.3...deepctl-cmd-speak-v0.0.4)
(2026-08-17)


### Features

* SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals
([#92](#92))
([50d96cf](50d96cf))
* **speak:** default to Flux TTS (flux-alexis-en) instead of Aura 2
([#89](#89))
([5a0b698](5a0b698))
</details>

<details><summary>deepctl-cmd-listen: 0.0.14</summary>

##
[0.0.14](deepctl-cmd-listen-v0.0.13...deepctl-cmd-listen-v0.0.14)
(2026-08-17)


### Features

* SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals
([#92](#92))
([50d96cf](50d96cf))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
GregHolmes added a commit that referenced this pull request Aug 18, 2026
…n 2.5 (#94)

## What broke

The `0.2.27` release tagged successfully, but **`Publish to PyPI` failed
— nothing reached PyPI**. The latest installable version is still
`0.2.26` (`pypi.org/pypi/deepctl/0.2.27/json` → 404).

The pinned `pypa/gh-action-pypi-publish` image runs a `twine check`
before uploading, and it rejected two wheels:

```
Checking dist/deepctl_cmd_plugin-0.1.12-py3-none-any.whl:
  ERROR InvalidDistribution: Invalid distribution metadata:
  '2.5' is not a valid metadata version
```

### Root cause

- 2 of the 31 packages (`deepctl-cmd-plugin`, `deepctl-cmd-skills`)
build with **hatchling**, which now emits `Metadata-Version: 2.5`. The
other 29 use setuptools and emit `2.4`.
- The **`pkginfo` bundled inside the old pinned action image predates
Metadata 2.5**, so its `twine check` errors out — aborting the step
*before any upload happened*.
- `make verify-packages` passed in the `Build` job because CI's own
twine is current. Only the action's bundled copy was stale.
- It surfaced now (and not in `0.2.26`) because **hatchling was upgraded
in the lockfile** after `0.2.26` shipped.

## The fix

Bump the action pin `ed0c539` → `dc37677` (**v1.14.2**), whose twine
understands Metadata 2.5.

**Verified locally** against the `v0.2.27` tree: all **62 artifacts**
(31 wheels + 31 sdists), including both `Metadata-Version: 2.5` wheels,
`PASSED` `twine check` on twine 7.0.0.

## Why this rolls forward to 0.2.28 instead of republishing 0.2.27

`v0.2.27` is tagged with a GitHub Release but has no PyPI artifact, and
it **cannot be re-published**: `release.yml`'s build/test/publish jobs
are gated on release-please's `release_created`, which is only true on
the run that *cuts* a release. Re-running the failed run would also
reuse the old (broken) pin.

So merging this cuts **0.2.28**, which ships **identical code to 0.2.27
plus this pipeline fix**. The follow-up release PR will carry the full
0.2.27 feature set (Flux TTS default, Flux controls, listen
redact/numerals, SDK 7.7.0, MCP pipe-safety) through to PyPI.

`v0.2.27` stays as a tag/GitHub Release with no PyPI artifact — worth a
note on that release so nobody hunts for `pip install deepctl==0.2.27`.

## Follow-ups (not in this PR)

- **Attestations warning:** the publish step warns that `attestations:
true` is ignored because an explicit password is set. Harmless, but
silenceable with `attestations: false` — or resolved properly by moving
to **Trusted Publishing** (the action already prints the enablement
links).
- **Changelog noise:** `#92` was a scope-less squashed `feat:` whose
README/`pyproject` pin edits rippled into
`deepctl-cmd-login`/`projects`/`usage`, giving those packages a "Flux
TTS controls" feature entry they didn't gain. Per-package commit scoping
(or a path filter) would prevent that.
GregHolmes added a commit that referenced this pull request Aug 18, 2026
The ALL COMMANDS section advertised seven invocations that the CLI rejects.
Anyone copying them got an error:

  dg keys create "ci-runner"          -> No such command 'keys create'
  dg projects list                    -> No such command 'projects list'
  dg members list                     -> No such command 'members list'
  dg models list --type stt           -> No such command 'models list'
  dg requests list --limit 20 -o json -> No such command 'requests list'
  dg completion install --shell zsh   -> No such command 'completion install'
  dg api GET /v1/projects             -> Got unexpected extra argument

These commands are flag-based, not subcommand-based. The page was already
internally inconsistent: its FAQ used the correct forms (dg projects --list,
dg keys --create --comment) while the cards used a subcommand style that has
never existed.

Content was also stale after the Flux TTS default (#89) and SDK 7.7.0 (#92):
the page said "Generate speech with Aura voices" with no mention of Flux, and
none of the new flags appeared anywhere.

- speak: document Flux as the default engine plus --speed and --expressivity,
  and keep an explicit Aura card so -m aura-2-* stays discoverable
- listen: add --redact/--numerals and Flux STT streaming (flux-general-en),
  noting it is streaming-only
- hero terminal: a default `dg speak` now reports flux-alexis-en, not
  aura-2-asteria-en
- add the missing commands: profiles, logout, ffprobe, debug browser,
  debug probe (27 cards -> 36)
- llms.txt / llms-full.txt: same Flux corrections, add the new flag rows, and
  fix a model name that does not exist (aura-2-en-us -> real aura-2-* ids)

Every advertised invocation in the cards was validated against the installed
CLI; the shell-composed ones were checked by hand. Build passes and the stale
forms are absent from the built output.
GregHolmes added a commit that referenced this pull request Aug 18, 2026
The ALL COMMANDS section advertised seven invocations that the CLI rejects.
Anyone copying them got an error:

  dg keys create "ci-runner"          -> No such command 'keys create'
  dg projects list                    -> No such command 'projects list'
  dg members list                     -> No such command 'members list'
  dg models list --type stt           -> No such command 'models list'
  dg requests list --limit 20 -o json -> No such command 'requests list'
  dg completion install --shell zsh   -> No such command 'completion install'
  dg api GET /v1/projects             -> Got unexpected extra argument

These commands are flag-based, not subcommand-based. The page was already
internally inconsistent: its FAQ used the correct forms (dg projects --list,
dg keys --create --comment) while the cards used a subcommand style that has
never existed.

Content was also stale after the Flux TTS default (#89) and SDK 7.7.0 (#92):
the page said "Generate speech with Aura voices" with no mention of Flux, and
none of the new flags appeared anywhere.

- speak: document Flux as the default engine plus --speed and --expressivity,
  and keep an explicit Aura card so -m aura-2-* stays discoverable
- listen: add --redact/--numerals and Flux STT streaming (flux-general-en),
  noting it is streaming-only
- hero terminal: a default `dg speak` now reports flux-alexis-en, not
  aura-2-asteria-en
- add the missing commands: profiles, logout, ffprobe, debug browser,
  debug probe (27 cards -> 36)
- llms.txt / llms-full.txt: same Flux corrections, add the new flag rows, and
  fix a model name that does not exist (aura-2-en-us -> real aura-2-* ids)

Every advertised invocation in the cards was validated against the installed
CLI; the shell-composed ones were checked by hand. Build passes and the stale
forms are absent from the built output.
GregHolmes added a commit that referenced this pull request Aug 18, 2026
…o json across account commands (#97)

Split out from #96 deliberately: that PR was a verified output-neutral
toolchain upgrade, this one changes content **and** fixes an output bug.
#95 and #96 are now merged, so this sits on current `main`.

## 1. Seven advertised commands don't work 🔴

The `ALL COMMANDS` cards used a subcommand style the CLI has never had.
Each was verified to fail:

| Page said | CLI response |
|---|---|
| `dg keys create "ci-runner"` | `No such command 'keys create'` |
| `dg projects list` | `No such command 'projects list'` |
| `dg members list` | `No such command 'members list'` |
| `dg models list --type stt` | `No such command 'models list'` |
| `dg requests list --limit 20 -o json` | `No such command 'requests
list'` |
| `dg completion install --shell zsh` | `No such command 'completion
install'` |
| `dg api GET /v1/projects` | `Got unexpected extra argument
(/v1/projects)` |

These commands are **flag-based**. The page was already internally
inconsistent — its FAQ text used the correct forms (`dg projects
--list`, `dg keys --create --comment`, `dg api /v1/projects --jq`) while
the cards used the broken style. Fixed to the verified forms.

## 2. Stale after the Flux changes (#89, #92)

The page said *"Generate speech with Aura voices"* with **zero**
mentions of Flux, even though `dg speak` now defaults to
`flux-alexis-en`. None of the new capabilities appeared anywhere
(`--speed`, `--expressivity`, `--redact`, `--numerals`,
`flux-general-en` all scored 0).

- **speak** — Flux documented as the default engine, new cards for
`--speed` / `--expressivity`, plus an explicit Aura card so `-m
aura-2-*` stays discoverable
- **listen** — new cards for `--redact` / `--numerals` and Flux STT
streaming, noting it is **streaming-only**
- **hero terminal** — a default `dg speak` now reports `flux-alexis-en`,
not `aura-2-asteria-en`

## 3. Missing commands added

`profiles`, `logout`, `ffprobe`, `debug browser`, `debug probe` had no
cards. **27 → 36 cards.**

## 4. llms.txt / llms-full.txt

These feed AI crawlers and had the same staleness, plus **a model name
that doesn't exist** (`aura-2-en-us` — confirmed rejected by the API,
rc=2). Corrected to real ids and added the new flag rows.

## 5. `-o json` now honored across list/detail commands (fixed here)

While verifying, I found `-o json` was **not honored consistently** —
several commands printed a Rich table or human text on stdout, ignoring
the flag and corrupting anything piped into `jq`. This PR fixes it
across `requests`, `read`, `models`, `projects`, `members`, `usage`, and
`billing` (+ `deepctl-core` exports `get_output_format`):

- Human tables/summaries are now gated on `get_output_format() ==
"default"`; for json/yaml/csv the framework serialises the returned
result to stdout.
- All status/progress chrome ("Fetching…", "Analyzing text…", errors)
moved to a stderr `Console`, so stdout stays clean.
- `requests --show` now returns a structured `RequestDetailResult` (the
full request detail) instead of a bare "displayed" message, so json/yaml
output is actually useful.

Verified live against the installed CLI + a real key: `dg -o json models
| jq`, `… projects --list`, `… requests --limit`, and `… read
--sentiment` all emit **pure JSON on stdout** with status text on
stderr.

## Tests

Added output-format gating tests for the three rendering shapes — table
(`requests`, `models`), list (`projects`, new test file), and inline
(`read`): each asserts that json mode writes **nothing** to the stdout
console while still returning a fully-populated result, and that default
mode still renders. Full suite green (`make check` + `uv run pytest`).

## Verification

Every card invocation was run against the installed CLI (`--help`-level
parse plus live runs for `--redact pci --numerals`,
`--speed`/`--expressivity`, and every model id including a
live-synthesis check that `aura-2-en-us` is rejected). Build passes; all
seven stale forms confirmed absent from the built output and the new
content confirmed present (including in the bundled hero-animation JS).
@GregHolmes GregHolmes mentioned this pull request Aug 18, 2026
GregHolmes added a commit that referenced this pull request Aug 19, 2026
…t-code + error-stream correctness (#102)

Blocks #100 — merge this first, then let release-please regenerate the
release PR as **0.3.0**.

## What this fixes

**1. `dg update` on pip silently doesn't deliver the release (root
`pyproject.toml`).**
Root declared floors as low as `>=0.0.1`, and pip's default
`only-if-needed` upgrade strategy leaves any sub-package whose installed
version already satisfies its floor. Measured from published
`deepctl==0.2.26` with the release wheels available: only 4 of 17
released packages upgraded; `dg --version` reported the new number while
the keys fixes — including the `-o json` fix that headlines the release
— never arrived. (uv resolves fresh and is unaffected, so the same `dg
update` lands two users in different states.) Floors now match the
versions being published.

**2. Eight packages can import a symbol their declared core floor
doesn't guarantee (`packages/*/pyproject.toml`).**

`deepctl-cmd-{billing,keys,members,models,projects,read,requests,usage}`
import `get_status_console` (new in core 0.2.16) at module scope while
declaring `deepctl-core>=0.1.10`. PyPI's latest published core is
0.2.14, so `pip install --upgrade deepctl-cmd-keys` alone reproduces a
broken CLI: the command vanishes and the ImportError prints to stdout.
Same class of hand-bump as #92; release-please has no cross-package
dependency automation, so these floors are hand-maintained.

**3. A crash exited 2 — the code reserved for user interrupt
(`src/deepctl/main.py`).**
Per the published contract (0 = success, 1 = error, 2 = user interrupt),
`main()`'s generic exception handler now exits 1. Because `cli()` runs
with `standalone_mode=False`, Click usage errors (bad flag, unknown
command, bare `dg`) propagate to this same handler and move from 2 to 1
as well — consistent with the contract, which reserves 2 for interrupt.
Also repairs three tests whose `patch.object(cli, "__call__", ...)` was
inert (dunder lookup bypasses instance attributes) and adds a
usage-error exit-code test.

**4. …but that alone demoted a real Ctrl-C to 1
(`src/deepctl/main.py`).**
With `standalone_mode=False`, Click catches a `KeyboardInterrupt` raised
during command execution and re-raises it as `click.exceptions.Abort` —
a `RuntimeError` subclass, not a `KeyboardInterrupt`. In this repo the
path is more direct still: `BaseCommand` catches the interrupt itself
and raises `click.Abort()`. So a mid-command Ctrl-C — the common case —
bypassed the `KeyboardInterrupt` handler entirely and landed in the
generic handler that 3 just changed, exiting 1 and printing an empty
`Error: ` (`str(Abort())` is `""`). Before 3 that path exited 2
correctly by accident. `Abort` is now caught alongside
`KeyboardInterrupt`, so user cancellation (Ctrl-C, Ctrl-D at a prompt)
always exits 2, with a test on the `Abort` delivery path.

**5. Root diagnostics printed to stdout, corrupting `-o json`
(`src/deepctl/main.py`).**
`main()`'s module-level console was a plain rich `Console()`, which
writes to stdout, and both of its handlers print through it — so a
crash, bad flag, unknown command, or bare `dg` wrote human-readable
prose to stdout. `dg -o json not-a-command` put `Error: No such command
...` on **stdout** and left stderr empty, so anything piping stdout into
`jq` parsed the error text instead of JSON. This is the root-handler
half of the #97 sweep: that issue's scope covered moving errors to a
stderr console "so stdout stays clean", and #101 closed the sweep, but
both only ever reached the command layer. Now aliases
`deepctl_core.output.stderr_console` — the same console `print_error()`
writes to, and the pattern `deepctl-cmd-mcp` already follows — so
root-level and command-level diagnostics format identically, including
the no-color handling for agentic/CI callers. Exit codes are unchanged
and success paths still write their payload to stdout. Adds tests
asserting a failing `dg -o json ...` writes nothing to stdout
(unknown-command and bad-flag paths) and that the cancellation notice is
on stderr.

## Why the BREAKING CHANGE footer

The exit-code enforcement (#101) landed as `fix:`, so release-please
would ship it as patch 0.2.28 with no version signal — and #100
currently confirms that: root reads `0.2.28` and its diff contains no `⚠
BREAKING CHANGES` section at all. There is no machine-readable breaking
marker anywhere in the cycle; the behavior change exists only as
hand-written prose in `21b8333` / `e327a5e`. The break belongs to #101's
already-merged code, so it cannot come from a conventional-commit type
on this PR's own diff — it has to be injected where the version
arithmetic can see it. With `bump-minor-pre-major`, that makes root
**0.3.0**.

The signal is deliberately stated in three places, because which one
release-please actually reads depends on how this PR is merged:

- **`BREAKING CHANGE:` footer on `914e132`** — the primary. That commit
touches only `src/deepctl/main.py` and its tests, i.e. a root-only path,
so the break is attributed to root alone and the eight sub-packages stay
patch bumps. This is the one that survives a **merge commit**, and it is
the only variant that produces the intended release shape.
- **`fix!:` in the title** — insurance. This repo's recent PRs were
squash-merged, and with `squash_merge_commit_title: PR_TITLE` /
`squash_merge_commit_message: PR_BODY` a squash discards every commit
message, footer included. The `!` keeps root at 0.3.0 in that case.
- **`BREAKING CHANGE:` footer at the foot of this description** — so a
squash also carries the descriptive text into the `⚠ BREAKING CHANGES`
section rather than just the subject line.

Trade-off to know before merging: **prefer a merge commit.** A squash
collapses all five commits into one that touches root *and* the eight
`packages/deepctl-cmd-*/pyproject.toml` files, so the break gets
attributed to those eight paths too — they would take minor bumps (keys
0.0.3 → 0.1.0, usage 0.1.13 → 0.2.0, …) with a breaking-change entry
about CLI exit codes that has nothing to do with them, and root's floors
here (`>=0.0.4`) would then sit below what actually published,
re-seeding the staleness this PR exists to fix. Root reaches 0.3.0
either way; only the sub-package shape differs.

## After merge — steps on the regenerated #100

1. `uv lock` and commit (version bumps stale the lock; CI runs `uv sync
--locked` — this is the `eff5291` wall and recurs every release until
release.yml regenerates the lock itself).
2. Re-apply the behavior-change prose (`git show 21b8333 e327a5e`),
changing `0.2.26 to 0.2.28` → `0.2.26 to 0.3.0`, and fold in the
exit-code and output-stream details from this PR: crashes and usage
errors move 2 → 1, `2` stays reserved for user interrupt (Ctrl-C during
a command still exits 2), and root error/cancellation output moves from
stdout to stderr. Decide there whether the stream move gets its own `⚠
BREAKING CHANGES` line or reads as a plain fix — root lands on 0.3.0
either way, so it is a notes-wording call, not a version call.
3. Verify root reads 0.3.0 across manifest / `pyproject.toml` /
`__init__.py` / `CHANGELOG` heading, and the `⚠ BREAKING CHANGES`
section renders the footer text.

Verification here: full suite **1094 passed / 6 skipped**; `make check`
clean (ruff + mypy, 115 files); `uv lock --check` clean after both floor
commits; live probes — `--version` → 0, bare `dg` → 1, bad flag → 1,
unknown command → 1, mid-command Ctrl-C → 2; `dg -o json not-a-command`
writes **0 bytes** to stdout with the error on stderr, while `dg -o json
models` still emits valid JSON on stdout.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

BREAKING CHANGE: `dg` now exits non-zero when a command fails: 1 for
errors (including crashes and usage errors), 2 for user interrupt, 0 on
success. Every command previously exited 0 regardless of outcome, so
scripts and CI steps that ignored the exit code will surface failures
they
were silently swallowing. No command that succeeds changes its exit
code.
GregHolmes added a commit that referenced this pull request Aug 19, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>0.3.0</summary>

## [0.3.0](v0.2.27...v0.3.0)
(2026-08-19)


### ⚠ BREAKING CHANGES

* `dg` now exits non-zero when a command fails: 1 for errors (including
crashes and usage errors), 2 for user interrupt, 0 on success. Every
command previously exited 0 regardless of outcome, so scripts and CI
steps that ignored the exit code will surface failures they were
silently swallowing. No command that succeeds changes its exit code.

### Bug Fixes

* correct web command examples, document Flux TTS/STT, and honor -o json
across account commands
([#97](#97))
([55984ec](55984ec))
* dependency floors that let dg update skip this release, and exit-code
+ error-stream correctness
([#102](#102))
([fd1e8a4](fd1e8a4))
* **deps:** cap mcp &lt;2 (fixes broken dg mcp), commit uv.lock, require
twine &gt;=7 ([#95](#95))
([997cd36](997cd36))
* **deps:** raise deepctl-core floor to 0.2.16 in the eight packages
that import get_status_console
([98f9e91](98f9e91))
* **deps:** raise root dependency floors to the versions this release
publishes
([c0b0023](c0b0023))
* exit 1, not 2, when a command crashes or is misused
([914e132](914e132))
* keep exit 2 when Ctrl-C interrupts a running command
([b0e80e2](b0e80e2))
* **keys:** honor -o json so stdout stays parseable (completes the
[#97](#97) sweep)
([#101](#101))
([e430a77](e430a77))
* **release:** bump pypi-publish action to v1.14.2 for Metadata-Version
2.5 ([#94](#94))
([582cd83](582cd83))
* send root error and interrupt output to stderr, not stdout
([f4b7c48](f4b7c48))
* **web:** repair broken Heap snippet, upgrade astro 6→7, clear all 20
npm alerts ([#96](#96))
([11928fe](11928fe))


### Behavior changes

Alongside the exit-code change above, upgrading to 0.3.0 changes these:

* The full exit-code contract is now enforced end to end: `0` = success,
`1` = error, `2` = user interrupt. Crashes **and usage errors** (bad
flag, unknown command, bare `dg`) exit `1`; `2` is reserved for
cancellation, so Ctrl-C during a running command and Ctrl-D at a prompt
both still exit `2`.
* Error and cancellation messages are written to **stderr** instead of
stdout. `dg -o json …` therefore keeps stdout machine-readable when a
command fails — previously a failure printed `Error: …` prose to stdout,
so a script piping stdout into `jq` parsed the error text instead of
JSON. Successful commands still write their payload to stdout.
* `-o yaml` and `-o csv` no longer drop square-bracketed text from
values. Output was passed through a renderer that read `[...]` as style
markup and deleted it, so an API key comment of `[ci] runner` was
emitted as `runner`. Long values are also no longer hard-wrapped
mid-field.
* `dg keys --delete KEY_ID` now asks for confirmation on stderr instead
of always reporting `Cancelled by user` without deleting. In a
non-interactive context it exits `1` and tells you to pass `--yes`.
* `dg keys --create --dry-run` now reports what it would create. It
previously failed with an internal `TypeError`.

### Previously unreleased

0.2.27 was tagged on 2026-08-17 but never reached PyPI — its publish
step failed with `InvalidDistribution: Invalid distribution metadata:
'2.5' is not a valid metadata version`, which
[#94](#94) and
[#95](#95) then fixed. PyPI
therefore goes straight from 0.2.26 to 0.3.0, and this release is the
first published build to include the 0.2.27 changes:

* SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals
([#92](#92))
([50d96cf](50d96cf))
* **speak:** default to Flux TTS (`flux-alexis-en`) instead of Aura 2
([#89](#89))
([5a0b698](5a0b698)).
This changes the default model for `dg speak`, so synthesised audio
differs unless you pass an `aura-*` model explicitly.
* **mcp:** swallow broken/closed-pipe on dg mcp startup notifications
and error path ([#88](#88))
([b24396e](b24396e))

Six packages tagged in that cycle also reach PyPI for the first time
here: `deepctl-cmd-listen` 0.0.14, `deepctl-cmd-login` 0.1.17,
`deepctl-cmd-skills` 0.0.7, `deepctl-cmd-speak` 0.0.4,
`deepctl-cmd-update` 0.2.6 and `deepctl-telemetry` 0.0.6.

Because 0.2.27 never published, `dg update` on pip also had to be
repaired for this release to arrive at all: root's inter-package
dependency floors were lower than the versions being published, so pip's
default `only-if-needed` strategy left most sub-packages stale and `dg
--version` reported the new number while the fixes never landed. Floors
now match the published versions exactly.
</details>

<details><summary>deepctl-core: 0.2.16</summary>

##
[0.2.16](deepctl-core-v0.2.15...deepctl-core-v0.2.16)
(2026-08-19)


### Bug Fixes

* correct web command examples, document Flux TTS/STT, and honor -o json
across account commands
([#97](#97))
([55984ec](55984ec))
* **keys:** honor -o json so stdout stays parseable (completes the
[#97](#97) sweep)
([#101](#101))
([e430a77](e430a77))


### Behavior changes

* Commands now map their result status to a process exit code (`error` →
`1`, `cancelled` → `2`, otherwise `0`), and
`BaseCommand.exit_code_for()` exposes that mapping. Exit codes were
previously discarded, so every command exited `0`.
* `-o yaml` and `-o csv` payloads are written verbatim; the renderer no
longer interprets `[...]` as markup or wraps long values.
* New `get_status_console()` returns the shared stderr console for
status output. Commands should use it instead of declaring their own.
Packages that import it require `deepctl-core>=0.2.16`.
</details>

<details><summary>deepctl-cmd-projects: 0.2.0</summary>

##
[0.2.0](deepctl-cmd-projects-v0.1.13...deepctl-cmd-projects-v0.2.0)
(2026-08-19)


### ⚠ BREAKING CHANGES

* `dg` now exits non-zero when a command fails: 1 for errors (including
crashes and usage errors), 2 for user interrupt, 0 on success. Every
command previously exited 0 regardless of outcome, so scripts and CI
steps that ignored the exit code will surface failures they were
silently swallowing. No command that succeeds changes its exit code.

### Bug Fixes

* correct web command examples, document Flux TTS/STT, and honor -o json
across account commands
([#97](#97))
([55984ec](55984ec))
* dependency floors that let dg update skip this release, and exit-code
+ error-stream correctness
([#102](#102))
([fd1e8a4](fd1e8a4))
* **deps:** raise deepctl-core floor to 0.2.16 in the eight packages
that import get_status_console
([98f9e91](98f9e91))
* **keys:** honor -o json so stdout stays parseable (completes the
[#97](#97) sweep)
([#101](#101))
([e430a77](e430a77))
</details>

<details><summary>deepctl-cmd-usage: 0.2.0</summary>

##
[0.2.0](deepctl-cmd-usage-v0.1.13...deepctl-cmd-usage-v0.2.0)
(2026-08-19)


### ⚠ BREAKING CHANGES

* `dg` now exits non-zero when a command fails: 1 for errors (including
crashes and usage errors), 2 for user interrupt, 0 on success. Every
command previously exited 0 regardless of outcome, so scripts and CI
steps that ignored the exit code will surface failures they were
silently swallowing. No command that succeeds changes its exit code.

### Bug Fixes

* correct web command examples, document Flux TTS/STT, and honor -o json
across account commands
([#97](#97))
([55984ec](55984ec))
* dependency floors that let dg update skip this release, and exit-code
+ error-stream correctness
([#102](#102))
([fd1e8a4](fd1e8a4))
* **deps:** raise deepctl-core floor to 0.2.16 in the eight packages
that import get_status_console
([98f9e91](98f9e91))
* **keys:** honor -o json so stdout stays parseable (completes the
[#97](#97) sweep)
([#101](#101))
([e430a77](e430a77))
</details>

<details><summary>deepctl-cmd-mcp: 0.1.15</summary>

##
[0.1.15](deepctl-cmd-mcp-v0.1.14...deepctl-cmd-mcp-v0.1.15)
(2026-08-19)


### Bug Fixes

* **deps:** cap mcp &lt;2 (fixes broken dg mcp), commit uv.lock, require
twine &gt;=7 ([#95](#95))
([997cd36](997cd36))
</details>

<details><summary>deepctl-cmd-models: 0.1.0</summary>

##
[0.1.0](deepctl-cmd-models-v0.0.2...deepctl-cmd-models-v0.1.0)
(2026-08-19)


### ⚠ BREAKING CHANGES

* `dg` now exits non-zero when a command fails: 1 for errors (including
crashes and usage errors), 2 for user interrupt, 0 on success. Every
command previously exited 0 regardless of outcome, so scripts and CI
steps that ignored the exit code will surface failures they were
silently swallowing. No command that succeeds changes its exit code.

### Bug Fixes

* correct web command examples, document Flux TTS/STT, and honor -o json
across account commands
([#97](#97))
([55984ec](55984ec))
* dependency floors that let dg update skip this release, and exit-code
+ error-stream correctness
([#102](#102))
([fd1e8a4](fd1e8a4))
* **deps:** raise deepctl-core floor to 0.2.16 in the eight packages
that import get_status_console
([98f9e91](98f9e91))
* **keys:** honor -o json so stdout stays parseable (completes the
[#97](#97) sweep)
([#101](#101))
([e430a77](e430a77))
</details>

<details><summary>deepctl-cmd-keys: 0.1.0</summary>

##
[0.1.0](deepctl-cmd-keys-v0.0.3...deepctl-cmd-keys-v0.1.0)
(2026-08-19)


### ⚠ BREAKING CHANGES

* `dg` now exits non-zero when a command fails: 1 for errors (including
crashes and usage errors), 2 for user interrupt, 0 on success. Every
command previously exited 0 regardless of outcome, so scripts and CI
steps that ignored the exit code will surface failures they were
silently swallowing. No command that succeeds changes its exit code.

### Bug Fixes

* dependency floors that let dg update skip this release, and exit-code
+ error-stream correctness
([#102](#102))
([fd1e8a4](fd1e8a4))
* **deps:** raise deepctl-core floor to 0.2.16 in the eight packages
that import get_status_console
([98f9e91](98f9e91))
* **keys:** honor -o json so stdout stays parseable (completes the
[#97](#97) sweep)
([#101](#101))
([e430a77](e430a77))
</details>

<details><summary>deepctl-cmd-read: 0.1.0</summary>

##
[0.1.0](deepctl-cmd-read-v0.0.2...deepctl-cmd-read-v0.1.0)
(2026-08-19)


### ⚠ BREAKING CHANGES

* `dg` now exits non-zero when a command fails: 1 for errors (including
crashes and usage errors), 2 for user interrupt, 0 on success. Every
command previously exited 0 regardless of outcome, so scripts and CI
steps that ignored the exit code will surface failures they were
silently swallowing. No command that succeeds changes its exit code.

### Bug Fixes

* correct web command examples, document Flux TTS/STT, and honor -o json
across account commands
([#97](#97))
([55984ec](55984ec))
* dependency floors that let dg update skip this release, and exit-code
+ error-stream correctness
([#102](#102))
([fd1e8a4](fd1e8a4))
* **deps:** raise deepctl-core floor to 0.2.16 in the eight packages
that import get_status_console
([98f9e91](98f9e91))
* **keys:** honor -o json so stdout stays parseable (completes the
[#97](#97) sweep)
([#101](#101))
([e430a77](e430a77))
</details>

<details><summary>deepctl-cmd-requests: 0.1.0</summary>

##
[0.1.0](deepctl-cmd-requests-v0.0.2...deepctl-cmd-requests-v0.1.0)
(2026-08-19)


### ⚠ BREAKING CHANGES

* `dg` now exits non-zero when a command fails: 1 for errors (including
crashes and usage errors), 2 for user interrupt, 0 on success. Every
command previously exited 0 regardless of outcome, so scripts and CI
steps that ignored the exit code will surface failures they were
silently swallowing. No command that succeeds changes its exit code.

### Bug Fixes

* correct web command examples, document Flux TTS/STT, and honor -o json
across account commands
([#97](#97))
([55984ec](55984ec))
* dependency floors that let dg update skip this release, and exit-code
+ error-stream correctness
([#102](#102))
([fd1e8a4](fd1e8a4))
* **deps:** raise deepctl-core floor to 0.2.16 in the eight packages
that import get_status_console
([98f9e91](98f9e91))
* **keys:** honor -o json so stdout stays parseable (completes the
[#97](#97) sweep)
([#101](#101))
([e430a77](e430a77))
</details>

<details><summary>deepctl-cmd-billing: 0.1.0</summary>

##
[0.1.0](deepctl-cmd-billing-v0.0.2...deepctl-cmd-billing-v0.1.0)
(2026-08-19)


### ⚠ BREAKING CHANGES

* `dg` now exits non-zero when a command fails: 1 for errors (including
crashes and usage errors), 2 for user interrupt, 0 on success. Every
command previously exited 0 regardless of outcome, so scripts and CI
steps that ignored the exit code will surface failures they were
silently swallowing. No command that succeeds changes its exit code.

### Bug Fixes

* correct web command examples, document Flux TTS/STT, and honor -o json
across account commands
([#97](#97))
([55984ec](55984ec))
* dependency floors that let dg update skip this release, and exit-code
+ error-stream correctness
([#102](#102))
([fd1e8a4](fd1e8a4))
* **deps:** raise deepctl-core floor to 0.2.16 in the eight packages
that import get_status_console
([98f9e91](98f9e91))
* **keys:** honor -o json so stdout stays parseable (completes the
[#97](#97) sweep)
([#101](#101))
([e430a77](e430a77))
</details>

<details><summary>deepctl-cmd-members: 0.1.0</summary>

##
[0.1.0](deepctl-cmd-members-v0.0.3...deepctl-cmd-members-v0.1.0)
(2026-08-19)


### ⚠ BREAKING CHANGES

* `dg` now exits non-zero when a command fails: 1 for errors (including
crashes and usage errors), 2 for user interrupt, 0 on success. Every
command previously exited 0 regardless of outcome, so scripts and CI
steps that ignored the exit code will surface failures they were
silently swallowing. No command that succeeds changes its exit code.

### Bug Fixes

* correct web command examples, document Flux TTS/STT, and honor -o json
across account commands
([#97](#97))
([55984ec](55984ec))
* dependency floors that let dg update skip this release, and exit-code
+ error-stream correctness
([#102](#102))
([fd1e8a4](fd1e8a4))
* **deps:** raise deepctl-core floor to 0.2.16 in the eight packages
that import get_status_console
([98f9e91](98f9e91))
* **keys:** honor -o json so stdout stays parseable (completes the
[#97](#97) sweep)
([#101](#101))
([e430a77](e430a77))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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