You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Map Ctrl-C to exit code 130 (cancel) instead of 0 (#202)
## Summary
This change establishes a consistent exit code convention for user
interrupts (Ctrl-C / SIGTERM) across all interactive and streaming
commands. Previously, Ctrl-C was treated as a clean success (exit 0),
which made it impossible for callers to distinguish between a normal
completion and an interrupt. Now all Ctrl-C paths exit with code 130
(the conventional Unix "cancelled by SIGINT" code), while maintaining
clean shutdown behavior.
## Key Changes
- **Core interrupt handling**: Added `CANCELLED_EXIT_CODE = 130`
constant in `aai_cli/core/errors.py` as the single source of truth for
cancel exit codes.
- **Telemetry tracking**: Updated `aai_cli/core/telemetry.py` to:
- Map exit code 130 to "cancelled" outcome (distinct from generic
"error")
- Treat raw `KeyboardInterrupt` exceptions as "cancelled" rather than
"internal_error"
- Preserve the distinction so interrupts don't inflate crash metrics
- **Command execution**: Refactored `aai_cli/app/context.py` to:
- Extract telemetry/update-check wrapping into `_run_body()` helper
- Catch `KeyboardInterrupt` in `run_command()` and map it to
`typer.Exit(code=130)`
- Preserve the existing deferred-config path that skips telemetry
- **Interactive commands**: Updated all interactive/streaming commands
to raise `typer.Exit(code=130)` on Ctrl-C instead of returning cleanly:
- `aai_cli/commands/llm/_exec.py` (follow mode)
- `aai_cli/commands/dictate/_exec.py`
- `aai_cli/commands/agent/_exec.py`
- `aai_cli/commands/agent_cascade/_exec.py`
- `aai_cli/commands/share/_exec.py`
- `aai_cli/commands/webhooks/_listen.py`
- `aai_cli/streaming/session.py` (batch streaming)
- `aai_cli/init/runner.py` (dev server)
- **Tests**: Updated all test expectations to verify exit code 130 on
Ctrl-C, with clarified comments explaining the cancel semantics.
## Implementation Details
- The change preserves all existing cleanup behavior (renderer closes,
tunnels tear down, etc.) — only the exit code changes.
- Commands with no interactive handler (raw `KeyboardInterrupt` reaching
`run_command`) now exit 130 instead of Click's default exit 1.
- Telemetry correctly identifies 130 as "cancelled" so it doesn't count
toward crash rates.
- The `config path` command (which tolerates unreadable config)
continues to skip telemetry/update-check wrappers as before.
- Callers can now reliably use exit code 130 to detect interrupts in
shell scripts: `assembly stream && next_command` will not run
`next_command` if the user pressed Ctrl-C.
https://claude.ai/code/session_01BRiB7KpAgb833DkxHh5ntc
Co-authored-by: Claude <noreply@anthropic.com>
0 commit comments