feat(exit-codes): Unix-standard process exit codes for all error types#564
Merged
feat(exit-codes): Unix-standard process exit codes for all error types#564
Conversation
Introduce EXIT_CODES constant table (sysexits.h conventions) and wire exitCode into every CliError subclass so the process exit code reflects the semantic type of failure: 0 success (default) 1 generic / unexpected error 2 argument / usage error (ArgumentError) 66 empty result / not found (EmptyResultError, SelectorError) 69 service unavailable (BrowserConnectError, AdapterLoadError) 77 permission / auth required (AuthRequiredError) 78 configuration error (ConfigError) 124 timeout (TimeoutError) 130 Ctrl-C / SIGINT (unchanged, tui.ts) resolveExitCode() in commanderAdapter.ts reads err.exitCode for typed CliErrors, and falls back to pattern-matching message text for untyped adapter errors (auth pattern → 77, not-found pattern → 66, else → 1). Shell scripts can now distinguish error categories: opencli spotify status || echo "exit $?" # 69 if browser not running opencli github issues --repo x 2>/dev/null; [ $? -eq 77 ] && opencli github auth
- TIMEOUT: change from 124 → 75 (EX_TEMPFAIL); 124 is bash timeout(1)'s own exit code, creating ambiguity when shell runs `timeout 30 opencli` - SelectorError: change from EMPTY_RESULT(66) → GENERIC_ERROR(1); a missing DOM selector is an adapter bug, not a user "no data" condition - normalizeArgValue: throw ArgumentError instead of bare CliError so invalid bool args correctly exit with USAGE_ERROR(2) not GENERIC_ERROR(1) - resolveExitCode: explicitly map 'http' classification to GENERIC_ERROR to keep exit-code path in sync with the render path - tui.ts: replace hardcoded process.exit(130) with EXIT_CODES.INTERRUPTED
…constants
Extend the exit code system to cover every process exit point in the codebase.
No magic numbers remain — all exit codes are now referenced by name.
Semantic upgrades beyond pure renaming:
- plugin update missing args → USAGE_ERROR (2) instead of 1
- plugin update conflicting → USAGE_ERROR (2) instead of 1
- opencli install <unknown> → USAGE_ERROR (2) instead of 1
- unknown command fallback → USAGE_ERROR (2) instead of 1
- record with no candidates → EMPTY_RESULT (66) instead of 1
- external CLI install fail → SERVICE_UNAVAIL (69) instead of 1
- daemon EADDRINUSE → SERVICE_UNAVAIL (69) instead of 1
Files touched: cli.ts, external.ts, daemon.ts, main.ts,
clis/antigravity/serve.ts
This was referenced Mar 28, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
opencli now returns meaningful Unix process exit codes, making it a proper citizen of shell pipelines and CI scripts.
01CliError, unknown errors2ArgumentError66EmptyResultError,SelectorError, untyped not-found errors69BrowserConnectError,AdapterLoadError77AuthRequiredError, untyped auth errors78ConfigError124TimeoutError130Implementation
src/errors.ts: AddedEXIT_CODESconstant table (single source of truth, documented in JSDoc). AddedexitCode: ExitCodefield toCliErrorbase class — each subclass passes its code viasuper(), so there's no external mapping to maintain.src/commanderAdapter.ts: AddedresolveExitCode(err)that readserr.exitCodeforCliErrorinstances, and falls back to pattern-matching message text for untyped adapter errors (auth pattern → 77, not-found → 66, else → 1). The catch block now callsresolveExitCodeinstead of hardcoding1.Usage examples
Test plan
opencli spotify authwithout credentials → exit 78 (ConfigError)opencli github issues --bad-arg→ exit 2 (ArgumentError)