Skip to content

fix(mcp): swallow broken/closed-pipe on dg mcp startup notifications and error path - #88

Merged
GregHolmes merged 3 commits into
mainfrom
fix/dg-mcp-broken-pipe-DX-CLI-P
Aug 17, 2026
Merged

fix(mcp): swallow broken/closed-pipe on dg mcp startup notifications and error path#88
GregHolmes merged 3 commits into
mainfrom
fix/dg-mcp-broken-pipe-DX-CLI-P

Conversation

@dg-coreylweathers

Copy link
Copy Markdown
Contributor

Summary

Fixes DX-CLI-Pdg mcp crashes with ValueError: I/O operation on closed file on a broken stderr pipe.

When dg runs as an MCP server and the host closes stdio (a normal condition on client disconnect), the crash was a two-stage cascade:

  1. Primary — after CLI execution, print_pending_notification() does a raw sys.stderr.write(...). On a closed pipe this raises BrokenPipeError.
  2. Secondary — the except Exception handler in main() then called console.print(...), but the console's underlying stream is also closed, so rich raised ValueError: I/O operation on closed file — an unhandled exception that escaped to the excepthook, crashing the process and generating a Sentry event.

Changes

  • Guard all three startup notification writers (update, plugin, skills) against (BrokenPipeError, OSError, ValueError), matching the existing closed-stream pattern in base_command.py.
  • Make main()'s error path stream-safe via a new _safe_console_print() helper, so a closed console no longer crashes the process on exit. Both the KeyboardInterrupt and generic Exception handlers route through it.
  • Defense-in-depth: drop broken-pipe / closed-file events in the Sentry before_send hook (_is_broken_pipe) so residual pipe-close on dg mcp produces no noise.
  • Adds unit coverage for the notification guard and the Sentry filter.

Testing

  • test_telemetry.py + test_startup_check.py: 42 passed (incl. 6 new)
  • plugin-update + skills packages: 33 passed
  • ruff check / ruff format --check / mypy on changed files: clean
  • End-to-end: reproduced the closed-stream scenario against print_pending_notification() (survives without raising) and ran a real dg mcp process with a broken output pipe (exit 0, no traceback leaked).

Sentry: https://deepgram.sentry.io/issues/DX-CLI-P

🤖 Generated with Claude Code

…and error path

When `dg mcp` runs as an MCP server and the host closes stdio, the startup
update-notification write to stderr raised BrokenPipeError, which then
cascaded into an unhandled ValueError from rich while the error handler
tried to print to the already-closed console — crashing through the
excepthook and generating a Sentry event (DX-CLI-P).

- Guard all three startup notification writers (update, plugin, skills)
  against (BrokenPipeError, OSError, ValueError), matching the existing
  closed-stream pattern in base_command.py.
- Route main()'s error/interrupt handlers through a new
  _safe_console_print() helper so a closed console no longer crashes the
  process on exit.
- Defense-in-depth: drop broken-pipe / closed-file events in the Sentry
  before_send hook (_is_broken_pipe) so residual pipe-close on dg mcp
  produces no noise.

Adds unit coverage for the notification guard and the Sentry filter.

Sentry: https://deepgram.sentry.io/issues/DX-CLI-P

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dg-coreylweathers
dg-coreylweathers requested a review from a team July 16, 2026 19:27
Adds the missing regression tests for the three DX-CLI-P guards that shipped
without coverage:

- _safe_console_print (main.py): swallows BrokenPipeError / OSError / rich's
  closed-file ValueError, plus an end-to-end test that main() still exits(2)
  when the cli raises AND the console is closed (the exact crash cascade).
- skills print_pending_notification: tolerates a broken stderr.
- plugin print_pending_plugin_notifications: tolerates a broken stderr.

Each guard test fails if its try/except is removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dg-coreylweathers

Copy link
Copy Markdown
Contributor Author

Reviewer context — DX-CLI-P

What broke: when dg runs as an MCP server and the host closes stdio (a normal disconnect), the startup update-notification write to stderr raised BrokenPipeError. The except Exception handler in main() then tried console.print(...), but that stream was also closed, so rich raised ValueError: I/O operation on closed file — an unhandled exception that escaped to the excepthook, crashing the process and generating a Sentry event.

The fix (commit 6091c6a):

  • Guarded all three startup notification writers (update, plugin, skills) against (BrokenPipeError, OSError, ValueError), matching the existing closed-stream pattern in base_command.py.
  • Routed main()'s error/interrupt handlers through a new _safe_console_print() so a closed console no longer crashes the process on exit — this is what stops the secondary cascade.
  • Defense-in-depth: drop broken-pipe / closed-file events in the Sentry before_send hook (_is_broken_pipe) so residual pipe-close on dg mcp produces no noise. (Note: this suppresses broken-pipe events globally, not just for dg mcp — a deliberate choice, since a broken pipe is environmental rather than an actionable CLI bug.)

Coverage (commit 5b9ea16): added regression tests for all three guards, including an end-to-end test that reproduces the exact cascade — cli raises and the console is closed — and asserts main() still exits(2) instead of crashing.

Verification:

  • 137 tests pass across the main, skills, update, and telemetry packages.
  • Mutation-checked: removing the _safe_console_print guard makes all 4 of its tests fail; restored, they pass — so the coverage genuinely guards the fix.
  • ruff / mypy clean on all changed source files.
  • Also exercised a real dg mcp process against a broken output pipe: exit 0, no traceback leaked.

One earlier review nit (bare ValueError vs the narrower "closed file" match in base_command.py) was left as-is intentionally — stderr.write() only raises ValueError for a closed file, so the broader catch is safe today.

@GregHolmes
GregHolmes merged commit b24396e into main Aug 17, 2026
39 checks passed
@GregHolmes
GregHolmes deleted the fix/dg-mcp-broken-pipe-DX-CLI-P branch August 17, 2026 11:25
@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 GregHolmes mentioned this pull request Aug 18, 2026
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